From 369c27b68fe303bac7d2bbe3946bd7f79690bf87 Mon Sep 17 00:00:00 2001
From: Geoffrey Arthaud <geoffrey.arthaud@developpement-durable.gouv.fr>
Date: Mon, 18 Mar 2024 20:23:05 +0100
Subject: [PATCH] Fix compatibility with job token's permissions

---
 i18n/en-GB/gitlab_project_doctor.ftl |  3 +-
 i18n/fr-FR/gitlab_project_doctor.ftl |  3 +-
 src/diagnosis/container_analysis.rs  |  4 +--
 src/diagnosis/gitlab_connection.rs   | 19 ++++--------
 src/diagnosis/job_analysis.rs        | 44 ++++++++++++++++------------
 src/diagnosis/package_analysis.rs    |  2 +-
 src/diagnosis/pipeline_analysis.rs   |  2 +-
 src/diagnosis/pipeline_clean.rs      | 43 ++++++++++++++-------------
 src/main.rs                          |  8 ++---
 9 files changed, 64 insertions(+), 64 deletions(-)

diff --git a/i18n/en-GB/gitlab_project_doctor.ftl b/i18n/en-GB/gitlab_project_doctor.ftl
index 5ad36a7..d9c5825 100644
--- a/i18n/en-GB/gitlab_project_doctor.ftl
+++ b/i18n/en-GB/gitlab_project_doctor.ftl
@@ -25,7 +25,8 @@ help-days = Number of days from which an element is considered "old", 30 by defa
 help-git-path = Analyze the project from a local path of a Git repository. Ignored if url option is specified
 help-token = (Not recommended) Gitlab private token for authentication, maintainer role is needed for objects deletion. It is strongly advised to use the environment variable GL_TOKEN instead of this argument, for security reasons.
 help-url = Analyze the project from the URL of Gitlab repository
-no-cicd = No CI/CD configured for this project
+no-cicd = No CI/CD configured for this project.
+no-permission-jobanalysis = The token has no permission for job analysis.
 package-analysing = Analysis of packages
 package-clean-report = Deleted {$nb_packages} packages, {$size} saved.
 package-deleting = Deleting obsolete packages files
diff --git a/i18n/fr-FR/gitlab_project_doctor.ftl b/i18n/fr-FR/gitlab_project_doctor.ftl
index de2a036..43999a6 100644
--- a/i18n/fr-FR/gitlab_project_doctor.ftl
+++ b/i18n/fr-FR/gitlab_project_doctor.ftl
@@ -25,7 +25,8 @@ help-days = Nombre de jours d'ancienneté à partir duquel un élément est cons
 help-git-path = Analyse du projet à partir d'un chemin vers un dépôt Git. Ignoré si l'option url est spécifiée
 help-token = (Non recommandé) Token privé Gitlab d'authentification, avec le rôle maintainer nécessaire pour la suppression. Il est conseillé d'utiliser la variable GL_TOKEN à la place de cet argument, pour des raisons de sécurité.
 help-url = Analyse du projet à partir d'une URL Gitlab
-no-cicd = Aucun CI/CD configuré pour ce projet
+no-cicd = Aucun CI/CD configuré pour ce projet.
+no-permission-jobanalysis = Le token n'a pas la permission d'analyser les jobs.
 package-analysing = Analyse du package registry
 package-clean-report = {$nb_packages} packages supprimés, {$size} récupérés.
 package-deleting = Suppression des fichiers de package obsolètes
diff --git a/src/diagnosis/container_analysis.rs b/src/diagnosis/container_analysis.rs
index 287ed9c..a546619 100644
--- a/src/diagnosis/container_analysis.rs
+++ b/src/diagnosis/container_analysis.rs
@@ -4,9 +4,9 @@ use gitlab::Gitlab;
 use human_bytes::human_bytes;
 use serde::Deserialize;
 
-use crate::{api, fl, Reportable, ReportJob, ReportPending, ReportStatus};
-use crate::diagnosis::{CONTAINER_REGISTRY_LIMIT, warning_if};
 use crate::diagnosis::gitlab_connection::{GitlabRepository, Project};
