From 6d2771dbb98fee6f9110e2bfbc3363c58ab9a883 Mon Sep 17 00:00:00 2001 From: Dennis Bijlsma Date: Wed, 9 Jul 2025 14:03:46 +0200 Subject: [PATCH 1/2] Revert "Revert "Block disappearing scope file"" --- .../reference/analysis-scope-configuration.md | 4 ++ sigridci/sigridci/sigridci_runner.py | 29 ++++++---- test/test_sigridci_runner.py | 58 ++++++++++++++++--- 3 files changed, 72 insertions(+), 19 deletions(-) diff --git a/docs/reference/analysis-scope-configuration.md b/docs/reference/analysis-scope-configuration.md index 747567d3..b7e109eb 100644 --- a/docs/reference/analysis-scope-configuration.md +++ b/docs/reference/analysis-scope-configuration.md @@ -427,6 +427,10 @@ We recommend you add `sigrid.yaml` to your repository, so that it is automatical - If you want to *retrieve* the scope configuration file used by Sigrid, you can use the [Sigrid API](../integrations/sigrid-api-documentation.md). You can also use the [example code on GitHub](https://github.com/Software-Improvement-Group/sigrid-integrations/tree/main/get-scope-file) for this. - If you want to *update* the scope configuration file, independently of your source code, you can still use [Sigrid CI](client-script-usage.md). You can also use the [example code on GitHub](https://github.com/Software-Improvement-Group/sigrid-integrations/tree/main/get-scope-file) for this. + +## Removing the scope configuration file + +Once you have decided to create a scope configuration file to customize your Sigrid configuration, there is no way to go back. If you try to delete the `sigrid.yaml` file from your repository, you will receive an error message when trying to run Sigrid CI. ## Sigrid metadata diff --git a/sigridci/sigridci/sigridci_runner.py b/sigridci/sigridci/sigridci_runner.py index 8ad5774c..4a01e567 100644 --- a/sigridci/sigridci/sigridci_runner.py +++ b/sigridci/sigridci/sigridci_runner.py @@ -46,6 +46,9 @@ class SigridCiRunner: "remark" ] + DOCS_URL = "https://docs.sigrid-says.com" + MISSING_SCOPE_URL = f"{DOCS_URL}/reference/analysis-scope-configuration.html#removing-the-scope-configuration-file" + def __init__(self, options: PublishOptions, apiClient: SigridApiClient): self.options = options self.apiClient = apiClient @@ -67,21 +70,22 @@ def run(self): systemExists = self.apiClient.checkSystemExists() UploadLog.log("Found system in Sigrid" if systemExists else "System is not yet on-boarded to Sigrid") - if systemExists and not self.apiClient.fetchMetadata().get("active", True): - UploadLog.log("Publish blocked: System has been deactivated by your Sigrid administrator, in the Sigrid system settings page") + metadata = self.apiClient.fetchMetadata() if systemExists else {} + if systemExists and not metadata.get("active", True): + UploadLog.log("Publish blocked: System has been deactivated by your Sigrid administrator in the Sigrid system settings page") sys.exit(1) self.prepareMetadata() - self.validateConfigurationFiles() + self.validateConfigurationFiles(metadata) analysisId = self.apiClient.submitUpload(systemExists) if not systemExists: UploadLog.log(f"System '{self.options.system}' has been on-boarded and will appear in Sigrid shortly") elif self.options.runMode == RunMode.PUBLISH_ONLY: UploadLog.log("Your project's source code has been published to Sigrid") - self.displayMetadata() + self.displayMetadata(metadata) else: - self.displayFeedback(analysisId) + self.displayFeedback(analysisId, metadata) def prepareRun(self): # We don't use the options.feedbackURL directly, since that's intended @@ -90,12 +94,12 @@ def prepareRun(self): if self.options.feedbackURL: self.apiClient.logPlatformInformation(Platform.getPlatformId()) - def displayFeedback(self, analysisId): + def displayFeedback(self, analysisId, metadata): if self.options.targetRating == "sigrid": self.options.targetRating = self.loadSigridTarget() feedback = self.apiClient.fetchAnalysisResults(analysisId) - self.displayMetadata() + self.displayMetadata(metadata) if not os.path.exists(self.options.outputDir): os.mkdir(self.options.outputDir) @@ -103,14 +107,19 @@ def displayFeedback(self, analysisId): for report in self.reports: report.generate(analysisId, feedback, self.options) - def validateConfigurationFiles(self): + def validateConfigurationFiles(self, metadata): scope = self.options.readScopeFile() + if scope is not None: if self.options.subsystem not in (None, "", "root", "scopefile"): UploadLog.log("Warning: You cannot provide a scope configuration file for a subsystem, it will be ignored.") self.validateConfiguration(lambda: self.apiClient.validateScopeFile(scope), "scope configuration file") + if scope is None and metadata.get("scopeFileInRepository"): + message = {"valid" : False, "notes" : ["Missing sigrid.yaml file", f"See {self.MISSING_SCOPE_URL}"]} + self.validateConfiguration(lambda: message, "scope configuration file") + metadataFile = self.options.readMetadataFile() if metadataFile is not None: self.validateConfiguration(lambda: self.apiClient.validateMetadata(metadataFile), "Sigrid metadata file") @@ -129,11 +138,11 @@ def validateConfiguration(self, validationCall, configurationName): UploadLog.log("-" * 80) sys.exit(1) - def displayMetadata(self): + def displayMetadata(self, metadata): if self.options.readMetadataFile() == None: print("") print("Sigrid metadata for this system:") - for key, value in self.apiClient.fetchMetadata().items(): + for key, value in metadata.items(): if value: print(f" {key}:".ljust(20) + str(value)) diff --git a/test/test_sigridci_runner.py b/test/test_sigridci_runner.py index 07200566..c5adb783 100644 --- a/test/test_sigridci_runner.py +++ b/test/test_sigridci_runner.py @@ -74,8 +74,7 @@ def testRegularRun(self): "/analysis-results/api/v1/system-metadata/aap/noot", "/inboundresults/sig/aap/noot/ci/uploads/v1", "UPLOAD", - "/analysis-results/sigridci/aap/noot/v1/ci/results/123", - "/analysis-results/api/v1/system-metadata/aap/noot" + "/analysis-results/sigridci/aap/noot/v1/ci/results/123" ] self.assertEqual(UploadLog.history, expectedLog) @@ -109,8 +108,7 @@ def testPublishRun(self): "/analysis-results/api/v1/system-metadata/aap/noot", "/inboundresults/sig/aap/noot/ci/uploads/v1/publish", "UPLOAD", - "/analysis-results/sigridci/aap/noot/v1/ci/results/123", - "/analysis-results/api/v1/system-metadata/aap/noot" + "/analysis-results/sigridci/aap/noot/v1/ci/results/123" ] self.assertEqual(UploadLog.history, expectedLog) @@ -143,8 +141,7 @@ def testPublishOnlyRun(self): "/analysis-results/sigridci/aap/noot/v1/ci", "/analysis-results/api/v1/system-metadata/aap/noot", "/inboundresults/sig/aap/noot/ci/uploads/v1/publishonly", - "UPLOAD", - "/analysis-results/api/v1/system-metadata/aap/noot" + "UPLOAD" ] self.assertEqual(UploadLog.history, expectedLog) @@ -196,8 +193,7 @@ def testAddSubsystemOptionToUrl(self): "/analysis-results/api/v1/system-metadata/aap/noot", "/inboundresults/sig/aap/noot/ci/uploads/v1/publish?subsystem=mysubsystem", "UPLOAD", - "/analysis-results/sigridci/aap/noot/v1/ci/results/123", - "/analysis-results/api/v1/system-metadata/aap/noot" + "/analysis-results/sigridci/aap/noot/v1/ci/results/123" ] self.assertEqual(apiClient.called, expectedCalls) @@ -623,7 +619,7 @@ def testSkipForDeactivatedSystems(self): expectedLog = [ "Using token ending in '****ummy'", "Found system in Sigrid", - "Publish blocked: System has been deactivated by your Sigrid administrator, in the Sigrid system settings page" + "Publish blocked: System has been deactivated by your Sigrid administrator in the Sigrid system settings page" ] expectedCalls = [ @@ -634,6 +630,50 @@ def testSkipForDeactivatedSystems(self): self.assertEqual(UploadLog.history, expectedLog) self.assertEqual(apiClient.called, expectedCalls) + def testMissingScopeFileIsNotErrorIfNoPreviousScopeFileExists(self): + apiClient = MockApiClient(self.options) + apiClient.responses["/inboundresults/sig/aap/noot/ci/validate/v1"] = {"valid" : True, "notes" : []} + apiClient.responses["/analysis-results/api/v1/system-metadata/aap/noot"] = {"scopeFileInRepository" : False} + + runner = SigridCiRunner(self.options, apiClient) + runner.reports = [] + + with self.assertRaises(SystemExit): + runner.run() + + expectedLog = [ + "Using token ending in '****ummy'", + "Found system in Sigrid", + "Creating upload", + "Upload size is 1 MB" + ] + + self.assertEqual(UploadLog.history, expectedLog) + + def testMissingScopeFileIsErrorIfPreviousScopeFileExists(self): + apiClient = MockApiClient(self.options) + apiClient.responses["/inboundresults/sig/aap/noot/ci/validate/v1"] = {"valid" : True, "notes" : []} + apiClient.responses["/analysis-results/api/v1/system-metadata/aap/noot"] = {"scopeFileInRepository" : True} + + runner = SigridCiRunner(self.options, apiClient) + runner.reports = [] + + with self.assertRaises(SystemExit): + runner.run() + + expectedLog = [ + "Using token ending in '****ummy'", + "Found system in Sigrid", + "Validating scope configuration file", + "--------------------------------------------------------------------------------", + "Invalid scope configuration file:", + " - Missing sigrid.yaml file", + " - See https://docs.sigrid-says.com/reference/analysis-scope-configuration.html#removing-the-scope-configuration-file", + "--------------------------------------------------------------------------------" + ] + + self.assertEqual(UploadLog.history, expectedLog) + def createTempFile(self, dir, name, contents): with open(f"{dir}/{name}", "w") as fileRef: fileRef.write(contents) From 7e9a7c5157763ef792b671e888c004cc3f9192e0 Mon Sep 17 00:00:00 2001 From: Dennis Bijlsma Date: Wed, 9 Jul 2025 14:38:43 +0200 Subject: [PATCH 2/2] Handle subsystems. --- sigridci/sigridci/sigridci_runner.py | 2 +- test/test_sigridci_runner.py | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/sigridci/sigridci/sigridci_runner.py b/sigridci/sigridci/sigridci_runner.py index 4a01e567..decc75e1 100644 --- a/sigridci/sigridci/sigridci_runner.py +++ b/sigridci/sigridci/sigridci_runner.py @@ -116,7 +116,7 @@ def validateConfigurationFiles(self, metadata): self.validateConfiguration(lambda: self.apiClient.validateScopeFile(scope), "scope configuration file") - if scope is None and metadata.get("scopeFileInRepository"): + if scope is None and metadata.get("scopeFileInRepository") and not self.options.subsystem: message = {"valid" : False, "notes" : ["Missing sigrid.yaml file", f"See {self.MISSING_SCOPE_URL}"]} self.validateConfiguration(lambda: message, "scope configuration file") diff --git a/test/test_sigridci_runner.py b/test/test_sigridci_runner.py index c5adb783..53f71031 100644 --- a/test/test_sigridci_runner.py +++ b/test/test_sigridci_runner.py @@ -674,6 +674,27 @@ def testMissingScopeFileIsErrorIfPreviousScopeFileExists(self): self.assertEqual(UploadLog.history, expectedLog) + def testMissingScopeFileIsFineForSubsystems(self): + apiClient = MockApiClient(self.options) + apiClient.responses["/inboundresults/sig/aap/noot/ci/validate/v1"] = {"valid" : True, "notes" : []} + apiClient.responses["/analysis-results/api/v1/system-metadata/aap/noot"] = {"scopeFileInRepository" : True} + + self.options.subsystem = "aap" + runner = SigridCiRunner(self.options, apiClient) + runner.reports = [] + + with self.assertRaises(SystemExit): + runner.run() + + expectedLog = [ + "Using token ending in '****ummy'", + "Found system in Sigrid", + "Creating upload", + "Upload size is 1 MB" + ] + + self.assertEqual(UploadLog.history, expectedLog) + def createTempFile(self, dir, name, contents): with open(f"{dir}/{name}", "w") as fileRef: fileRef.write(contents)