From e270c18756ca32135f47c5fd6696565b4da963fa Mon Sep 17 00:00:00 2001 From: Jeremy Dubreil Date: Tue, 19 May 2026 14:19:25 +0000 Subject: [PATCH] feat(COD-7026): cache the analysis results of the target branch --- package-lock.json | 27 +++++++++++++++------------ package.json | 2 +- src/index.ts | 47 +++++++++++++++++++++++++++++++++++++++++++++-- src/util.ts | 8 +++----- 4 files changed, 64 insertions(+), 20 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3c07033d..5da12e05 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "license": "Apache-2.0", "dependencies": { "@actions/artifact": "^2.3.2", - "@actions/cache": "^3.2.1", + "@actions/cache": "^4.1.0", "@actions/core": "^1.10.0", "@actions/github": "^5.1.1", "@octokit/core": "^3.6.0", @@ -63,10 +63,10 @@ } }, "node_modules/@actions/cache": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@actions/cache/-/cache-3.3.0.tgz", - "integrity": "sha512-+eCsMTIZUEm+QA9GqjollOhCdvRrZ1JV8d9Rp34zVNizBkYITO8dhKczP5Xps1dFzc5n59p7vYVtZrGt18bb5Q==", - "deprecated": "Please upgrade to v4 or higher. See https://github.com/actions/toolkit/discussions/1890 for more details.", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@actions/cache/-/cache-4.1.0.tgz", + "integrity": "sha512-z3Opg+P4Y7baq+g1dODXgdtsvPLSewr3ZKpp3U0HQR1A/vWCoJFS52XSezjdngo4SIOdR5oHtyK3a3Arar+X9A==", + "license": "MIT", "dependencies": { "@actions/core": "^1.11.1", "@actions/exec": "^1.0.1", @@ -76,6 +76,7 @@ "@azure/abort-controller": "^1.1.0", "@azure/ms-rest-js": "^2.6.0", "@azure/storage-blob": "^12.13.0", + "@protobuf-ts/runtime-rpc": "^2.11.1", "semver": "^6.3.1" } }, @@ -1901,16 +1902,18 @@ } }, "node_modules/@protobuf-ts/runtime": { - "version": "2.9.6", - "resolved": "https://registry.npmjs.org/@protobuf-ts/runtime/-/runtime-2.9.6.tgz", - "integrity": "sha512-C0CfpKx4n4LBbUrajOdRj2BTbd3qBoK0SiKWLq7RgCoU6xiN4wesBMFHUOBp3fFzKeZwgU8Q2KtzaqzIvPLRXg==" + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/@protobuf-ts/runtime/-/runtime-2.11.1.tgz", + "integrity": "sha512-KuDaT1IfHkugM2pyz+FwiY80ejWrkH1pAtOBOZFuR6SXEFTsnb/jiQWQ1rCIrcKx2BtyxnxW6BWwsVSA/Ie+WQ==", + "license": "(Apache-2.0 AND BSD-3-Clause)" }, "node_modules/@protobuf-ts/runtime-rpc": { - "version": "2.9.6", - "resolved": "https://registry.npmjs.org/@protobuf-ts/runtime-rpc/-/runtime-rpc-2.9.6.tgz", - "integrity": "sha512-0UeqDRzNxgsh08lY5eWzFJNfD3gZ8Xf+WG1HzbIAbVAigzigwjwsYNNhTeas5H3gco1U5owTzCg177aambKOOw==", + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/@protobuf-ts/runtime-rpc/-/runtime-rpc-2.11.1.tgz", + "integrity": "sha512-4CqqUmNA+/uMz00+d3CYKgElXO9VrEbucjnBFEjqI4GuDrEQ32MaI3q+9qPBvIGOlL4PmHXrzM32vBPWRhQKWQ==", + "license": "Apache-2.0", "dependencies": { - "@protobuf-ts/runtime": "^2.9.6" + "@protobuf-ts/runtime": "^2.11.1" } }, "node_modules/@sinclair/typebox": { diff --git a/package.json b/package.json index 82678a81..71b4ddda 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "main": "index.js", "dependencies": { "@actions/artifact": "^2.3.2", - "@actions/cache": "^3.2.1", + "@actions/cache": "^4.1.0", "@actions/core": "^1.10.0", "@actions/github": "^5.1.1", "@octokit/core": "^3.6.0", diff --git a/src/index.ts b/src/index.ts index b3a18180..fb43db75 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,4 @@ +import * as cache from '@actions/cache' import { error, getInput, info, setOutput } from '@actions/core' import { copyFileSync, existsSync, mkdirSync } from 'fs' import * as path from 'path' @@ -8,6 +9,7 @@ import { uploadArtifact, } from './actions' import { callCommand, runCodesec, getOptionalEnvVariable, readMarkdownFile } from './util' +import { simpleGit } from 'simple-git' // Global scanner toggles - set to false to disable a scanner globally const enableScaRunning = true @@ -35,7 +37,42 @@ async function runAnalysis() { if (target == 'push') { targetScan = 'scan' } - const resultsPath = await runCodesec('scan', enableIacRunning, enableScaRunning, targetScan) + + // Create scan-results directory + const resultsPath = path.join(process.cwd(), 'scan-results') + + // Cache the analysis results when scanning the target branch + let cacheHit = false + const commit = (await simpleGit().revparse(['HEAD'])).trim() + let cacheKey = `codesec-${commit}` + if (targetScan === 'old') { + const restored = await cache.restoreCache([resultsPath], cacheKey) + if (restored) { + info(`Cache hit for ${cacheKey} — skipping scan`) + cacheHit = true + } else { + info(`Cache miss for ${cacheKey} — running scan`) + } + } + + if (!cacheHit) { + let success = await runCodesec( + 'scan', + enableIacRunning, + enableScaRunning, + resultsPath, + targetScan + ) + if (success && targetScan !== 'new') { + // Save the analysis results when not scanning the PR source branch + try { + await cache.saveCache([resultsPath], cacheKey) + info(`Saved analysis results for ${cacheKey}`) + } catch (e) { + info(`Failed to save cache for ${cacheKey}: ${(e as Error).message}`) + } + } + } // Upload SCA SARIF from the returned results path if (enableScaRunning) { @@ -103,7 +140,13 @@ async function displayResults() { } // Run codesec compare mode with available scanners - await runCodesec('compare', enableIacRunning && iacAvailable, enableScaRunning && scaAvailable) + const resultsPath = path.join(process.cwd(), 'scan-results') + await runCodesec( + 'compare', + enableIacRunning && iacAvailable, + enableScaRunning && scaAvailable, + resultsPath + ) // Read comparison output - check all possible outputs const outputs = [ diff --git a/src/util.ts b/src/util.ts index 2153fbe1..5e57a3d4 100644 --- a/src/util.ts +++ b/src/util.ts @@ -129,15 +129,13 @@ export async function runCodesec( action: string, runIac: boolean = false, runSca: boolean = false, + reportsDir: string, scanTarget?: string -): Promise { +): Promise { const lwAccount = getRequiredEnvVariable('LW_ACCOUNT') const lwApiKey = getRequiredEnvVariable('LW_API_KEY') const lwApiSecret = getRequiredEnvVariable('LW_API_SECRET') - // Create scan-results directory - const reportsDir = path.join(process.cwd(), 'scan-results') - if (action === 'scan') { const containerName = `codesec-scan-${scanTarget || 'default'}` @@ -267,7 +265,7 @@ export async function runCodesec( // Cleanup container await callCommand('docker', 'rm', containerName) } - return reportsDir + return true } export function readMarkdownFile(filePath: string): string {