+use crate::diagnosis::{warning_if, CONTAINER_REGISTRY_LIMIT};
+use crate::{api, fl, ReportJob, ReportPending, ReportStatus, Reportable};
 
 #[derive(Debug, Deserialize)]
 pub struct GitlabRawContainerRepository {
diff --git a/src/diagnosis/gitlab_connection.rs b/src/diagnosis/gitlab_connection.rs
index 0c1dfa8..3a436b3 100644
--- a/src/diagnosis/gitlab_connection.rs
+++ b/src/diagnosis/gitlab_connection.rs
@@ -9,8 +9,8 @@ use regex::Regex;
 use serde::{Deserialize, Serialize};
 
 use crate::diagnosis::{
-    ARTIFACT_JOBS_LIMIT, PACKAGE_REGISTRY_LIMIT, REPO_LIMIT, Reportable, ReportJob, ReportPending,
-    ReportStatus, STORAGE_LIMIT, warning_if,
+    warning_if, ReportJob, ReportPending, ReportStatus, Reportable, ARTIFACT_JOBS_LIMIT,
+    PACKAGE_REGISTRY_LIMIT, REPO_LIMIT, STORAGE_LIMIT,
 };
 use crate::fl;
 
@@ -149,10 +149,7 @@ impl ConnectionJob {
         Ok((client, project))
     }
 
-    fn _gitlab_project_job_token(
-        server: &str,
-        token: &str,
-    ) -> Result<(Gitlab, Project)> {
+    fn _gitlab_project_job_token(server: &str, token: &str) -> Result<(Gitlab, Project)> {
         let client = Gitlab::new_job_token(server, token)?;
         let project = Project {
             id: env::var("CI_PROJECT_ID")?.parse()?,
@@ -243,10 +240,7 @@ fn _report_artifact_storage(stats: &Statistics) -> ReportStatus {
         human_bytes(stats.job_artifacts_size as f64),
         100 * stats.job_artifacts_size / stats.storage_size
     );
-    warning_if(
-        stats.job_artifacts_size > ARTIFACT_JOBS_LIMIT,
-        msg,
-    )
+    warning_if(stats.job_artifacts_size > ARTIFACT_JOBS_LIMIT, msg)
 }
 
 fn _report_package_storage(stats: &Statistics) -> ReportStatus {
@@ -256,10 +250,7 @@ fn _report_package_storage(stats: &Statistics) -> ReportStatus {
         human_bytes(stats.packages_size as f64),
         100 * stats.packages_size / stats.storage_size
     );
-    warning_if(
-        stats.packages_size > PACKAGE_REGISTRY_LIMIT,
-        msg,
-    )
+    warning_if(stats.packages_size > PACKAGE_REGISTRY_LIMIT, msg)
 }
 
 fn gitlab_url(repo: &Repository) -> Option<(String, String)> {
diff --git a/src/diagnosis/job_analysis.rs b/src/diagnosis/job_analysis.rs
index 2ffbcfa..eed9e04 100644
--- a/src/diagnosis/job_analysis.rs
+++ b/src/diagnosis/job_analysis.rs
@@ -1,13 +1,13 @@
 use chrono::{DateTime, Local, TimeDelta};
-use gitlab::api::{Pagination, projects, Query};
 use gitlab::api::paged;
+use gitlab::api::{projects, Pagination, Query};
 use gitlab::Gitlab;
 use human_bytes::human_bytes;
 use serde::Deserialize;
 
-use crate::{Reportable, ReportJob, ReportPending};
 use crate::diagnosis::gitlab_connection::{GitlabRepository, Project};
 use crate::diagnosis::ReportStatus;
+use crate::{fl, ReportJob, ReportPending, Reportable};
 
 #[derive(Debug, Deserialize)]
 pub struct Artifact {
@@ -38,6 +38,16 @@ impl Reportable for JobAnalysisReport {
     }
 }
 
+impl JobAnalysisReport {
+    pub fn simple(status: ReportStatus) -> JobAnalysisReport {
+        JobAnalysisReport {
+            gitlab_jobs: vec![],
+            report_status: vec![status],
+            savable_bytes: 0,
+        }
+    }
+}
+
 impl ReportJob for JobAnalysisJob {
     type Diagnosis = JobAnalysisReport;
 
@@ -47,27 +57,25 @@ impl ReportJob for JobAnalysisJob {
             job: {
                 std::thread::spawn(move || {
                     if !self.project.jobs_enabled {
-                        return JobAnalysisReport {
-                            report_status: vec![ReportStatus::NA(
-                                "No CI/CD configured on this project".to_string(),
-                            )],
-                            gitlab_jobs: vec![],
-                            savable_bytes: 0,
-                        };
+                        return JobAnalysisReport::simple(ReportStatus::NA(fl!("no-cicd")));
                     }
                     let endpoint = projects::jobs::Jobs::builder()
                         .project(self.project.id)
                         .build()
                         .unwrap();
-                    let jobs: Vec<GitlabJob> = paged(endpoint, Pagination::All)
-                        .query(&self.gitlab)
-                        .unwrap();
-                    let (report, bytes_savable) = self._number_jobs(&jobs);
-                    JobAnalysisReport {
-                        report_status: vec![report],
-                        gitlab_jobs: jobs,
-                        savable_bytes: bytes_savable,
-                    }
+                    paged(endpoint, Pagination::All).query(&self.gitlab).map_or(
+                        JobAnalysisReport::simple(ReportStatus::NA(fl!(
+                            "no-permission-jobanalysis"
+                        ))),
+                        |jobs| {
+                            let (report, bytes_savable) = self._number_jobs(&jobs);
+                            JobAnalysisReport {
+                                report_status: vec![report],
+                                gitlab_jobs: jobs,
+                                savable_bytes: bytes_savable,
+                            }
+                        },
+                    )
                 })
             },
             progress: None,
diff --git a/src/diagnosis/package_analysis.rs b/src/diagnosis/package_analysis.rs
index 035e2d9..f1152f1 100644
--- a/src/diagnosis/package_analysis.rs
+++ b/src/diagnosis/package_analysis.rs
@@ -6,9 +6,9 @@ use lazy_static::lazy_static;
 use regex::Regex;
 use serde::Deserialize;
 
-use crate::{fl, Reportable, ReportJob, ReportPending, ReportStatus};
 use crate::api::packages::{PackageFiles, Packages};
 use crate::diagnosis::gitlab_connection::{GitlabRepository, Project};
+use crate::{fl, ReportJob, ReportPending, ReportStatus, Reportable};
 
 #[derive(Debug, Deserialize)]
 pub struct GitlabPackage {
diff --git a/src/diagnosis/pipeline_analysis.rs b/src/diagnosis/pipeline_analysis.rs
index 61d23b0..062dba4 100644
--- a/src/diagnosis/pipeline_analysis.rs
+++ b/src/diagnosis/pipeline_analysis.rs
@@ -3,8 +3,8 @@ use gitlab::api::{Pagination, Query};
 use gitlab::Gitlab;
 use serde::Deserialize;
 
-use crate::{fl, Reportable, ReportJob, ReportPending, ReportStatus};
 use crate::diagnosis::gitlab_connection::{GitlabRepository, Project};
+use crate::{fl, ReportJob, ReportPending, ReportStatus, Reportable};
 
 #[derive(Debug, Deserialize)]
 pub struct GitlabPipeline {
diff --git a/src/diagnosis/pipeline_clean.rs b/src/diagnosis/pipeline_clean.rs
index 24b3873..46236c4 100644
--- a/src/diagnosis/pipeline_clean.rs
+++ b/src/diagnosis/pipeline_clean.rs
@@ -6,10 +6,10 @@ use gitlab::api::{ApiError, Query};
 use gitlab::Gitlab;
 use human_bytes::human_bytes;
 
-use crate::{fl, Reportable, ReportPending, ReportStatus};
-use crate::diagnosis::{GITLAB_SCOPE_ERROR, RemedyJob};
 use crate::diagnosis::gitlab_connection::{Project, Statistics};
 use crate::diagnosis::pipeline_analysis::{GitlabPipeline, PipelineAnalysisReport};
+use crate::diagnosis::{RemedyJob, GITLAB_SCOPE_ERROR};
+use crate::{fl, ReportPending, ReportStatus, Reportable};
 
 pub struct PipelineCleanJob {
     pub pipeline_report: PipelineAnalysisReport,
@@ -118,25 +118,26 @@ impl RemedyJob for PipelineCleanJob {
 
                 let mut report_status = vec![];
 
-                let saved_bytes = if let Some(stats) = self.pipeline_report.project.statistics.as_ref() {
-                    let saved_bytes_value = PipelineCleanJob::_compute_saved_bytes(
-                        &self.pipeline_report.gitlab,
-                        &self.pipeline_report.project,
-                        stats,
-                    );
-                    report_status.push(ReportStatus::OK(fl!(
-                    "pipeline-clean-report-size",
-                    nb_pipelines = deleted_pipelines.len(),
-                    size = human_bytes(saved_bytes_value as f64)
-                )));
-                    Some(saved_bytes_value)
-                } else {
-                    report_status.push(ReportStatus::OK(fl!(
-                    "pipeline-clean-report",
-                    nb_pipelines = deleted_pipelines.len()
-                )));
-                    None
-                };
+                let saved_bytes =
+                    if let Some(stats) = self.pipeline_report.project.statistics.as_ref() {
+                        let saved_bytes_value = PipelineCleanJob::_compute_saved_bytes(
+                            &self.pipeline_report.gitlab,
+                            &self.pipeline_report.project,
+                            stats,
+                        );
+                        report_status.push(ReportStatus::OK(fl!(
+                            "pipeline-clean-report-size",
+                            nb_pipelines = deleted_pipelines.len(),
+                            size = human_bytes(saved_bytes_value as f64)
+                        )));
+                        Some(saved_bytes_value)
+                    } else {
+                        report_status.push(ReportStatus::OK(fl!(
+                            "pipeline-clean-report",
+                            nb_pipelines = deleted_pipelines.len()
+                        )));
+                        None
+                    };
 
                 if last_is_old {
                     report_status.push(ReportStatus::NA(fl!("pipeline-last-notdeleted")));
diff --git a/src/main.rs b/src/main.rs
index 24ba522..f9f6456 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,6 +1,6 @@
 use i18n_embed::{
-    DesktopLanguageRequester,
     fluent::{fluent_language_loader, FluentLanguageLoader},
+    DesktopLanguageRequester,
 };
 use lazy_static::lazy_static;
 use rust_embed::RustEmbed;
@@ -9,7 +9,6 @@ use structopt::StructOpt;
 
 use cli::Args;
 
-use crate::diagnosis::{RemedyJob, Reportable, ReportJob, ReportPending};
 use crate::diagnosis::conf_analysis::{ConfAnalysisJob, ConfAnalysisReport};
 use crate::diagnosis::container_analysis::{ContainerAnalysisJob, ContainerAnalysisReport};
 use crate::diagnosis::gitlab_connection::{ConnectionJob, GitlabRepository, Statistics};
@@ -19,6 +18,7 @@ use crate::diagnosis::package_clean::PackageCleanJob;
 use crate::diagnosis::pipeline_analysis::{PipelineAnalysisJob, PipelineAnalysisReport};
 use crate::diagnosis::pipeline_clean::PipelineCleanJob;
 use crate::diagnosis::ReportStatus;
+use crate::diagnosis::{RemedyJob, ReportJob, ReportPending, Reportable};
 
 pub mod api;
 pub mod cli;
@@ -112,9 +112,7 @@ impl AnalysisReport {
             0
         };
         (savable_repo * 9 + self.savable_bytes_jobs + self.savable_bytes_packages) * 100
-            / (stats.repository_size * 9
-            + stats.job_artifacts_size
-            + stats.packages_size)
+            / (stats.repository_size * 9 + stats.job_artifacts_size + stats.packages_size)
     }
 }
 
-- 
GitLab