Merge branch 'main' into dependabot/github_actions/dot-github/workflows/actions/download-artifact-8

This commit is contained in:
Michael B. Gale
2026-03-05 15:32:00 +00:00
committed by GitHub
26 changed files with 1045 additions and 995 deletions
+1 -6
View File
@@ -42,11 +42,6 @@ jobs:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: 3.11
- name: Install dependencies
run: |
# Use the system Bash shell to ensure we can run commands like `npm ci`
@@ -68,7 +63,7 @@ jobs:
- name: Run pr-checks tests
if: always()
working-directory: pr-checks
run: python -m unittest discover
run: npm ci && npx tsx --test
- name: Lint
if: always() && matrix.os != 'windows-latest'
+2 -6
View File
@@ -73,17 +73,13 @@ jobs:
npm run lint -- --fix
npm run build
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: 3.11
- name: Sync back version updates to generated workflows
# Only sync back versions on Dependabot update PRs
if: startsWith(env.HEAD_REF, 'dependabot/')
working-directory: pr-checks
run: |
python3 sync_back.py -v
npm ci
npx tsx sync_back.ts --verbose
- name: Generate workflows
working-directory: pr-checks
+2 -2
View File
@@ -46053,14 +46053,14 @@ var require_package = __commonJS({
"eslint-import-resolver-typescript": "^3.8.7",
"eslint-plugin-github": "^6.0.0",
"eslint-plugin-import-x": "^4.16.1",
"eslint-plugin-jsdoc": "^62.6.0",
"eslint-plugin-jsdoc": "^62.7.1",
"eslint-plugin-no-async-foreach": "^0.1.1",
glob: "^11.1.0",
globals: "^17.3.0",
nock: "^14.0.11",
sinon: "^21.0.1",
typescript: "^5.9.3",
"typescript-eslint": "^8.56.0"
"typescript-eslint": "^8.56.1"
},
overrides: {
"@actions/tool-cache": {
+2 -2
View File
@@ -46053,14 +46053,14 @@ var require_package = __commonJS({
"eslint-import-resolver-typescript": "^3.8.7",
"eslint-plugin-github": "^6.0.0",
"eslint-plugin-import-x": "^4.16.1",
"eslint-plugin-jsdoc": "^62.6.0",
"eslint-plugin-jsdoc": "^62.7.1",
"eslint-plugin-no-async-foreach": "^0.1.1",
glob: "^11.1.0",
globals: "^17.3.0",
nock: "^14.0.11",
sinon: "^21.0.1",
typescript: "^5.9.3",
"typescript-eslint": "^8.56.0"
"typescript-eslint": "^8.56.1"
},
overrides: {
"@actions/tool-cache": {
+2 -2
View File
@@ -46053,14 +46053,14 @@ var require_package = __commonJS({
"eslint-import-resolver-typescript": "^3.8.7",
"eslint-plugin-github": "^6.0.0",
"eslint-plugin-import-x": "^4.16.1",
"eslint-plugin-jsdoc": "^62.6.0",
"eslint-plugin-jsdoc": "^62.7.1",
"eslint-plugin-no-async-foreach": "^0.1.1",
glob: "^11.1.0",
globals: "^17.3.0",
nock: "^14.0.11",
sinon: "^21.0.1",
typescript: "^5.9.3",
"typescript-eslint": "^8.56.0"
"typescript-eslint": "^8.56.1"
},
overrides: {
"@actions/tool-cache": {
+2 -2
View File
@@ -46053,14 +46053,14 @@ var require_package = __commonJS({
"eslint-import-resolver-typescript": "^3.8.7",
"eslint-plugin-github": "^6.0.0",
"eslint-plugin-import-x": "^4.16.1",
"eslint-plugin-jsdoc": "^62.6.0",
"eslint-plugin-jsdoc": "^62.7.1",
"eslint-plugin-no-async-foreach": "^0.1.1",
glob: "^11.1.0",
globals: "^17.3.0",
nock: "^14.0.11",
sinon: "^21.0.1",
typescript: "^5.9.3",
"typescript-eslint": "^8.56.0"
"typescript-eslint": "^8.56.1"
},
overrides: {
"@actions/tool-cache": {
+119 -96
View File
@@ -46053,14 +46053,14 @@ var require_package = __commonJS({
"eslint-import-resolver-typescript": "^3.8.7",
"eslint-plugin-github": "^6.0.0",
"eslint-plugin-import-x": "^4.16.1",
"eslint-plugin-jsdoc": "^62.6.0",
"eslint-plugin-jsdoc": "^62.7.1",
"eslint-plugin-no-async-foreach": "^0.1.1",
glob: "^11.1.0",
globals: "^17.3.0",
nock: "^14.0.11",
sinon: "^21.0.1",
typescript: "^5.9.3",
"typescript-eslint": "^8.56.0"
"typescript-eslint": "^8.56.1"
},
overrides: {
"@actions/tool-cache": {
@@ -106393,9 +106393,9 @@ var OVERLAY_ANALYSIS_CODE_SCANNING_FEATURES = {
rust: "overlay_analysis_code_scanning_rust" /* OverlayAnalysisCodeScanningRust */,
swift: "overlay_analysis_code_scanning_swift" /* OverlayAnalysisCodeScanningSwift */
};
async function isOverlayAnalysisFeatureEnabled(features, codeql, languages, codeScanningConfig) {
async function checkOverlayAnalysisFeatureEnabled(features, codeql, languages, codeScanningConfig) {
if (!await features.getValue("overlay_analysis" /* OverlayAnalysis */, codeql)) {
return false;
return new Failure("overall-feature-not-enabled" /* OverallFeatureNotEnabled */);
}
let enableForCodeScanningOnly = false;
for (const language of languages) {
@@ -106408,17 +106408,20 @@ async function isOverlayAnalysisFeatureEnabled(features, codeql, languages, code
enableForCodeScanningOnly = true;
continue;
}
return false;
return new Failure("language-not-enabled" /* LanguageNotEnabled */);
}
if (enableForCodeScanningOnly) {
return codeScanningConfig["disable-default-queries"] !== true && codeScanningConfig.packs === void 0 && codeScanningConfig.queries === void 0 && codeScanningConfig["query-filters"] === void 0;
const usesDefaultQueriesOnly = codeScanningConfig["disable-default-queries"] !== true && codeScanningConfig.packs === void 0 && codeScanningConfig.queries === void 0 && codeScanningConfig["query-filters"] === void 0;
if (!usesDefaultQueriesOnly) {
return new Failure("non-default-queries" /* NonDefaultQueries */);
}
}
return true;
return new Success(void 0);
}
function runnerHasSufficientDiskSpace(diskUsage, logger, useV2ResourceChecks) {
const minimumDiskSpaceBytes = useV2ResourceChecks ? OVERLAY_MINIMUM_AVAILABLE_DISK_SPACE_V2_BYTES : OVERLAY_MINIMUM_AVAILABLE_DISK_SPACE_BYTES;
if (diskUsage === void 0 || diskUsage.numAvailableBytes < minimumDiskSpaceBytes) {
const diskSpaceMb = diskUsage === void 0 ? 0 : Math.round(diskUsage.numAvailableBytes / 1e6);
if (diskUsage.numAvailableBytes < minimumDiskSpaceBytes) {
const diskSpaceMb = Math.round(diskUsage.numAvailableBytes / 1e6);
const minimumDiskSpaceMb = Math.round(minimumDiskSpaceBytes / 1e6);
logger.info(
`Setting overlay database mode to ${"none" /* None */} due to insufficient disk space (${diskSpaceMb} MB, needed ${minimumDiskSpaceMb} MB).`
@@ -106449,93 +106452,110 @@ async function runnerHasSufficientMemory(codeql, ramInput, logger) {
);
return true;
}
async function runnerSupportsOverlayAnalysis(codeql, diskUsage, ramInput, logger, useV2ResourceChecks) {
async function checkRunnerResources(codeql, diskUsage, ramInput, logger, useV2ResourceChecks) {
if (!runnerHasSufficientDiskSpace(diskUsage, logger, useV2ResourceChecks)) {
return false;
return new Failure("insufficient-disk-space" /* InsufficientDiskSpace */);
}
if (!await runnerHasSufficientMemory(codeql, ramInput, logger)) {
return false;
return new Failure("insufficient-memory" /* InsufficientMemory */);
}
return true;
return new Success(void 0);
}
async function getOverlayDatabaseMode(codeql, features, languages, sourceRoot, buildMode, ramInput, codeScanningConfig, repositoryProperties, gitVersion, logger) {
let overlayDatabaseMode = "none" /* None */;
let useOverlayDatabaseCaching = false;
let disabledReason;
async function checkOverlayEnablement(codeql, features, languages, sourceRoot, buildMode, ramInput, codeScanningConfig, repositoryProperties, gitVersion, logger) {
const modeEnv = process.env.CODEQL_OVERLAY_DATABASE_MODE;
if (modeEnv === "overlay" /* Overlay */ || modeEnv === "overlay-base" /* OverlayBase */ || modeEnv === "none" /* None */) {
overlayDatabaseMode = modeEnv;
logger.info(
`Setting overlay database mode to ${overlayDatabaseMode} from the CODEQL_OVERLAY_DATABASE_MODE environment variable.`
`Setting overlay database mode to ${modeEnv} from the CODEQL_OVERLAY_DATABASE_MODE environment variable.`
);
} else if (repositoryProperties["github-codeql-disable-overlay" /* DISABLE_OVERLAY */] === true) {
if (modeEnv === "none" /* None */) {
return new Failure("disabled-by-environment-variable" /* DisabledByEnvironmentVariable */);
}
return validateOverlayDatabaseMode(
modeEnv,
false,
codeql,
languages,
sourceRoot,
buildMode,
gitVersion,
logger
);
}
if (repositoryProperties["github-codeql-disable-overlay" /* DISABLE_OVERLAY */] === true) {
logger.info(
`Setting overlay database mode to ${"none" /* None */} because the ${"github-codeql-disable-overlay" /* DISABLE_OVERLAY */} repository property is set to true.`
);
overlayDatabaseMode = "none" /* None */;
disabledReason = "disabled-by-repository-property" /* DisabledByRepositoryProperty */;
} else if (await isOverlayAnalysisFeatureEnabled(
return new Failure("disabled-by-repository-property" /* DisabledByRepositoryProperty */);
}
const featureResult = await checkOverlayAnalysisFeatureEnabled(
features,
codeql,
languages,
codeScanningConfig
)) {
const performResourceChecks = !await features.getValue(
"overlay_analysis_skip_resource_checks" /* OverlayAnalysisSkipResourceChecks */,
codeql
);
if (featureResult.isFailure()) {
return featureResult;
}
const performResourceChecks = !await features.getValue(
"overlay_analysis_skip_resource_checks" /* OverlayAnalysisSkipResourceChecks */,
codeql
);
const useV2ResourceChecks = await features.getValue(
"overlay_analysis_resource_checks_v2" /* OverlayAnalysisResourceChecksV2 */
);
const checkOverlayStatus = await features.getValue(
"overlay_analysis_status_check" /* OverlayAnalysisStatusCheck */
);
const needDiskUsage = performResourceChecks || checkOverlayStatus;
const diskUsage = needDiskUsage ? await checkDiskUsage(logger) : void 0;
if (needDiskUsage && diskUsage === void 0) {
logger.warning(
`Unable to determine disk usage, therefore setting overlay database mode to ${"none" /* None */}.`
);
const useV2ResourceChecks = await features.getValue(
"overlay_analysis_resource_checks_v2" /* OverlayAnalysisResourceChecksV2 */
return new Failure("unable-to-determine-disk-usage" /* UnableToDetermineDiskUsage */);
}
const resourceResult = performResourceChecks && diskUsage !== void 0 ? await checkRunnerResources(
codeql,
diskUsage,
ramInput,
logger,
useV2ResourceChecks
) : new Success(void 0);
if (resourceResult.isFailure()) {
return resourceResult;
}
if (checkOverlayStatus && diskUsage !== void 0 && await shouldSkipOverlayAnalysis(codeql, languages, diskUsage, logger)) {
logger.info(
`Setting overlay database mode to ${"none" /* None */} because overlay analysis previously failed with this combination of languages, disk space, and CodeQL version.`
);
const checkOverlayStatus = await features.getValue(
"overlay_analysis_status_check" /* OverlayAnalysisStatusCheck */
return new Failure("skipped-due-to-cached-status" /* SkippedDueToCachedStatus */);
}
let overlayDatabaseMode;
if (isAnalyzingPullRequest()) {
overlayDatabaseMode = "overlay" /* Overlay */;
logger.info(
`Setting overlay database mode to ${overlayDatabaseMode} with caching because we are analyzing a pull request.`
);
} else if (await isAnalyzingDefaultBranch()) {
overlayDatabaseMode = "overlay-base" /* OverlayBase */;
logger.info(
`Setting overlay database mode to ${overlayDatabaseMode} with caching because we are analyzing the default branch.`
);
const diskUsage = performResourceChecks || checkOverlayStatus ? await checkDiskUsage(logger) : void 0;
if (performResourceChecks && !await runnerSupportsOverlayAnalysis(
codeql,
diskUsage,
ramInput,
logger,
useV2ResourceChecks
)) {
overlayDatabaseMode = "none" /* None */;
disabledReason = "insufficient-resources" /* InsufficientResources */;
} else if (checkOverlayStatus && diskUsage === void 0) {
logger.warning(
`Unable to determine disk usage, therefore setting overlay database mode to ${"none" /* None */}.`
);
overlayDatabaseMode = "none" /* None */;
disabledReason = "unable-to-determine-disk-usage" /* UnableToDetermineDiskUsage */;
} else if (checkOverlayStatus && diskUsage && await shouldSkipOverlayAnalysis(codeql, languages, diskUsage, logger)) {
logger.info(
`Setting overlay database mode to ${"none" /* None */} because overlay analysis previously failed with this combination of languages, disk space, and CodeQL version.`
);
overlayDatabaseMode = "none" /* None */;
disabledReason = "skipped-due-to-cached-status" /* SkippedDueToCachedStatus */;
} else if (isAnalyzingPullRequest()) {
overlayDatabaseMode = "overlay" /* Overlay */;
useOverlayDatabaseCaching = true;
logger.info(
`Setting overlay database mode to ${overlayDatabaseMode} with caching because we are analyzing a pull request.`
);
} else if (await isAnalyzingDefaultBranch()) {
overlayDatabaseMode = "overlay-base" /* OverlayBase */;
useOverlayDatabaseCaching = true;
logger.info(
`Setting overlay database mode to ${overlayDatabaseMode} with caching because we are analyzing the default branch.`
);
}
} else {
disabledReason = "feature-not-enabled" /* FeatureNotEnabled */;
}
const disabledResult = (reason) => ({
overlayDatabaseMode: "none" /* None */,
useOverlayDatabaseCaching: false,
disabledReason: reason
});
if (overlayDatabaseMode === "none" /* None */) {
return disabledResult(disabledReason);
return new Failure("not-pull-request-or-default-branch" /* NotPullRequestOrDefaultBranch */);
}
return validateOverlayDatabaseMode(
overlayDatabaseMode,
true,
codeql,
languages,
sourceRoot,
buildMode,
gitVersion,
logger
);
}
async function validateOverlayDatabaseMode(overlayDatabaseMode, useOverlayDatabaseCaching, codeql, languages, sourceRoot, buildMode, gitVersion, logger) {
if (buildMode !== "none" /* None */ && (await Promise.all(
languages.map(
async (l) => l !== "go" /* go */ && // Workaround to allow overlay analysis for Go with any build
@@ -106548,37 +106568,36 @@ async function getOverlayDatabaseMode(codeql, features, languages, sourceRoot, b
logger.warning(
`Cannot build an ${overlayDatabaseMode} database because build-mode is set to "${buildMode}" instead of "none". Falling back to creating a normal full database instead.`
);
return disabledResult("incompatible-build-mode" /* IncompatibleBuildMode */);
return new Failure("incompatible-build-mode" /* IncompatibleBuildMode */);
}
if (!await codeQlVersionAtLeast(codeql, CODEQL_OVERLAY_MINIMUM_VERSION)) {
logger.warning(
`Cannot build an ${overlayDatabaseMode} database because the CodeQL CLI is older than ${CODEQL_OVERLAY_MINIMUM_VERSION}. Falling back to creating a normal full database instead.`
);
return disabledResult("incompatible-codeql" /* IncompatibleCodeQl */);
return new Failure("incompatible-codeql" /* IncompatibleCodeQl */);
}
if (await getGitRoot(sourceRoot) === void 0) {
logger.warning(
`Cannot build an ${overlayDatabaseMode} database because the source root "${sourceRoot}" is not inside a git repository. Falling back to creating a normal full database instead.`
);
return disabledResult("no-git-root" /* NoGitRoot */);
return new Failure("no-git-root" /* NoGitRoot */);
}
if (gitVersion === void 0) {
logger.warning(
`Cannot build an ${overlayDatabaseMode} database because the Git version could not be determined. Falling back to creating a normal full database instead.`
);
return disabledResult("incompatible-git" /* IncompatibleGit */);
return new Failure("incompatible-git" /* IncompatibleGit */);
}
if (!gitVersion.isAtLeast(GIT_MINIMUM_VERSION_FOR_OVERLAY)) {
logger.warning(
`Cannot build an ${overlayDatabaseMode} database because the installed Git version is older than ${GIT_MINIMUM_VERSION_FOR_OVERLAY}. Falling back to creating a normal full database instead.`
);
return disabledResult("incompatible-git" /* IncompatibleGit */);
return new Failure("incompatible-git" /* IncompatibleGit */);
}
return {
return new Success({
overlayDatabaseMode,
useOverlayDatabaseCaching,
disabledReason
};
useOverlayDatabaseCaching
});
}
function dbLocationOrDefault(dbLocation, tempDir) {
return dbLocation || path9.resolve(tempDir, "codeql_databases");
@@ -106666,11 +106685,7 @@ async function initConfig(features, inputs) {
} else {
logger.debug(`Skipping check for generated files.`);
}
const {
overlayDatabaseMode,
useOverlayDatabaseCaching,
disabledReason: overlayDisabledReason
} = await getOverlayDatabaseMode(
const overlayDatabaseModeResult = await checkOverlayEnablement(
inputs.codeql,
inputs.features,
config.languages,
@@ -106682,19 +106697,27 @@ async function initConfig(features, inputs) {
gitVersion,
logger
);
logger.info(
`Using overlay database mode: ${overlayDatabaseMode} ${useOverlayDatabaseCaching ? "with" : "without"} caching.`
);
config.overlayDatabaseMode = overlayDatabaseMode;
config.useOverlayDatabaseCaching = useOverlayDatabaseCaching;
if (overlayDisabledReason !== void 0) {
if (overlayDatabaseModeResult.isSuccess()) {
const { overlayDatabaseMode, useOverlayDatabaseCaching } = overlayDatabaseModeResult.value;
logger.info(
`Using overlay database mode: ${overlayDatabaseMode} ${useOverlayDatabaseCaching ? "with" : "without"} caching.`
);
config.overlayDatabaseMode = overlayDatabaseMode;
config.useOverlayDatabaseCaching = useOverlayDatabaseCaching;
} else {
const overlayDisabledReason = overlayDatabaseModeResult.value;
logger.info(
`Using overlay database mode: ${"none" /* None */} without caching.`
);
config.overlayDatabaseMode = "none" /* None */;
config.useOverlayDatabaseCaching = false;
await addOverlayDisablementDiagnostics(
config,
inputs.codeql,
overlayDisabledReason
);
}
if (overlayDatabaseMode === "overlay" /* Overlay */ || await shouldPerformDiffInformedAnalysis(
if (config.overlayDatabaseMode === "overlay" /* Overlay */ || await shouldPerformDiffInformedAnalysis(
inputs.codeql,
inputs.features,
logger
+2 -2
View File
@@ -46053,14 +46053,14 @@ var require_package = __commonJS({
"eslint-import-resolver-typescript": "^3.8.7",
"eslint-plugin-github": "^6.0.0",
"eslint-plugin-import-x": "^4.16.1",
"eslint-plugin-jsdoc": "^62.6.0",
"eslint-plugin-jsdoc": "^62.7.1",
"eslint-plugin-no-async-foreach": "^0.1.1",
glob: "^11.1.0",
globals: "^17.3.0",
nock: "^14.0.11",
sinon: "^21.0.1",
typescript: "^5.9.3",
"typescript-eslint": "^8.56.0"
"typescript-eslint": "^8.56.1"
},
overrides: {
"@actions/tool-cache": {
+2 -2
View File
@@ -46053,14 +46053,14 @@ var require_package = __commonJS({
"eslint-import-resolver-typescript": "^3.8.7",
"eslint-plugin-github": "^6.0.0",
"eslint-plugin-import-x": "^4.16.1",
"eslint-plugin-jsdoc": "^62.6.0",
"eslint-plugin-jsdoc": "^62.7.1",
"eslint-plugin-no-async-foreach": "^0.1.1",
glob: "^11.1.0",
globals: "^17.3.0",
nock: "^14.0.11",
sinon: "^21.0.1",
typescript: "^5.9.3",
"typescript-eslint": "^8.56.0"
"typescript-eslint": "^8.56.1"
},
overrides: {
"@actions/tool-cache": {
+2 -2
View File
@@ -46053,14 +46053,14 @@ var require_package = __commonJS({
"eslint-import-resolver-typescript": "^3.8.7",
"eslint-plugin-github": "^6.0.0",
"eslint-plugin-import-x": "^4.16.1",
"eslint-plugin-jsdoc": "^62.6.0",
"eslint-plugin-jsdoc": "^62.7.1",
"eslint-plugin-no-async-foreach": "^0.1.1",
glob: "^11.1.0",
globals: "^17.3.0",
nock: "^14.0.11",
sinon: "^21.0.1",
typescript: "^5.9.3",
"typescript-eslint": "^8.56.0"
"typescript-eslint": "^8.56.1"
},
overrides: {
"@actions/tool-cache": {
+2 -2
View File
@@ -46053,14 +46053,14 @@ var require_package = __commonJS({
"eslint-import-resolver-typescript": "^3.8.7",
"eslint-plugin-github": "^6.0.0",
"eslint-plugin-import-x": "^4.16.1",
"eslint-plugin-jsdoc": "^62.6.0",
"eslint-plugin-jsdoc": "^62.7.1",
"eslint-plugin-no-async-foreach": "^0.1.1",
glob: "^11.1.0",
globals: "^17.3.0",
nock: "^14.0.11",
sinon: "^21.0.1",
typescript: "^5.9.3",
"typescript-eslint": "^8.56.0"
"typescript-eslint": "^8.56.1"
},
overrides: {
"@actions/tool-cache": {
+2 -2
View File
@@ -47350,14 +47350,14 @@ var require_package = __commonJS({
"eslint-import-resolver-typescript": "^3.8.7",
"eslint-plugin-github": "^6.0.0",
"eslint-plugin-import-x": "^4.16.1",
"eslint-plugin-jsdoc": "^62.6.0",
"eslint-plugin-jsdoc": "^62.7.1",
"eslint-plugin-no-async-foreach": "^0.1.1",
glob: "^11.1.0",
globals: "^17.3.0",
nock: "^14.0.11",
sinon: "^21.0.1",
typescript: "^5.9.3",
"typescript-eslint": "^8.56.0"
"typescript-eslint": "^8.56.1"
},
overrides: {
"@actions/tool-cache": {
+2 -2
View File
@@ -46053,14 +46053,14 @@ var require_package = __commonJS({
"eslint-import-resolver-typescript": "^3.8.7",
"eslint-plugin-github": "^6.0.0",
"eslint-plugin-import-x": "^4.16.1",
"eslint-plugin-jsdoc": "^62.6.0",
"eslint-plugin-jsdoc": "^62.7.1",
"eslint-plugin-no-async-foreach": "^0.1.1",
glob: "^11.1.0",
globals: "^17.3.0",
nock: "^14.0.11",
sinon: "^21.0.1",
typescript: "^5.9.3",
"typescript-eslint": "^8.56.0"
"typescript-eslint": "^8.56.1"
},
overrides: {
"@actions/tool-cache": {
+2 -2
View File
@@ -46053,14 +46053,14 @@ var require_package = __commonJS({
"eslint-import-resolver-typescript": "^3.8.7",
"eslint-plugin-github": "^6.0.0",
"eslint-plugin-import-x": "^4.16.1",
"eslint-plugin-jsdoc": "^62.6.0",
"eslint-plugin-jsdoc": "^62.7.1",
"eslint-plugin-no-async-foreach": "^0.1.1",
glob: "^11.1.0",
globals: "^17.3.0",
nock: "^14.0.11",
sinon: "^21.0.1",
typescript: "^5.9.3",
"typescript-eslint": "^8.56.0"
"typescript-eslint": "^8.56.1"
},
overrides: {
"@actions/tool-cache": {
+95 -82
View File
@@ -52,14 +52,14 @@
"eslint-import-resolver-typescript": "^3.8.7",
"eslint-plugin-github": "^6.0.0",
"eslint-plugin-import-x": "^4.16.1",
"eslint-plugin-jsdoc": "^62.6.0",
"eslint-plugin-jsdoc": "^62.7.1",
"eslint-plugin-no-async-foreach": "^0.1.1",
"glob": "^11.1.0",
"globals": "^17.3.0",
"nock": "^14.0.11",
"sinon": "^21.0.1",
"typescript": "^5.9.3",
"typescript-eslint": "^8.56.0"
"typescript-eslint": "^8.56.1"
}
},
"node_modules/@aashutoshrathi/word-wrap": {
@@ -2553,17 +2553,17 @@
"license": "MIT"
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.56.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.56.0.tgz",
"integrity": "sha512-lRyPDLzNCuae71A3t9NEINBiTn7swyOhvUj3MyUOxb8x6g6vPEFoOU+ZRmGMusNC3X3YMhqMIX7i8ShqhT74Pw==",
"version": "8.56.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.56.1.tgz",
"integrity": "sha512-Jz9ZztpB37dNC+HU2HI28Bs9QXpzCz+y/twHOwhyrIRdbuVDxSytJNDl6z/aAKlaRIwC7y8wJdkBv7FxYGgi0A==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/regexpp": "^4.12.2",
"@typescript-eslint/scope-manager": "8.56.0",
"@typescript-eslint/type-utils": "8.56.0",
"@typescript-eslint/utils": "8.56.0",
"@typescript-eslint/visitor-keys": "8.56.0",
"@typescript-eslint/scope-manager": "8.56.1",
"@typescript-eslint/type-utils": "8.56.1",
"@typescript-eslint/utils": "8.56.1",
"@typescript-eslint/visitor-keys": "8.56.1",
"ignore": "^7.0.5",
"natural-compare": "^1.4.0",
"ts-api-utils": "^2.4.0"
@@ -2576,7 +2576,7 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"@typescript-eslint/parser": "^8.56.0",
"@typescript-eslint/parser": "^8.56.1",
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.0.0"
}
@@ -2592,16 +2592,16 @@
}
},
"node_modules/@typescript-eslint/parser": {
"version": "8.56.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.56.0.tgz",
"integrity": "sha512-IgSWvLobTDOjnaxAfDTIHaECbkNlAlKv2j5SjpB2v7QHKv1FIfjwMy8FsDbVfDX/KjmCmYICcw7uGaXLhtsLNg==",
"version": "8.56.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.56.1.tgz",
"integrity": "sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/scope-manager": "8.56.0",
"@typescript-eslint/types": "8.56.0",
"@typescript-eslint/typescript-estree": "8.56.0",
"@typescript-eslint/visitor-keys": "8.56.0",
"@typescript-eslint/scope-manager": "8.56.1",
"@typescript-eslint/types": "8.56.1",
"@typescript-eslint/typescript-estree": "8.56.1",
"@typescript-eslint/visitor-keys": "8.56.1",
"debug": "^4.4.3"
},
"engines": {
@@ -2635,14 +2635,14 @@
}
},
"node_modules/@typescript-eslint/project-service": {
"version": "8.56.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.56.0.tgz",
"integrity": "sha512-M3rnyL1vIQOMeWxTWIW096/TtVP+8W3p/XnaFflhmcFp+U4zlxUxWj4XwNs6HbDeTtN4yun0GNTTDBw/SvufKg==",
"version": "8.56.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.56.1.tgz",
"integrity": "sha512-TAdqQTzHNNvlVFfR+hu2PDJrURiwKsUvxFn1M0h95BB8ah5jejas08jUWG4dBA68jDMI988IvtfdAI53JzEHOQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/tsconfig-utils": "^8.56.0",
"@typescript-eslint/types": "^8.56.0",
"@typescript-eslint/tsconfig-utils": "^8.56.1",
"@typescript-eslint/types": "^8.56.1",
"debug": "^4.4.3"
},
"engines": {
@@ -2675,14 +2675,14 @@
}
},
"node_modules/@typescript-eslint/scope-manager": {
"version": "8.56.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.56.0.tgz",
"integrity": "sha512-7UiO/XwMHquH+ZzfVCfUNkIXlp/yQjjnlYUyYz7pfvlK3/EyyN6BK+emDmGNyQLBtLGaYrTAI6KOw8tFucWL2w==",
"version": "8.56.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.56.1.tgz",
"integrity": "sha512-YAi4VDKcIZp0O4tz/haYKhmIDZFEUPOreKbfdAN3SzUDMcPhJ8QI99xQXqX+HoUVq8cs85eRKnD+rne2UAnj2w==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.56.0",
"@typescript-eslint/visitor-keys": "8.56.0"
"@typescript-eslint/types": "8.56.1",
"@typescript-eslint/visitor-keys": "8.56.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -2693,9 +2693,9 @@
}
},
"node_modules/@typescript-eslint/tsconfig-utils": {
"version": "8.56.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.56.0.tgz",
"integrity": "sha512-bSJoIIt4o3lKXD3xmDh9chZcjCz5Lk8xS7Rxn+6l5/pKrDpkCwtQNQQwZ2qRPk7TkUYhrq3WPIHXOXlbXP0itg==",
"version": "8.56.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.56.1.tgz",
"integrity": "sha512-qOtCYzKEeyr3aR9f28mPJqBty7+DBqsdd63eO0yyDwc6vgThj2UjWfJIcsFeSucYydqcuudMOprZ+x1SpF3ZuQ==",
"dev": true,
"license": "MIT",
"engines": {
@@ -2710,15 +2710,15 @@
}
},
"node_modules/@typescript-eslint/type-utils": {
"version": "8.56.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.56.0.tgz",
"integrity": "sha512-qX2L3HWOU2nuDs6GzglBeuFXviDODreS58tLY/BALPC7iu3Fa+J7EOTwnX9PdNBxUI7Uh0ntP0YWGnxCkXzmfA==",
"version": "8.56.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.56.1.tgz",
"integrity": "sha512-yB/7dxi7MgTtGhZdaHCemf7PuwrHMenHjmzgUW1aJpO+bBU43OycnM3Wn+DdvDO/8zzA9HlhaJ0AUGuvri4oGg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.56.0",
"@typescript-eslint/typescript-estree": "8.56.0",
"@typescript-eslint/utils": "8.56.0",
"@typescript-eslint/types": "8.56.1",
"@typescript-eslint/typescript-estree": "8.56.1",
"@typescript-eslint/utils": "8.56.1",
"debug": "^4.4.3",
"ts-api-utils": "^2.4.0"
},
@@ -2753,9 +2753,9 @@
}
},
"node_modules/@typescript-eslint/types": {
"version": "8.56.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.56.0.tgz",
"integrity": "sha512-DBsLPs3GsWhX5HylbP9HNG15U0bnwut55Lx12bHB9MpXxQ+R5GC8MwQe+N1UFXxAeQDvEsEDY6ZYwX03K7Z6HQ==",
"version": "8.56.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.56.1.tgz",
"integrity": "sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -2767,18 +2767,18 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
"version": "8.56.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.56.0.tgz",
"integrity": "sha512-ex1nTUMWrseMltXUHmR2GAQ4d+WjkZCT4f+4bVsps8QEdh0vlBsaCokKTPlnqBFqqGaxilDNJG7b8dolW2m43Q==",
"version": "8.56.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.56.1.tgz",
"integrity": "sha512-qzUL1qgalIvKWAf9C1HpvBjif+Vm6rcT5wZd4VoMb9+Km3iS3Cv9DY6dMRMDtPnwRAFyAi7YXJpTIEXLvdfPxg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/project-service": "8.56.0",
"@typescript-eslint/tsconfig-utils": "8.56.0",
"@typescript-eslint/types": "8.56.0",
"@typescript-eslint/visitor-keys": "8.56.0",
"@typescript-eslint/project-service": "8.56.1",
"@typescript-eslint/tsconfig-utils": "8.56.1",
"@typescript-eslint/types": "8.56.1",
"@typescript-eslint/visitor-keys": "8.56.1",
"debug": "^4.4.3",
"minimatch": "^9.0.5",
"minimatch": "^10.2.2",
"semver": "^7.7.3",
"tinyglobby": "^0.2.15",
"ts-api-utils": "^2.4.0"
@@ -2794,14 +2794,27 @@
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
"integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
"dev": true,
"license": "MIT",
"engines": {
"node": "18 || 20 || >=22"
}
},
"node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz",
"integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==",
"dev": true,
"license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0"
"balanced-match": "^4.0.2"
},
"engines": {
"node": "18 || 20 || >=22"
}
},
"node_modules/@typescript-eslint/typescript-estree/node_modules/debug": {
@@ -2823,32 +2836,32 @@
}
},
"node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
"version": "9.0.9",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz",
"integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==",
"version": "10.2.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz",
"integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==",
"dev": true,
"license": "ISC",
"license": "BlueOak-1.0.0",
"dependencies": {
"brace-expansion": "^2.0.2"
"brace-expansion": "^5.0.2"
},
"engines": {
"node": ">=16 || 14 >=14.17"
"node": "18 || 20 || >=22"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/@typescript-eslint/utils": {
"version": "8.56.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.56.0.tgz",
"integrity": "sha512-RZ3Qsmi2nFGsS+n+kjLAYDPVlrzf7UhTffrDIKr+h2yzAlYP/y5ZulU0yeDEPItos2Ph46JAL5P/On3pe7kDIQ==",
"version": "8.56.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.56.1.tgz",
"integrity": "sha512-HPAVNIME3tABJ61siYlHzSWCGtOoeP2RTIaHXFMPqjrQKCGB9OgUVdiNgH7TJS2JNIQ5qQ4RsAUDuGaGme/KOA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.9.1",
"@typescript-eslint/scope-manager": "8.56.0",
"@typescript-eslint/types": "8.56.0",
"@typescript-eslint/typescript-estree": "8.56.0"
"@typescript-eslint/scope-manager": "8.56.1",
"@typescript-eslint/types": "8.56.1",
"@typescript-eslint/typescript-estree": "8.56.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -2863,13 +2876,13 @@
}
},
"node_modules/@typescript-eslint/visitor-keys": {
"version": "8.56.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.56.0.tgz",
"integrity": "sha512-q+SL+b+05Ud6LbEE35qe4A99P+htKTKVbyiNEe45eCbJFyh/HVK9QXwlrbz+Q4L8SOW4roxSVwXYj4DMBT7Ieg==",
"version": "8.56.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.56.1.tgz",
"integrity": "sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.56.0",
"@typescript-eslint/types": "8.56.1",
"eslint-visitor-keys": "^5.0.0"
},
"engines": {
@@ -2881,9 +2894,9 @@
}
},
"node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.0.tgz",
"integrity": "sha512-A0XeIi7CXU7nPlfHS9loMYEKxUaONu/hTEzHTGba9Huu94Cq1hPivf+DE5erJozZOky0LfvXAyrV/tcswpLI0Q==",
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
"integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
"dev": true,
"license": "Apache-2.0",
"engines": {
@@ -5146,9 +5159,9 @@
}
},
"node_modules/eslint-plugin-jsdoc": {
"version": "62.6.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-62.6.0.tgz",
"integrity": "sha512-Z18zZD1Q2m9usqFbAzb30z+lF8bzE4WiUy+dfOXljJlZ1Jm5uhkuAWfGV97FYyh+WlKfrvpDYs+s1z45eZWMfA==",
"version": "62.7.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-62.7.1.tgz",
"integrity": "sha512-4Zvx99Q7d1uggYBUX/AIjvoyqXhluGbbKrRmG8SQTLprPFg6fa293tVJH1o1GQwNe3lUydd8ZHzn37OaSncgSQ==",
"dev": true,
"license": "BSD-3-Clause",
"dependencies": {
@@ -5163,7 +5176,7 @@
"html-entities": "^2.6.0",
"object-deep-merge": "^2.0.0",
"parse-imports-exports": "^0.2.4",
"semver": "^7.7.3",
"semver": "^7.7.4",
"spdx-expression-parse": "^4.0.0",
"to-valid-identifier": "^1.0.0"
},
@@ -5171,7 +5184,7 @@
"node": "^20.19.0 || ^22.13.0 || >=24"
},
"peerDependencies": {
"eslint": "^7.0.0 || ^8.0.0 || ^9.0.0"
"eslint": "^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0"
}
},
"node_modules/eslint-plugin-jsdoc/node_modules/debug": {
@@ -9189,16 +9202,16 @@
}
},
"node_modules/typescript-eslint": {
"version": "8.56.0",
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.56.0.tgz",
"integrity": "sha512-c7toRLrotJ9oixgdW7liukZpsnq5CZ7PuKztubGYlNppuTqhIoWfhgHo/7EU0v06gS2l/x0i2NEFK1qMIf0rIg==",
"version": "8.56.1",
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.56.1.tgz",
"integrity": "sha512-U4lM6pjmBX7J5wk4szltF7I1cGBHXZopnAXCMXb3+fZ3B/0Z3hq3wS/CCUB2NZBNAExK92mCU2tEohWuwVMsDQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/eslint-plugin": "8.56.0",
"@typescript-eslint/parser": "8.56.0",
"@typescript-eslint/typescript-estree": "8.56.0",
"@typescript-eslint/utils": "8.56.0"
"@typescript-eslint/eslint-plugin": "8.56.1",
"@typescript-eslint/parser": "8.56.1",
"@typescript-eslint/typescript-estree": "8.56.1",
"@typescript-eslint/utils": "8.56.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+2 -2
View File
@@ -67,14 +67,14 @@
"eslint-import-resolver-typescript": "^3.8.7",
"eslint-plugin-github": "^6.0.0",
"eslint-plugin-import-x": "^4.16.1",
"eslint-plugin-jsdoc": "^62.6.0",
"eslint-plugin-jsdoc": "^62.7.1",
"eslint-plugin-no-async-foreach": "^0.1.1",
"glob": "^11.1.0",
"globals": "^17.3.0",
"nock": "^14.0.11",
"sinon": "^21.0.1",
"typescript": "^5.9.3",
"typescript-eslint": "^8.56.0"
"typescript-eslint": "^8.56.1"
},
"overrides": {
"@actions/tool-cache": {
-3
View File
@@ -1,4 +1 @@
env
__pycache__/
*.pyc
node_modules/
View File
+4 -4
View File
@@ -6,9 +6,9 @@ to one of the files in this directory.
## Updating workflows
Run `./sync.sh` to invoke the workflow generator and re-generate the workflow files in `.github/workflows/` based on the templates in `pr-checks/checks/`.
Alternatively, you can use `just`:
1. Install https://github.com/casey/just by whichever way you prefer.
2. Run `just update-pr-checks` in your terminal.
### If you don't want to install `just`
Manually run each step in the `justfile`.
-185
View File
@@ -1,185 +0,0 @@
#!/usr/bin/env python3
"""
Sync-back script to automatically update action versions in source templates
from the generated workflow files after Dependabot updates.
This script scans the generated workflow files (.github/workflows/__*.yml) to find
all external action versions used, then updates:
1. Hardcoded action versions in pr-checks/sync.py
2. Action version references in template files in pr-checks/checks/
The script automatically detects all actions used in generated workflows and
preserves version comments (e.g., # v1.2.3) when syncing versions.
This ensures that when Dependabot updates action versions in generated workflows,
those changes are properly synced back to the source templates. Regular workflow
files are updated directly by Dependabot and don't need sync-back.
"""
import os
import re
import glob
import argparse
import sys
from pathlib import Path
from typing import Dict, List
def scan_generated_workflows(workflow_dir: str) -> Dict[str, str]:
"""
Scan generated workflow files to extract the latest action versions.
Args:
workflow_dir: Path to .github/workflows directory
Returns:
Dictionary mapping action names to their latest versions (including comments)
"""
action_versions = {}
generated_files = glob.glob(os.path.join(workflow_dir, "__*.yml"))
for file_path in generated_files:
with open(file_path, 'r') as f:
content = f.read()
# Find all action uses in the file, including potential comments
# This pattern captures: action_name@version_with_possible_comment
pattern = r'uses:\s+([^/\s]+/[^@\s]+)@([^@\n]+)'
matches = re.findall(pattern, content)
for action_name, version_with_comment in matches:
# Only track non-local actions (those with / but not starting with ./)
if not action_name.startswith('./'):
# Assume that version numbers are consistent (this should be the case on a Dependabot update PR)
action_versions[action_name] = version_with_comment.rstrip()
return action_versions
def update_sync_py(sync_py_path: str, action_versions: Dict[str, str]) -> bool:
"""
Update hardcoded action versions in pr-checks/sync.py
Args:
sync_py_path: Path to sync.py file
action_versions: Dictionary of action names to versions (may include comments)
Returns:
True if file was modified, False otherwise
"""
if not os.path.exists(sync_py_path):
raise FileNotFoundError(f"Could not find {sync_py_path}")
with open(sync_py_path, 'r') as f:
content = f.read()
original_content = content
# Update hardcoded action versions
for action_name, version_with_comment in action_versions.items():
# Extract just the version part (before any comment) for sync.py
version = version_with_comment.split('#')[0].strip() if '#' in version_with_comment else version_with_comment.strip()
# Look for patterns like 'uses': 'actions/setup-node@v4'
# Note that this will break if we store an Action uses reference in a
# variable - that's a risk we're happy to take since in that case the
# PR checks will just fail.
pattern = rf"('uses':\s*'){re.escape(action_name)}@(?:[^']+)(')"
replacement = rf"\1{action_name}@{version}\2"
content = re.sub(pattern, replacement, content)
if content != original_content:
with open(sync_py_path, 'w') as f:
f.write(content)
print(f"Updated {sync_py_path}")
return True
else:
print(f"No changes needed in {sync_py_path}")
return False
def update_template_files(checks_dir: str, action_versions: Dict[str, str]) -> List[str]:
"""
Update action versions in template files in pr-checks/checks/
Args:
checks_dir: Path to pr-checks/checks directory
action_versions: Dictionary of action names to versions (may include comments)
Returns:
List of files that were modified
"""
modified_files = []
template_files = glob.glob(os.path.join(checks_dir, "*.yml"))
for file_path in template_files:
with open(file_path, 'r') as f:
content = f.read()
original_content = content
# Update action versions
for action_name, version_with_comment in action_versions.items():
# Look for patterns like 'uses: actions/setup-node@v4' or 'uses: actions/setup-node@sha # comment'
pattern = rf"(uses:\s+{re.escape(action_name)})@(?:[^@\n]+)"
replacement = rf"\1@{version_with_comment}"
content = re.sub(pattern, replacement, content)
if content != original_content:
with open(file_path, 'w') as f:
f.write(content)
modified_files.append(file_path)
print(f"Updated {file_path}")
return modified_files
def main():
parser = argparse.ArgumentParser(description="Sync action versions from generated workflows back to templates")
parser.add_argument("--verbose", "-v", action="store_true", help="Enable verbose output")
args = parser.parse_args()
# Get the repository root (assuming script is in pr-checks/)
script_dir = Path(__file__).parent
repo_root = script_dir.parent
workflow_dir = repo_root / ".github" / "workflows"
checks_dir = script_dir / "checks"
sync_py_path = script_dir / "sync.py"
print("Scanning generated workflows for latest action versions...")
action_versions = scan_generated_workflows(str(workflow_dir))
if args.verbose:
print("Found action versions:")
for action, version in action_versions.items():
print(f" {action}@{version}")
if not action_versions:
print("No action versions found in generated workflows")
return 1
# Update files
print("\nUpdating source files...")
modified_files = []
# Update sync.py
if update_sync_py(str(sync_py_path), action_versions):
modified_files.append(str(sync_py_path))
# Update template files
template_modified = update_template_files(str(checks_dir), action_versions)
modified_files.extend(template_modified)
if modified_files:
print(f"\nSync completed. Modified {len(modified_files)} files:")
for file_path in modified_files:
print(f" {file_path}")
else:
print("\nNo files needed updating - all action versions are already in sync")
return 0
if __name__ == "__main__":
sys.exit(main())
+250
View File
@@ -0,0 +1,250 @@
#!/usr/bin/env npx tsx
/*
Tests for the sync_back.ts script
*/
import * as assert from "node:assert/strict";
import * as fs from "node:fs";
import * as os from "node:os";
import * as path from "node:path";
import { afterEach, beforeEach, describe, it } from "node:test";
import {
scanGeneratedWorkflows,
updateSyncTs,
updateTemplateFiles,
} from "./sync_back";
let testDir: string;
let workflowDir: string;
let checksDir: string;
let syncTsPath: string;
beforeEach(() => {
/** Set up temporary directories and files for testing */
testDir = fs.mkdtempSync(path.join(os.tmpdir(), "sync-back-test-"));
workflowDir = path.join(testDir, ".github", "workflows");
checksDir = path.join(testDir, "pr-checks", "checks");
fs.mkdirSync(workflowDir, { recursive: true });
fs.mkdirSync(checksDir, { recursive: true });
// Create sync.ts file path
syncTsPath = path.join(testDir, "pr-checks", "sync.ts");
});
afterEach(() => {
/** Clean up temporary directories */
fs.rmSync(testDir, { recursive: true, force: true });
});
describe("scanGeneratedWorkflows", () => {
it("basic workflow scanning", () => {
/** Test basic workflow scanning functionality */
const workflowContent = `
name: Test Workflow
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v5
- uses: actions/setup-go@v6
`;
fs.writeFileSync(path.join(workflowDir, "__test.yml"), workflowContent);
const result = scanGeneratedWorkflows(workflowDir);
assert.equal(result["actions/checkout"], "v4");
assert.equal(result["actions/setup-node"], "v5");
assert.equal(result["actions/setup-go"], "v6");
});
it("scanning workflows with version comments", () => {
/** Test scanning workflows with version comments */
const workflowContent = `
name: Test Workflow
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ruby/setup-ruby@44511735964dcb71245e7e55f72539531f7bc0eb # v1.257.0
- uses: actions/setup-python@v6 # Latest Python
`;
fs.writeFileSync(path.join(workflowDir, "__test.yml"), workflowContent);
const result = scanGeneratedWorkflows(workflowDir);
assert.equal(result["actions/checkout"], "v4");
assert.equal(
result["ruby/setup-ruby"],
"44511735964dcb71245e7e55f72539531f7bc0eb # v1.257.0",
);
assert.equal(result["actions/setup-python"], "v6 # Latest Python");
});
it("ignores local actions", () => {
/** Test that local actions (starting with ./) are ignored */
const workflowContent = `
name: Test Workflow
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/local-action
- uses: ./another-local-action@v1
`;
fs.writeFileSync(path.join(workflowDir, "__test.yml"), workflowContent);
const result = scanGeneratedWorkflows(workflowDir);
assert.equal(result["actions/checkout"], "v4");
assert.equal("./.github/actions/local-action" in result, false);
assert.equal("./another-local-action" in result, false);
});
});
describe("updateSyncTs", () => {
it("updates sync.ts file", () => {
/** Test updating sync.ts file */
const syncTsContent = `
const steps = [
{
uses: "actions/setup-node@v4",
with: { "node-version": "16" },
},
{
uses: "actions/setup-go@v5",
with: { "go-version": "1.19" },
},
];
`;
fs.writeFileSync(syncTsPath, syncTsContent);
const actionVersions = {
"actions/setup-node": "v5",
"actions/setup-go": "v6",
};
const result = updateSyncTs(syncTsPath, actionVersions);
assert.equal(result, true);
const updatedContent = fs.readFileSync(syncTsPath, "utf8");
assert.ok(updatedContent.includes('uses: "actions/setup-node@v5"'));
assert.ok(updatedContent.includes('uses: "actions/setup-go@v6"'));
});
it("strips comments from versions", () => {
/** Test updating sync.ts file when versions have comments */
const syncTsContent = `
const steps = [
{
uses: "actions/setup-node@v4",
with: { "node-version": "16" },
},
];
`;
fs.writeFileSync(syncTsPath, syncTsContent);
const actionVersions = {
"actions/setup-node": "v5 # Latest version",
};
const result = updateSyncTs(syncTsPath, actionVersions);
assert.equal(result, true);
const updatedContent = fs.readFileSync(syncTsPath, "utf8");
// sync.ts should get the version without comment
assert.ok(updatedContent.includes('uses: "actions/setup-node@v5"'));
assert.ok(!updatedContent.includes("# Latest version"));
});
it("returns false when no changes are needed", () => {
/** Test that updateSyncTs returns false when no changes are needed */
const syncTsContent = `
const steps = [
{
uses: "actions/setup-node@v5",
with: { "node-version": "16" },
},
];
`;
fs.writeFileSync(syncTsPath, syncTsContent);
const actionVersions = {
"actions/setup-node": "v5",
};
const result = updateSyncTs(syncTsPath, actionVersions);
assert.equal(result, false);
});
});
describe("updateTemplateFiles", () => {
it("updates template files", () => {
/** Test updating template files */
const templateContent = `
name: Test Template
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v4
with:
node-version: 16
`;
const templatePath = path.join(checksDir, "test.yml");
fs.writeFileSync(templatePath, templateContent);
const actionVersions = {
"actions/checkout": "v4",
"actions/setup-node": "v5 # Latest",
};
const result = updateTemplateFiles(checksDir, actionVersions);
assert.equal(result.length, 1);
assert.ok(result.includes(templatePath));
const updatedContent = fs.readFileSync(templatePath, "utf8");
assert.ok(updatedContent.includes("uses: actions/checkout@v4"));
assert.ok(updatedContent.includes("uses: actions/setup-node@v5 # Latest"));
});
it("preserves version comments", () => {
/** Test that updating template files preserves version comments */
const templateContent = `
name: Test Template
steps:
- uses: ruby/setup-ruby@44511735964dcb71245e7e55f72539531f7bc0eb # v1.256.0
`;
const templatePath = path.join(checksDir, "test.yml");
fs.writeFileSync(templatePath, templateContent);
const actionVersions = {
"ruby/setup-ruby":
"55511735964dcb71245e7e55f72539531f7bc0eb # v1.257.0",
};
const result = updateTemplateFiles(checksDir, actionVersions);
assert.equal(result.length, 1);
const updatedContent = fs.readFileSync(templatePath, "utf8");
assert.ok(
updatedContent.includes(
"uses: ruby/setup-ruby@55511735964dcb71245e7e55f72539531f7bc0eb # v1.257.0",
),
);
});
});
+220
View File
@@ -0,0 +1,220 @@
#!/usr/bin/env npx tsx
/*
Sync-back script to automatically update action versions in source templates
from the generated workflow files after Dependabot updates.
This script scans the generated workflow files (.github/workflows/__*.yml) to find
all external action versions used, then updates:
1. Hardcoded action versions in pr-checks/sync.ts
2. Action version references in template files in pr-checks/checks/
The script automatically detects all actions used in generated workflows and
preserves version comments (e.g., # v1.2.3) when syncing versions.
This ensures that when Dependabot updates action versions in generated workflows,
those changes are properly synced back to the source templates. Regular workflow
files are updated directly by Dependabot and don't need sync-back.
*/
import { parseArgs } from "node:util";
import * as fs from "fs";
import * as path from "path";
const THIS_DIR = __dirname;
const CHECKS_DIR = path.join(THIS_DIR, "checks");
const WORKFLOW_DIR = path.join(THIS_DIR, "..", ".github", "workflows");
const SYNC_TS_PATH = path.join(THIS_DIR, "sync.ts");
/**
* Scan generated workflow files to extract the latest action versions.
*
* @param workflowDir - Path to .github/workflows directory
* @returns Map from action names to their latest versions (including comments)
*/
export function scanGeneratedWorkflows(workflowDir: string): Record<string, string> {
const actionVersions: Record<string, string> = {};
const generatedFiles = fs
.readdirSync(workflowDir)
.filter((f) => f.startsWith("__") && f.endsWith(".yml"))
.map((f) => path.join(workflowDir, f));
for (const filePath of generatedFiles) {
const content = fs.readFileSync(filePath, "utf8");
// Find all action uses in the file, including potential comments
// This pattern captures: action_name@version_with_possible_comment
const pattern = /uses:\s+([^/\s]+\/[^@\s]+)@([^@\n]+)/g;
let match: RegExpExecArray | null;
while ((match = pattern.exec(content)) !== null) {
const actionName = match[1];
const versionWithComment = match[2].trimEnd();
// Only track non-local actions (those with / but not starting with ./)
if (!actionName.startsWith("./")) {
// Assume that version numbers are consistent (this should be the case on a Dependabot update PR)
actionVersions[actionName] = versionWithComment;
}
}
}
return actionVersions;
}
/**
* Update hardcoded action versions in pr-checks/sync.ts
*
* @param syncTsPath - Path to sync.ts file
* @param actionVersions - Map of action names to versions (may include comments)
* @returns True if the file was modified, false otherwise
*/
export function updateSyncTs(
syncTsPath: string,
actionVersions: Record<string, string>,
): boolean {
if (!fs.existsSync(syncTsPath)) {
throw new Error(`Could not find ${syncTsPath}`);
}
let content = fs.readFileSync(syncTsPath, "utf8");
const originalContent = content;
// Update hardcoded action versions
for (const [actionName, versionWithComment] of Object.entries(
actionVersions,
)) {
// Extract just the version part (before any comment) for sync.ts
const version = versionWithComment.includes("#")
? versionWithComment.split("#")[0].trim()
: versionWithComment.trim();
// Look for patterns like uses: "actions/setup-node@v4"
// Note that this will break if we store an Action uses reference in a
// variable - that's a risk we're happy to take since in that case the
// PR checks will just fail.
const escaped = actionName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
const pattern = new RegExp(
`(uses:\\s*")${escaped}@(?:[^"]+)(")`,
"g",
);
content = content.replace(pattern, `$1${actionName}@${version}$2`);
}
if (content !== originalContent) {
fs.writeFileSync(syncTsPath, content, "utf8");
console.info(`Updated ${syncTsPath}`);
return true;
} else {
console.info(`No changes needed in ${syncTsPath}`);
return false;
}
}
/**
* Update action versions in template files in pr-checks/checks/
*
* @param checksDir - Path to pr-checks/checks directory
* @param actionVersions - Map of action names to versions (may include comments)
* @returns List of files that were modified
*/
export function updateTemplateFiles(
checksDir: string,
actionVersions: Record<string, string>,
): string[] {
const modifiedFiles: string[] = [];
const templateFiles = fs
.readdirSync(checksDir)
.filter((f) => f.endsWith(".yml"))
.map((f) => path.join(checksDir, f));
for (const filePath of templateFiles) {
let content = fs.readFileSync(filePath, "utf8");
const originalContent = content;
// Update action versions
for (const [actionName, versionWithComment] of Object.entries(
actionVersions,
)) {
// Look for patterns like 'uses: actions/setup-node@v4' or 'uses: actions/setup-node@sha # comment'
const escaped = actionName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
const pattern = new RegExp(
`(uses:\\s+${escaped})@(?:[^@\n]+)`,
"g",
);
content = content.replace(pattern, `$1@${versionWithComment}`);
}
if (content !== originalContent) {
fs.writeFileSync(filePath, content, "utf8");
modifiedFiles.push(filePath);
console.info(`Updated ${filePath}`);
}
}
return modifiedFiles;
}
function main(): number {
const { values } = parseArgs({
options: {
verbose: {
type: "boolean",
short: "v",
default: false,
},
},
strict: true,
});
const verbose = values.verbose ?? false;
console.info("Scanning generated workflows for latest action versions...");
const actionVersions = scanGeneratedWorkflows(WORKFLOW_DIR);
if (verbose) {
console.info("Found action versions:");
for (const [action, version] of Object.entries(actionVersions)) {
console.info(` ${action}@${version}`);
}
}
if (Object.keys(actionVersions).length === 0) {
console.error("No action versions found in generated workflows");
return 1;
}
// Update files
console.info("\nUpdating source files...");
const modifiedFiles: string[] = [];
// Update sync.ts
if (updateSyncTs(SYNC_TS_PATH, actionVersions)) {
modifiedFiles.push(SYNC_TS_PATH);
}
// Update template files
const templateModified = updateTemplateFiles(CHECKS_DIR, actionVersions);
modifiedFiles.push(...templateModified);
if (modifiedFiles.length > 0) {
console.info(`\nSync completed. Modified ${modifiedFiles.length} files:`);
for (const filePath of modifiedFiles) {
console.info(` ${filePath}`);
}
} else {
console.info(
"\nNo files needed updating - all action versions are already in sync",
);
}
return 0;
}
// Only call `main` if this script was run directly.
if (require.main === module) {
process.exit(main());
}
-237
View File
@@ -1,237 +0,0 @@
#!/usr/bin/env python3
"""
Tests for the sync_back.py script
"""
import os
import shutil
import tempfile
import unittest
import sync_back
class TestSyncBack(unittest.TestCase):
def setUp(self):
"""Set up temporary directories and files for testing"""
self.test_dir = tempfile.mkdtemp()
self.workflow_dir = os.path.join(self.test_dir, ".github", "workflows")
self.checks_dir = os.path.join(self.test_dir, "pr-checks", "checks")
os.makedirs(self.workflow_dir)
os.makedirs(self.checks_dir)
# Create sync.py file
self.sync_py_path = os.path.join(self.test_dir, "pr-checks", "sync.py")
def tearDown(self):
"""Clean up temporary directories"""
shutil.rmtree(self.test_dir)
def test_scan_generated_workflows_basic(self):
"""Test basic workflow scanning functionality"""
# Create a test generated workflow file
workflow_content = """
name: Test Workflow
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v5
- uses: actions/setup-go@v6
"""
with open(os.path.join(self.workflow_dir, "__test.yml"), 'w') as f:
f.write(workflow_content)
result = sync_back.scan_generated_workflows(self.workflow_dir)
self.assertEqual(result['actions/checkout'], 'v4')
self.assertEqual(result['actions/setup-node'], 'v5')
self.assertEqual(result['actions/setup-go'], 'v6')
def test_scan_generated_workflows_with_comments(self):
"""Test scanning workflows with version comments"""
workflow_content = """
name: Test Workflow
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ruby/setup-ruby@44511735964dcb71245e7e55f72539531f7bc0eb # v1.257.0
- uses: actions/setup-python@v6 # Latest Python
"""
with open(os.path.join(self.workflow_dir, "__test.yml"), 'w') as f:
f.write(workflow_content)
result = sync_back.scan_generated_workflows(self.workflow_dir)
self.assertEqual(result['actions/checkout'], 'v4')
self.assertEqual(result['ruby/setup-ruby'], '44511735964dcb71245e7e55f72539531f7bc0eb # v1.257.0')
self.assertEqual(result['actions/setup-python'], 'v6 # Latest Python')
def test_scan_generated_workflows_ignores_local_actions(self):
"""Test that local actions (starting with ./) are ignored"""
workflow_content = """
name: Test Workflow
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/local-action
- uses: ./another-local-action@v1
"""
with open(os.path.join(self.workflow_dir, "__test.yml"), 'w') as f:
f.write(workflow_content)
result = sync_back.scan_generated_workflows(self.workflow_dir)
self.assertEqual(result['actions/checkout'], 'v4')
self.assertNotIn('./.github/actions/local-action', result)
self.assertNotIn('./another-local-action', result)
def test_update_sync_py(self):
"""Test updating sync.py file"""
sync_py_content = """
steps = [
{
'uses': 'actions/setup-node@v4',
'with': {'node-version': '16'}
},
{
'uses': 'actions/setup-go@v5',
'with': {'go-version': '1.19'}
}
]
"""
with open(self.sync_py_path, 'w') as f:
f.write(sync_py_content)
action_versions = {
'actions/setup-node': 'v5',
'actions/setup-go': 'v6'
}
result = sync_back.update_sync_py(self.sync_py_path, action_versions)
self.assertTrue(result)
with open(self.sync_py_path, 'r') as f:
updated_content = f.read()
self.assertIn("'uses': 'actions/setup-node@v5'", updated_content)
self.assertIn("'uses': 'actions/setup-go@v6'", updated_content)
def test_update_sync_py_with_comments(self):
"""Test updating sync.py file when versions have comments"""
sync_py_content = """
steps = [
{
'uses': 'actions/setup-node@v4',
'with': {'node-version': '16'}
}
]
"""
with open(self.sync_py_path, 'w') as f:
f.write(sync_py_content)
action_versions = {
'actions/setup-node': 'v5 # Latest version'
}
result = sync_back.update_sync_py(self.sync_py_path, action_versions)
self.assertTrue(result)
with open(self.sync_py_path, 'r') as f:
updated_content = f.read()
# sync.py should get the version without comment
self.assertIn("'uses': 'actions/setup-node@v5'", updated_content)
self.assertNotIn("# Latest version", updated_content)
def test_update_template_files(self):
"""Test updating template files"""
template_content = """
name: Test Template
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v4
with:
node-version: 16
"""
template_path = os.path.join(self.checks_dir, "test.yml")
with open(template_path, 'w') as f:
f.write(template_content)
action_versions = {
'actions/checkout': 'v4',
'actions/setup-node': 'v5 # Latest'
}
result = sync_back.update_template_files(self.checks_dir, action_versions)
self.assertEqual(len(result), 1)
self.assertIn(template_path, result)
with open(template_path, 'r') as f:
updated_content = f.read()
self.assertIn("uses: actions/checkout@v4", updated_content)
self.assertIn("uses: actions/setup-node@v5 # Latest", updated_content)
def test_update_template_files_preserves_comments(self):
"""Test that updating template files preserves version comments"""
template_content = """
name: Test Template
steps:
- uses: ruby/setup-ruby@44511735964dcb71245e7e55f72539531f7bc0eb # v1.256.0
"""
template_path = os.path.join(self.checks_dir, "test.yml")
with open(template_path, 'w') as f:
f.write(template_content)
action_versions = {
'ruby/setup-ruby': '55511735964dcb71245e7e55f72539531f7bc0eb # v1.257.0'
}
result = sync_back.update_template_files(self.checks_dir, action_versions)
self.assertEqual(len(result), 1)
with open(template_path, 'r') as f:
updated_content = f.read()
self.assertIn("uses: ruby/setup-ruby@55511735964dcb71245e7e55f72539531f7bc0eb # v1.257.0", updated_content)
def test_no_changes_needed(self):
"""Test that functions return False/empty when no changes are needed"""
# Test sync.py with no changes needed
sync_py_content = """
steps = [
{
'uses': 'actions/setup-node@v5',
'with': {'node-version': '16'}
}
]
"""
with open(self.sync_py_path, 'w') as f:
f.write(sync_py_content)
action_versions = {
'actions/setup-node': 'v5'
}
result = sync_back.update_sync_py(self.sync_py_path, action_versions)
self.assertFalse(result)
if __name__ == '__main__':
unittest.main()
+136 -208
View File
@@ -40,6 +40,8 @@ import {
withTmpDir,
BuildMode,
DiskUsage,
Success,
Failure,
} from "./util";
import * as util from "./util";
@@ -942,55 +944,46 @@ for (const { displayName, language, feature } of [
feature: Feature.DisableCsharpBuildless,
},
]) {
test.serial(
`Build mode not overridden when disable ${displayName} buildless feature flag disabled`,
async (t) => {
const messages: LoggedMessage[] = [];
const buildMode = await configUtils.parseBuildModeInput(
"none",
[language],
createFeatures([]),
getRecordingLogger(messages),
);
t.is(buildMode, BuildMode.None);
t.deepEqual(messages, []);
},
);
test(`Build mode not overridden when disable ${displayName} buildless feature flag disabled`, async (t) => {
const messages: LoggedMessage[] = [];
const buildMode = await configUtils.parseBuildModeInput(
"none",
[language],
createFeatures([]),
getRecordingLogger(messages),
);
t.is(buildMode, BuildMode.None);
t.deepEqual(messages, []);
});
test.serial(
`Build mode not overridden for other languages when disable ${displayName} buildless feature flag enabled`,
async (t) => {
const messages: LoggedMessage[] = [];
const buildMode = await configUtils.parseBuildModeInput(
"none",
[KnownLanguage.python],
createFeatures([feature]),
getRecordingLogger(messages),
);
t.is(buildMode, BuildMode.None);
t.deepEqual(messages, []);
},
);
test(`Build mode not overridden for other languages when disable ${displayName} buildless feature flag enabled`, async (t) => {
const messages: LoggedMessage[] = [];
const buildMode = await configUtils.parseBuildModeInput(
"none",
[KnownLanguage.python],
createFeatures([feature]),
getRecordingLogger(messages),
);
t.is(buildMode, BuildMode.None);
t.deepEqual(messages, []);
});
test.serial(
`Build mode overridden when analyzing ${displayName} and disable ${displayName} buildless feature flag enabled`,
async (t) => {
const messages: LoggedMessage[] = [];
const buildMode = await configUtils.parseBuildModeInput(
"none",
[language],
createFeatures([feature]),
getRecordingLogger(messages),
);
t.is(buildMode, BuildMode.Autobuild);
t.deepEqual(messages, [
{
message: `Scanning ${displayName} code without a build is temporarily unavailable. Falling back to 'autobuild' build mode.`,
type: "warning",
},
]);
},
);
test(`Build mode overridden when analyzing ${displayName} and disable ${displayName} buildless feature flag enabled`, async (t) => {
const messages: LoggedMessage[] = [];
const buildMode = await configUtils.parseBuildModeInput(
"none",
[language],
createFeatures([feature]),
getRecordingLogger(messages),
);
t.is(buildMode, BuildMode.Autobuild);
t.deepEqual(messages, [
{
message: `Scanning ${displayName} code without a build is temporarily unavailable. Falling back to 'autobuild' build mode.`,
type: "warning",
},
]);
});
}
interface OverlayDatabaseModeTestSetup {
@@ -1033,16 +1026,19 @@ const defaultOverlayDatabaseModeTestSetup: OverlayDatabaseModeTestSetup = {
repositoryProperties: {},
};
const getOverlayDatabaseModeMacro = test.macro({
const checkOverlayEnablementMacro = test.macro({
exec: async (
t: ExecutionContext,
_title: string,
setupOverrides: Partial<OverlayDatabaseModeTestSetup>,
expected: {
overlayDatabaseMode: OverlayDatabaseMode;
useOverlayDatabaseCaching: boolean;
disabledReason?: OverlayDisabledReason;
},
expected:
| {
overlayDatabaseMode: OverlayDatabaseMode;
useOverlayDatabaseCaching: boolean;
}
| {
disabledReason: OverlayDisabledReason;
},
) => {
return await withTmpDir(async (tempDir) => {
const messages: LoggedMessage[] = [];
@@ -1100,7 +1096,7 @@ const getOverlayDatabaseModeMacro = test.macro({
.stub(gitUtils, "isAnalyzingDefaultBranch")
.resolves(setup.isDefaultBranch);
const result = await configUtils.getOverlayDatabaseMode(
const result = await configUtils.checkOverlayEnablement(
codeql,
features,
setup.languages,
@@ -1113,22 +1109,22 @@ const getOverlayDatabaseModeMacro = test.macro({
logger,
);
if (!("disabledReason" in expected)) {
expected.disabledReason = undefined;
if ("disabledReason" in expected) {
t.deepEqual(result, new Failure(expected.disabledReason));
} else {
t.deepEqual(result, new Success(expected));
}
t.deepEqual(result, expected);
} finally {
// Restore the original environment
process.env = originalEnv;
}
});
},
title: (_, title) => `getOverlayDatabaseMode: ${title}`,
title: (_, title) => `checkOverlayEnablement: ${title}`,
});
test.serial(
getOverlayDatabaseModeMacro,
checkOverlayEnablementMacro,
"Environment variable override - Overlay",
{
overlayDatabaseEnvVar: "overlay",
@@ -1140,7 +1136,7 @@ test.serial(
);
test.serial(
getOverlayDatabaseModeMacro,
checkOverlayEnablementMacro,
"Environment variable override - OverlayBase",
{
overlayDatabaseEnvVar: "overlay-base",
@@ -1152,45 +1148,41 @@ test.serial(
);
test.serial(
getOverlayDatabaseModeMacro,
checkOverlayEnablementMacro,
"Environment variable override - None",
{
overlayDatabaseEnvVar: "none",
},
{
overlayDatabaseMode: OverlayDatabaseMode.None,
useOverlayDatabaseCaching: false,
disabledReason: OverlayDisabledReason.DisabledByEnvironmentVariable,
},
);
test.serial(
getOverlayDatabaseModeMacro,
checkOverlayEnablementMacro,
"Ignore invalid environment variable",
{
overlayDatabaseEnvVar: "invalid-mode",
},
{
overlayDatabaseMode: OverlayDatabaseMode.None,
useOverlayDatabaseCaching: false,
disabledReason: OverlayDisabledReason.FeatureNotEnabled,
disabledReason: OverlayDisabledReason.OverallFeatureNotEnabled,
},
);
test.serial(
getOverlayDatabaseModeMacro,
checkOverlayEnablementMacro,
"Ignore feature flag when analyzing non-default branch",
{
languages: [KnownLanguage.javascript],
features: [Feature.OverlayAnalysis, Feature.OverlayAnalysisJavascript],
},
{
overlayDatabaseMode: OverlayDatabaseMode.None,
useOverlayDatabaseCaching: false,
disabledReason: OverlayDisabledReason.NotPullRequestOrDefaultBranch,
},
);
test.serial(
getOverlayDatabaseModeMacro,
checkOverlayEnablementMacro,
"Overlay-base database on default branch when feature enabled",
{
languages: [KnownLanguage.javascript],
@@ -1204,7 +1196,7 @@ test.serial(
);
test.serial(
getOverlayDatabaseModeMacro,
checkOverlayEnablementMacro,
"Overlay-base database on default branch when feature enabled with custom analysis",
{
languages: [KnownLanguage.javascript],
@@ -1221,7 +1213,7 @@ test.serial(
);
test.serial(
getOverlayDatabaseModeMacro,
checkOverlayEnablementMacro,
"Overlay-base database on default branch when code-scanning feature enabled",
{
languages: [KnownLanguage.javascript],
@@ -1238,7 +1230,7 @@ test.serial(
);
test.serial(
getOverlayDatabaseModeMacro,
checkOverlayEnablementMacro,
"No overlay-base database on default branch if runner disk space is too low",
{
languages: [KnownLanguage.javascript],
@@ -1253,14 +1245,12 @@ test.serial(
},
},
{
overlayDatabaseMode: OverlayDatabaseMode.None,
useOverlayDatabaseCaching: false,
disabledReason: OverlayDisabledReason.InsufficientResources,
disabledReason: OverlayDisabledReason.InsufficientDiskSpace,
},
);
test.serial(
getOverlayDatabaseModeMacro,
checkOverlayEnablementMacro,
"No overlay-base database on default branch if we can't determine runner disk space",
{
languages: [KnownLanguage.javascript],
@@ -1272,14 +1262,12 @@ test.serial(
diskUsage: undefined,
},
{
overlayDatabaseMode: OverlayDatabaseMode.None,
useOverlayDatabaseCaching: false,
disabledReason: OverlayDisabledReason.InsufficientResources,
disabledReason: OverlayDisabledReason.UnableToDetermineDiskUsage,
},
);
test.serial(
getOverlayDatabaseModeMacro,
checkOverlayEnablementMacro,
"Overlay-base database on default branch if runner disk space is too low and skip resource checks flag is enabled",
{
languages: [KnownLanguage.javascript],
@@ -1301,7 +1289,7 @@ test.serial(
);
test.serial(
getOverlayDatabaseModeMacro,
checkOverlayEnablementMacro,
"No overlay-base database on default branch if runner disk space is below v2 limit and v2 resource checks enabled",
{
languages: [KnownLanguage.javascript],
@@ -1317,14 +1305,12 @@ test.serial(
},
},
{
overlayDatabaseMode: OverlayDatabaseMode.None,
useOverlayDatabaseCaching: false,
disabledReason: OverlayDisabledReason.InsufficientResources,
disabledReason: OverlayDisabledReason.InsufficientDiskSpace,
},
);
test.serial(
getOverlayDatabaseModeMacro,
checkOverlayEnablementMacro,
"Overlay-base database on default branch if runner disk space is between v2 and v1 limits and v2 resource checks enabled",
{
languages: [KnownLanguage.javascript],
@@ -1346,7 +1332,7 @@ test.serial(
);
test.serial(
getOverlayDatabaseModeMacro,
checkOverlayEnablementMacro,
"No overlay-base database on default branch if runner disk space is between v2 and v1 limits and v2 resource checks not enabled",
{
languages: [KnownLanguage.javascript],
@@ -1361,14 +1347,12 @@ test.serial(
},
},
{
overlayDatabaseMode: OverlayDatabaseMode.None,
useOverlayDatabaseCaching: false,
disabledReason: OverlayDisabledReason.InsufficientResources,
disabledReason: OverlayDisabledReason.InsufficientDiskSpace,
},
);
test.serial(
getOverlayDatabaseModeMacro,
checkOverlayEnablementMacro,
"No overlay-base database on default branch if memory flag is too low",
{
languages: [KnownLanguage.javascript],
@@ -1380,14 +1364,12 @@ test.serial(
memoryFlagValue: 3072,
},
{
overlayDatabaseMode: OverlayDatabaseMode.None,
useOverlayDatabaseCaching: false,
disabledReason: OverlayDisabledReason.InsufficientResources,
disabledReason: OverlayDisabledReason.InsufficientMemory,
},
);
test.serial(
getOverlayDatabaseModeMacro,
checkOverlayEnablementMacro,
"Overlay-base database on default branch if memory flag is too low but CodeQL >= 2.24.3",
{
languages: [KnownLanguage.javascript],
@@ -1406,7 +1388,7 @@ test.serial(
);
test.serial(
getOverlayDatabaseModeMacro,
checkOverlayEnablementMacro,
"Overlay-base database on default branch if memory flag is too low and skip resource checks flag is enabled",
{
languages: [KnownLanguage.javascript],
@@ -1425,7 +1407,7 @@ test.serial(
);
test.serial(
getOverlayDatabaseModeMacro,
checkOverlayEnablementMacro,
"No overlay-base database on default branch when cached status indicates previous failure",
{
languages: [KnownLanguage.javascript],
@@ -1438,14 +1420,12 @@ test.serial(
shouldSkipOverlayAnalysisDueToCachedStatus: true,
},
{
overlayDatabaseMode: OverlayDatabaseMode.None,
useOverlayDatabaseCaching: false,
disabledReason: OverlayDisabledReason.SkippedDueToCachedStatus,
},
);
test.serial(
getOverlayDatabaseModeMacro,
checkOverlayEnablementMacro,
"No overlay analysis on PR when cached status indicates previous failure",
{
languages: [KnownLanguage.javascript],
@@ -1458,14 +1438,12 @@ test.serial(
shouldSkipOverlayAnalysisDueToCachedStatus: true,
},
{
overlayDatabaseMode: OverlayDatabaseMode.None,
useOverlayDatabaseCaching: false,
disabledReason: OverlayDisabledReason.SkippedDueToCachedStatus,
},
);
test.serial(
getOverlayDatabaseModeMacro,
checkOverlayEnablementMacro,
"No overlay-base database on default branch when code-scanning feature enabled with disable-default-queries",
{
languages: [KnownLanguage.javascript],
@@ -1479,14 +1457,12 @@ test.serial(
isDefaultBranch: true,
},
{
overlayDatabaseMode: OverlayDatabaseMode.None,
useOverlayDatabaseCaching: false,
disabledReason: OverlayDisabledReason.FeatureNotEnabled,
disabledReason: OverlayDisabledReason.NonDefaultQueries,
},
);
test.serial(
getOverlayDatabaseModeMacro,
checkOverlayEnablementMacro,
"No overlay-base database on default branch when code-scanning feature enabled with packs",
{
languages: [KnownLanguage.javascript],
@@ -1500,14 +1476,12 @@ test.serial(
isDefaultBranch: true,
},
{
overlayDatabaseMode: OverlayDatabaseMode.None,
useOverlayDatabaseCaching: false,
disabledReason: OverlayDisabledReason.FeatureNotEnabled,
disabledReason: OverlayDisabledReason.NonDefaultQueries,
},
);
test.serial(
getOverlayDatabaseModeMacro,
checkOverlayEnablementMacro,
"No overlay-base database on default branch when code-scanning feature enabled with queries",
{
languages: [KnownLanguage.javascript],
@@ -1521,14 +1495,12 @@ test.serial(
isDefaultBranch: true,
},
{
overlayDatabaseMode: OverlayDatabaseMode.None,
useOverlayDatabaseCaching: false,
disabledReason: OverlayDisabledReason.FeatureNotEnabled,
disabledReason: OverlayDisabledReason.NonDefaultQueries,
},
);
test.serial(
getOverlayDatabaseModeMacro,
checkOverlayEnablementMacro,
"No overlay-base database on default branch when code-scanning feature enabled with query-filters",
{
languages: [KnownLanguage.javascript],
@@ -1542,14 +1514,12 @@ test.serial(
isDefaultBranch: true,
},
{
overlayDatabaseMode: OverlayDatabaseMode.None,
useOverlayDatabaseCaching: false,
disabledReason: OverlayDisabledReason.FeatureNotEnabled,
disabledReason: OverlayDisabledReason.NonDefaultQueries,
},
);
test.serial(
getOverlayDatabaseModeMacro,
checkOverlayEnablementMacro,
"No overlay-base database on default branch when only language-specific feature enabled",
{
languages: [KnownLanguage.javascript],
@@ -1557,14 +1527,12 @@ test.serial(
isDefaultBranch: true,
},
{
overlayDatabaseMode: OverlayDatabaseMode.None,
useOverlayDatabaseCaching: false,
disabledReason: OverlayDisabledReason.FeatureNotEnabled,
disabledReason: OverlayDisabledReason.OverallFeatureNotEnabled,
},
);
test.serial(
getOverlayDatabaseModeMacro,
checkOverlayEnablementMacro,
"No overlay-base database on default branch when only code-scanning feature enabled",
{
languages: [KnownLanguage.javascript],
@@ -1572,14 +1540,12 @@ test.serial(
isDefaultBranch: true,
},
{
overlayDatabaseMode: OverlayDatabaseMode.None,
useOverlayDatabaseCaching: false,
disabledReason: OverlayDisabledReason.FeatureNotEnabled,
disabledReason: OverlayDisabledReason.OverallFeatureNotEnabled,
},
);
test.serial(
getOverlayDatabaseModeMacro,
checkOverlayEnablementMacro,
"No overlay-base database on default branch when language-specific feature disabled",
{
languages: [KnownLanguage.javascript],
@@ -1587,14 +1553,12 @@ test.serial(
isDefaultBranch: true,
},
{
overlayDatabaseMode: OverlayDatabaseMode.None,
useOverlayDatabaseCaching: false,
disabledReason: OverlayDisabledReason.FeatureNotEnabled,
disabledReason: OverlayDisabledReason.LanguageNotEnabled,
},
);
test.serial(
getOverlayDatabaseModeMacro,
checkOverlayEnablementMacro,
"Overlay analysis on PR when feature enabled",
{
languages: [KnownLanguage.javascript],
@@ -1608,7 +1572,7 @@ test.serial(
);
test.serial(
getOverlayDatabaseModeMacro,
checkOverlayEnablementMacro,
"Overlay analysis on PR when feature enabled with custom analysis",
{
languages: [KnownLanguage.javascript],
@@ -1625,7 +1589,7 @@ test.serial(
);
test.serial(
getOverlayDatabaseModeMacro,
checkOverlayEnablementMacro,
"Overlay analysis on PR when code-scanning feature enabled",
{
languages: [KnownLanguage.javascript],
@@ -1642,7 +1606,7 @@ test.serial(
);
test.serial(
getOverlayDatabaseModeMacro,
checkOverlayEnablementMacro,
"No overlay analysis on PR if runner disk space is too low",
{
languages: [KnownLanguage.javascript],
@@ -1657,14 +1621,12 @@ test.serial(
},
},
{
overlayDatabaseMode: OverlayDatabaseMode.None,
useOverlayDatabaseCaching: false,
disabledReason: OverlayDisabledReason.InsufficientResources,
disabledReason: OverlayDisabledReason.InsufficientDiskSpace,
},
);
test.serial(
getOverlayDatabaseModeMacro,
checkOverlayEnablementMacro,
"Overlay analysis on PR if runner disk space is too low and skip resource checks flag is enabled",
{
languages: [KnownLanguage.javascript],
@@ -1686,7 +1648,7 @@ test.serial(
);
test.serial(
getOverlayDatabaseModeMacro,
checkOverlayEnablementMacro,
"No overlay analysis on PR if we can't determine runner disk space",
{
languages: [KnownLanguage.javascript],
@@ -1698,14 +1660,12 @@ test.serial(
diskUsage: undefined,
},
{
overlayDatabaseMode: OverlayDatabaseMode.None,
useOverlayDatabaseCaching: false,
disabledReason: OverlayDisabledReason.InsufficientResources,
disabledReason: OverlayDisabledReason.UnableToDetermineDiskUsage,
},
);
test.serial(
getOverlayDatabaseModeMacro,
checkOverlayEnablementMacro,
"No overlay analysis on PR if memory flag is too low",
{
languages: [KnownLanguage.javascript],
@@ -1717,14 +1677,12 @@ test.serial(
memoryFlagValue: 3072,
},
{
overlayDatabaseMode: OverlayDatabaseMode.None,
useOverlayDatabaseCaching: false,
disabledReason: OverlayDisabledReason.InsufficientResources,
disabledReason: OverlayDisabledReason.InsufficientMemory,
},
);
test.serial(
getOverlayDatabaseModeMacro,
checkOverlayEnablementMacro,
"Overlay analysis on PR if memory flag is too low but CodeQL >= 2.24.3",
{
languages: [KnownLanguage.javascript],
@@ -1743,7 +1701,7 @@ test.serial(
);
test.serial(
getOverlayDatabaseModeMacro,
checkOverlayEnablementMacro,
"Overlay analysis on PR if memory flag is too low and skip resource checks flag is enabled",
{
languages: [KnownLanguage.javascript],
@@ -1762,7 +1720,7 @@ test.serial(
);
test.serial(
getOverlayDatabaseModeMacro,
checkOverlayEnablementMacro,
"No overlay analysis on PR when code-scanning feature enabled with disable-default-queries",
{
languages: [KnownLanguage.javascript],
@@ -1776,14 +1734,12 @@ test.serial(
isPullRequest: true,
},
{
overlayDatabaseMode: OverlayDatabaseMode.None,
useOverlayDatabaseCaching: false,
disabledReason: OverlayDisabledReason.FeatureNotEnabled,
disabledReason: OverlayDisabledReason.NonDefaultQueries,
},
);
test.serial(
getOverlayDatabaseModeMacro,
checkOverlayEnablementMacro,
"No overlay analysis on PR when code-scanning feature enabled with packs",
{
languages: [KnownLanguage.javascript],
@@ -1797,14 +1753,12 @@ test.serial(
isPullRequest: true,
},
{
overlayDatabaseMode: OverlayDatabaseMode.None,
useOverlayDatabaseCaching: false,
disabledReason: OverlayDisabledReason.FeatureNotEnabled,
disabledReason: OverlayDisabledReason.NonDefaultQueries,
},
);
test.serial(
getOverlayDatabaseModeMacro,
checkOverlayEnablementMacro,
"No overlay analysis on PR when code-scanning feature enabled with queries",
{
languages: [KnownLanguage.javascript],
@@ -1818,14 +1772,12 @@ test.serial(
isPullRequest: true,
},
{
overlayDatabaseMode: OverlayDatabaseMode.None,
useOverlayDatabaseCaching: false,
disabledReason: OverlayDisabledReason.FeatureNotEnabled,
disabledReason: OverlayDisabledReason.NonDefaultQueries,
},
);
test.serial(
getOverlayDatabaseModeMacro,
checkOverlayEnablementMacro,
"No overlay analysis on PR when code-scanning feature enabled with query-filters",
{
languages: [KnownLanguage.javascript],
@@ -1839,14 +1791,12 @@ test.serial(
isPullRequest: true,
},
{
overlayDatabaseMode: OverlayDatabaseMode.None,
useOverlayDatabaseCaching: false,
disabledReason: OverlayDisabledReason.FeatureNotEnabled,
disabledReason: OverlayDisabledReason.NonDefaultQueries,
},
);
test.serial(
getOverlayDatabaseModeMacro,
checkOverlayEnablementMacro,
"No overlay analysis on PR when only language-specific feature enabled",
{
languages: [KnownLanguage.javascript],
@@ -1854,14 +1804,12 @@ test.serial(
isPullRequest: true,
},
{
overlayDatabaseMode: OverlayDatabaseMode.None,
useOverlayDatabaseCaching: false,
disabledReason: OverlayDisabledReason.FeatureNotEnabled,
disabledReason: OverlayDisabledReason.OverallFeatureNotEnabled,
},
);
test.serial(
getOverlayDatabaseModeMacro,
checkOverlayEnablementMacro,
"No overlay analysis on PR when only code-scanning feature enabled",
{
languages: [KnownLanguage.javascript],
@@ -1869,14 +1817,12 @@ test.serial(
isPullRequest: true,
},
{
overlayDatabaseMode: OverlayDatabaseMode.None,
useOverlayDatabaseCaching: false,
disabledReason: OverlayDisabledReason.FeatureNotEnabled,
disabledReason: OverlayDisabledReason.OverallFeatureNotEnabled,
},
);
test.serial(
getOverlayDatabaseModeMacro,
checkOverlayEnablementMacro,
"No overlay analysis on PR when language-specific feature disabled",
{
languages: [KnownLanguage.javascript],
@@ -1884,14 +1830,12 @@ test.serial(
isPullRequest: true,
},
{
overlayDatabaseMode: OverlayDatabaseMode.None,
useOverlayDatabaseCaching: false,
disabledReason: OverlayDisabledReason.FeatureNotEnabled,
disabledReason: OverlayDisabledReason.LanguageNotEnabled,
},
);
test.serial(
getOverlayDatabaseModeMacro,
checkOverlayEnablementMacro,
"Overlay PR analysis by env",
{
overlayDatabaseEnvVar: "overlay",
@@ -1903,7 +1847,7 @@ test.serial(
);
test.serial(
getOverlayDatabaseModeMacro,
checkOverlayEnablementMacro,
"Overlay PR analysis by env on a runner with low disk space",
{
overlayDatabaseEnvVar: "overlay",
@@ -1916,7 +1860,7 @@ test.serial(
);
test.serial(
getOverlayDatabaseModeMacro,
checkOverlayEnablementMacro,
"Overlay PR analysis by feature flag",
{
languages: [KnownLanguage.javascript],
@@ -1930,7 +1874,7 @@ test.serial(
);
test.serial(
getOverlayDatabaseModeMacro,
checkOverlayEnablementMacro,
"Fallback due to autobuild with traced language",
{
overlayDatabaseEnvVar: "overlay",
@@ -1938,14 +1882,12 @@ test.serial(
languages: [KnownLanguage.java],
},
{
overlayDatabaseMode: OverlayDatabaseMode.None,
useOverlayDatabaseCaching: false,
disabledReason: OverlayDisabledReason.IncompatibleBuildMode,
},
);
test.serial(
getOverlayDatabaseModeMacro,
checkOverlayEnablementMacro,
"Fallback due to no build mode with traced language",
{
overlayDatabaseEnvVar: "overlay",
@@ -1953,70 +1895,60 @@ test.serial(
languages: [KnownLanguage.java],
},
{
overlayDatabaseMode: OverlayDatabaseMode.None,
useOverlayDatabaseCaching: false,
disabledReason: OverlayDisabledReason.IncompatibleBuildMode,
},
);
test.serial(
getOverlayDatabaseModeMacro,
checkOverlayEnablementMacro,
"Fallback due to old CodeQL version",
{
overlayDatabaseEnvVar: "overlay",
codeqlVersion: "2.14.0",
},
{
overlayDatabaseMode: OverlayDatabaseMode.None,
useOverlayDatabaseCaching: false,
disabledReason: OverlayDisabledReason.IncompatibleCodeQl,
},
);
test.serial(
getOverlayDatabaseModeMacro,
checkOverlayEnablementMacro,
"Fallback due to missing git root",
{
overlayDatabaseEnvVar: "overlay",
gitRoot: undefined,
},
{
overlayDatabaseMode: OverlayDatabaseMode.None,
useOverlayDatabaseCaching: false,
disabledReason: OverlayDisabledReason.NoGitRoot,
},
);
test.serial(
getOverlayDatabaseModeMacro,
checkOverlayEnablementMacro,
"Fallback due to old git version",
{
overlayDatabaseEnvVar: "overlay",
gitVersion: new GitVersionInfo("2.30.0", "2.30.0"), // Version below required 2.38.0
},
{
overlayDatabaseMode: OverlayDatabaseMode.None,
useOverlayDatabaseCaching: false,
disabledReason: OverlayDisabledReason.IncompatibleGit,
},
);
test.serial(
getOverlayDatabaseModeMacro,
checkOverlayEnablementMacro,
"Fallback when git version cannot be determined",
{
overlayDatabaseEnvVar: "overlay",
gitVersion: undefined,
},
{
overlayDatabaseMode: OverlayDatabaseMode.None,
useOverlayDatabaseCaching: false,
disabledReason: OverlayDisabledReason.IncompatibleGit,
},
);
test.serial(
getOverlayDatabaseModeMacro,
checkOverlayEnablementMacro,
"No overlay when disabled via repository property",
{
languages: [KnownLanguage.javascript],
@@ -2027,14 +1959,12 @@ test.serial(
},
},
{
overlayDatabaseMode: OverlayDatabaseMode.None,
useOverlayDatabaseCaching: false,
disabledReason: OverlayDisabledReason.DisabledByRepositoryProperty,
},
);
test.serial(
getOverlayDatabaseModeMacro,
checkOverlayEnablementMacro,
"Overlay not disabled when repository property is false",
{
languages: [KnownLanguage.javascript],
@@ -2051,7 +1981,7 @@ test.serial(
);
test.serial(
getOverlayDatabaseModeMacro,
checkOverlayEnablementMacro,
"Environment variable override takes precedence over repository property",
{
overlayDatabaseEnvVar: "overlay",
@@ -2068,7 +1998,7 @@ test.serial(
// Exercise language-specific overlay analysis features code paths
for (const language in KnownLanguage) {
test.serial(
getOverlayDatabaseModeMacro,
checkOverlayEnablementMacro,
`Check default overlay analysis feature for ${language}`,
{
languages: [language],
@@ -2076,9 +2006,7 @@ for (const language in KnownLanguage) {
isPullRequest: true,
},
{
overlayDatabaseMode: OverlayDatabaseMode.None,
useOverlayDatabaseCaching: false,
disabledReason: OverlayDisabledReason.FeatureNotEnabled,
disabledReason: OverlayDisabledReason.LanguageNotEnabled,
},
);
}
+175 -140
View File
@@ -69,6 +69,9 @@ import {
isInTestMode,
joinAtMost,
DiskUsage,
Result,
Success,
Failure,
} from "./util";
/**
@@ -653,14 +656,18 @@ const OVERLAY_ANALYSIS_CODE_SCANNING_FEATURES: Record<Language, Feature> = {
swift: Feature.OverlayAnalysisCodeScanningSwift,
};
async function isOverlayAnalysisFeatureEnabled(
/**
* Checks whether the overlay analysis feature is enabled for the given
* languages and configuration.
*/
async function checkOverlayAnalysisFeatureEnabled(
features: FeatureEnablement,
codeql: CodeQL,
languages: Language[],
codeScanningConfig: UserConfig,
): Promise<boolean> {
): Promise<Result<void, OverlayDisabledReason>> {
if (!(await features.getValue(Feature.OverlayAnalysis, codeql))) {
return false;
return new Failure(OverlayDisabledReason.OverallFeatureNotEnabled);
}
let enableForCodeScanningOnly = false;
for (const language of languages) {
@@ -677,39 +684,35 @@ async function isOverlayAnalysisFeatureEnabled(
enableForCodeScanningOnly = true;
continue;
}
return false;
return new Failure(OverlayDisabledReason.LanguageNotEnabled);
}
if (enableForCodeScanningOnly) {
// A code-scanning configuration runs only the (default) code-scanning suite
// if the default queries are not disabled, and no packs, queries, or
// query-filters are specified.
return (
const usesDefaultQueriesOnly =
codeScanningConfig["disable-default-queries"] !== true &&
codeScanningConfig.packs === undefined &&
codeScanningConfig.queries === undefined &&
codeScanningConfig["query-filters"] === undefined
);
codeScanningConfig["query-filters"] === undefined;
if (!usesDefaultQueriesOnly) {
return new Failure(OverlayDisabledReason.NonDefaultQueries);
}
}
return true;
return new Success(undefined);
}
/** Checks if the runner has enough disk space for overlay analysis. */
function runnerHasSufficientDiskSpace(
diskUsage: DiskUsage | undefined,
diskUsage: DiskUsage,
logger: Logger,
useV2ResourceChecks: boolean,
): boolean {
const minimumDiskSpaceBytes = useV2ResourceChecks
? OVERLAY_MINIMUM_AVAILABLE_DISK_SPACE_V2_BYTES
: OVERLAY_MINIMUM_AVAILABLE_DISK_SPACE_BYTES;
if (
diskUsage === undefined ||
diskUsage.numAvailableBytes < minimumDiskSpaceBytes
) {
const diskSpaceMb =
diskUsage === undefined
? 0
: Math.round(diskUsage.numAvailableBytes / 1_000_000);
if (diskUsage.numAvailableBytes < minimumDiskSpaceBytes) {
const diskSpaceMb = Math.round(diskUsage.numAvailableBytes / 1_000_000);
const minimumDiskSpaceMb = Math.round(minimumDiskSpaceBytes / 1_000_000);
logger.info(
`Setting overlay database mode to ${OverlayDatabaseMode.None} ` +
@@ -754,23 +757,28 @@ async function runnerHasSufficientMemory(
}
/**
* Checks if the runner supports overlay analysis based on available disk space
* and the maximum memory CodeQL will be allowed to use.
* Checks if the runner has sufficient disk space and memory for overlay
* analysis.
*/
async function runnerSupportsOverlayAnalysis(
async function checkRunnerResources(
codeql: CodeQL,
diskUsage: DiskUsage | undefined,
diskUsage: DiskUsage,
ramInput: string | undefined,
logger: Logger,
useV2ResourceChecks: boolean,
): Promise<boolean> {
): Promise<Result<void, OverlayDisabledReason>> {
if (!runnerHasSufficientDiskSpace(diskUsage, logger, useV2ResourceChecks)) {
return false;
return new Failure(OverlayDisabledReason.InsufficientDiskSpace);
}
if (!(await runnerHasSufficientMemory(codeql, ramInput, logger))) {
return false;
return new Failure(OverlayDisabledReason.InsufficientMemory);
}
return true;
return new Success(undefined);
}
interface EnabledOverlayConfig {
overlayDatabaseMode: Exclude<OverlayDatabaseMode, OverlayDatabaseMode.None>;
useOverlayDatabaseCaching: boolean;
}
/**
@@ -791,10 +799,11 @@ async function runnerSupportsOverlayAnalysis(
* For `Overlay` and `OverlayBase`, the function performs further checks and
* reverts to `None` if any check should fail.
*
* @returns An object containing the overlay database mode and whether the
* action should perform overlay-base database caching.
* @returns A `Success` containing the overlay database mode and whether the
* action should perform overlay-base database caching, or a `Failure`
* containing the reason why overlay analysis is disabled.
*/
export async function getOverlayDatabaseMode(
export async function checkOverlayEnablement(
codeql: CodeQL,
features: FeatureEnablement,
languages: Language[],
@@ -805,15 +814,7 @@ export async function getOverlayDatabaseMode(
repositoryProperties: RepositoryProperties,
gitVersion: GitVersionInfo | undefined,
logger: Logger,
): Promise<{
overlayDatabaseMode: OverlayDatabaseMode;
useOverlayDatabaseCaching: boolean;
disabledReason: OverlayDisabledReason | undefined;
}> {
let overlayDatabaseMode = OverlayDatabaseMode.None;
let useOverlayDatabaseCaching = false;
let disabledReason: OverlayDisabledReason | undefined;
): Promise<Result<EnabledOverlayConfig, OverlayDisabledReason>> {
const modeEnv = process.env.CODEQL_OVERLAY_DATABASE_MODE;
// Any unrecognized CODEQL_OVERLAY_DATABASE_MODE value will be ignored and
// treated as if the environment variable was not set.
@@ -822,101 +823,132 @@ export async function getOverlayDatabaseMode(
modeEnv === OverlayDatabaseMode.OverlayBase ||
modeEnv === OverlayDatabaseMode.None
) {
overlayDatabaseMode = modeEnv;
logger.info(
`Setting overlay database mode to ${overlayDatabaseMode} ` +
`Setting overlay database mode to ${modeEnv} ` +
"from the CODEQL_OVERLAY_DATABASE_MODE environment variable.",
);
} else if (
repositoryProperties[RepositoryPropertyName.DISABLE_OVERLAY] === true
) {
if (modeEnv === OverlayDatabaseMode.None) {
return new Failure(OverlayDisabledReason.DisabledByEnvironmentVariable);
}
return validateOverlayDatabaseMode(
modeEnv,
false,
codeql,
languages,
sourceRoot,
buildMode,
gitVersion,
logger,
);
}
if (repositoryProperties[RepositoryPropertyName.DISABLE_OVERLAY] === true) {
logger.info(
`Setting overlay database mode to ${OverlayDatabaseMode.None} ` +
`because the ${RepositoryPropertyName.DISABLE_OVERLAY} repository property is set to true.`,
);
overlayDatabaseMode = OverlayDatabaseMode.None;
disabledReason = OverlayDisabledReason.DisabledByRepositoryProperty;
} else if (
await isOverlayAnalysisFeatureEnabled(
features,
codeql,
languages,
codeScanningConfig,
)
return new Failure(OverlayDisabledReason.DisabledByRepositoryProperty);
}
const featureResult = await checkOverlayAnalysisFeatureEnabled(
features,
codeql,
languages,
codeScanningConfig,
);
if (featureResult.isFailure()) {
return featureResult;
}
const performResourceChecks = !(await features.getValue(
Feature.OverlayAnalysisSkipResourceChecks,
codeql,
));
const useV2ResourceChecks = await features.getValue(
Feature.OverlayAnalysisResourceChecksV2,
);
const checkOverlayStatus = await features.getValue(
Feature.OverlayAnalysisStatusCheck,
);
const needDiskUsage = performResourceChecks || checkOverlayStatus;
const diskUsage = needDiskUsage ? await checkDiskUsage(logger) : undefined;
if (needDiskUsage && diskUsage === undefined) {
logger.warning(
`Unable to determine disk usage, therefore setting overlay database mode to ${OverlayDatabaseMode.None}.`,
);
return new Failure(OverlayDisabledReason.UnableToDetermineDiskUsage);
}
const resourceResult =
performResourceChecks && diskUsage !== undefined
? await checkRunnerResources(
codeql,
diskUsage,
ramInput,
logger,
useV2ResourceChecks,
)
: new Success<void>(undefined);
if (resourceResult.isFailure()) {
return resourceResult;
}
if (
checkOverlayStatus &&
diskUsage !== undefined &&
(await shouldSkipOverlayAnalysis(codeql, languages, diskUsage, logger))
) {
const performResourceChecks = !(await features.getValue(
Feature.OverlayAnalysisSkipResourceChecks,
codeql,
));
const useV2ResourceChecks = await features.getValue(
Feature.OverlayAnalysisResourceChecksV2,
logger.info(
`Setting overlay database mode to ${OverlayDatabaseMode.None} ` +
"because overlay analysis previously failed with this combination of languages, " +
"disk space, and CodeQL version.",
);
const checkOverlayStatus = await features.getValue(
Feature.OverlayAnalysisStatusCheck,
return new Failure(OverlayDisabledReason.SkippedDueToCachedStatus);
}
let overlayDatabaseMode: OverlayDatabaseMode;
if (isAnalyzingPullRequest()) {
overlayDatabaseMode = OverlayDatabaseMode.Overlay;
logger.info(
`Setting overlay database mode to ${overlayDatabaseMode} ` +
"with caching because we are analyzing a pull request.",
);
} else if (await isAnalyzingDefaultBranch()) {
overlayDatabaseMode = OverlayDatabaseMode.OverlayBase;
logger.info(
`Setting overlay database mode to ${overlayDatabaseMode} ` +
"with caching because we are analyzing the default branch.",
);
const diskUsage =
performResourceChecks || checkOverlayStatus
? await checkDiskUsage(logger)
: undefined;
if (
performResourceChecks &&
!(await runnerSupportsOverlayAnalysis(
codeql,
diskUsage,
ramInput,
logger,
useV2ResourceChecks,
))
) {
overlayDatabaseMode = OverlayDatabaseMode.None;
disabledReason = OverlayDisabledReason.InsufficientResources;
} else if (checkOverlayStatus && diskUsage === undefined) {
logger.warning(
`Unable to determine disk usage, therefore setting overlay database mode to ${OverlayDatabaseMode.None}.`,
);
overlayDatabaseMode = OverlayDatabaseMode.None;
disabledReason = OverlayDisabledReason.UnableToDetermineDiskUsage;
} else if (
checkOverlayStatus &&
diskUsage &&
(await shouldSkipOverlayAnalysis(codeql, languages, diskUsage, logger))
) {
logger.info(
`Setting overlay database mode to ${OverlayDatabaseMode.None} ` +
"because overlay analysis previously failed with this combination of languages, " +
"disk space, and CodeQL version.",
);
overlayDatabaseMode = OverlayDatabaseMode.None;
disabledReason = OverlayDisabledReason.SkippedDueToCachedStatus;
} else if (isAnalyzingPullRequest()) {
overlayDatabaseMode = OverlayDatabaseMode.Overlay;
useOverlayDatabaseCaching = true;
logger.info(
`Setting overlay database mode to ${overlayDatabaseMode} ` +
"with caching because we are analyzing a pull request.",
);
} else if (await isAnalyzingDefaultBranch()) {
overlayDatabaseMode = OverlayDatabaseMode.OverlayBase;
useOverlayDatabaseCaching = true;
logger.info(
`Setting overlay database mode to ${overlayDatabaseMode} ` +
"with caching because we are analyzing the default branch.",
);
}
} else {
disabledReason = OverlayDisabledReason.FeatureNotEnabled;
return new Failure(OverlayDisabledReason.NotPullRequestOrDefaultBranch);
}
const disabledResult = (reason: OverlayDisabledReason | undefined) => ({
overlayDatabaseMode: OverlayDatabaseMode.None,
useOverlayDatabaseCaching: false,
disabledReason: reason,
});
if (overlayDatabaseMode === OverlayDatabaseMode.None) {
return disabledResult(disabledReason);
}
return validateOverlayDatabaseMode(
overlayDatabaseMode,
true,
codeql,
languages,
sourceRoot,
buildMode,
gitVersion,
logger,
);
}
/**
* Validates that the given overlay database mode is compatible with the current
* configuration (build mode, CodeQL version, git repository, git version). Returns
* the mode unchanged if all checks pass, or falls back to `None` with the
* appropriate disabled reason.
*/
async function validateOverlayDatabaseMode(
overlayDatabaseMode: Exclude<OverlayDatabaseMode, OverlayDatabaseMode.None>,
useOverlayDatabaseCaching: boolean,
codeql: CodeQL,
languages: Language[],
sourceRoot: string,
buildMode: BuildMode | undefined,
gitVersion: GitVersionInfo | undefined,
logger: Logger,
): Promise<Result<EnabledOverlayConfig, OverlayDisabledReason>> {
if (
buildMode !== BuildMode.None &&
(
@@ -937,7 +969,7 @@ export async function getOverlayDatabaseMode(
`build-mode is set to "${buildMode}" instead of "none". ` +
"Falling back to creating a normal full database instead.",
);
return disabledResult(OverlayDisabledReason.IncompatibleBuildMode);
return new Failure(OverlayDisabledReason.IncompatibleBuildMode);
}
if (!(await codeQlVersionAtLeast(codeql, CODEQL_OVERLAY_MINIMUM_VERSION))) {
logger.warning(
@@ -945,7 +977,7 @@ export async function getOverlayDatabaseMode(
`the CodeQL CLI is older than ${CODEQL_OVERLAY_MINIMUM_VERSION}. ` +
"Falling back to creating a normal full database instead.",
);
return disabledResult(OverlayDisabledReason.IncompatibleCodeQl);
return new Failure(OverlayDisabledReason.IncompatibleCodeQl);
}
if ((await getGitRoot(sourceRoot)) === undefined) {
logger.warning(
@@ -953,7 +985,7 @@ export async function getOverlayDatabaseMode(
`the source root "${sourceRoot}" is not inside a git repository. ` +
"Falling back to creating a normal full database instead.",
);
return disabledResult(OverlayDisabledReason.NoGitRoot);
return new Failure(OverlayDisabledReason.NoGitRoot);
}
if (gitVersion === undefined) {
logger.warning(
@@ -961,7 +993,7 @@ export async function getOverlayDatabaseMode(
"the Git version could not be determined. " +
"Falling back to creating a normal full database instead.",
);
return disabledResult(OverlayDisabledReason.IncompatibleGit);
return new Failure(OverlayDisabledReason.IncompatibleGit);
}
if (!gitVersion.isAtLeast(GIT_MINIMUM_VERSION_FOR_OVERLAY)) {
logger.warning(
@@ -969,14 +1001,13 @@ export async function getOverlayDatabaseMode(
`the installed Git version is older than ${GIT_MINIMUM_VERSION_FOR_OVERLAY}. ` +
"Falling back to creating a normal full database instead.",
);
return disabledResult(OverlayDisabledReason.IncompatibleGit);
return new Failure(OverlayDisabledReason.IncompatibleGit);
}
return {
return new Success({
overlayDatabaseMode,
useOverlayDatabaseCaching,
disabledReason,
};
});
}
function dbLocationOrDefault(
@@ -1122,11 +1153,7 @@ export async function initConfig(
// and queries, which in turn depends on the user config and the augmentation
// properties. So we need to calculate the overlay database mode after the
// rest of the config has been populated.
const {
overlayDatabaseMode,
useOverlayDatabaseCaching,
disabledReason: overlayDisabledReason,
} = await getOverlayDatabaseMode(
const overlayDatabaseModeResult = await checkOverlayEnablement(
inputs.codeql,
inputs.features,
config.languages,
@@ -1138,14 +1165,22 @@ export async function initConfig(
gitVersion,
logger,
);
logger.info(
`Using overlay database mode: ${overlayDatabaseMode} ` +
`${useOverlayDatabaseCaching ? "with" : "without"} caching.`,
);
config.overlayDatabaseMode = overlayDatabaseMode;
config.useOverlayDatabaseCaching = useOverlayDatabaseCaching;
if (overlayDisabledReason !== undefined) {
if (overlayDatabaseModeResult.isSuccess()) {
const { overlayDatabaseMode, useOverlayDatabaseCaching } =
overlayDatabaseModeResult.value;
logger.info(
`Using overlay database mode: ${overlayDatabaseMode} ` +
`${useOverlayDatabaseCaching ? "with" : "without"} caching.`,
);
config.overlayDatabaseMode = overlayDatabaseMode;
config.useOverlayDatabaseCaching = useOverlayDatabaseCaching;
} else {
const overlayDisabledReason = overlayDatabaseModeResult.value;
logger.info(
`Using overlay database mode: ${OverlayDatabaseMode.None} without caching.`,
);
config.overlayDatabaseMode = OverlayDatabaseMode.None;
config.useOverlayDatabaseCaching = false;
await addOverlayDisablementDiagnostics(
config,
inputs.codeql,
@@ -1154,7 +1189,7 @@ export async function initConfig(
}
if (
overlayDatabaseMode === OverlayDatabaseMode.Overlay ||
config.overlayDatabaseMode === OverlayDatabaseMode.Overlay ||
(await shouldPerformDiffInformedAnalysis(
inputs.codeql,
inputs.features,
+19 -4
View File
@@ -10,20 +10,35 @@ import { RepositoryPropertyName } from "../feature-flags/properties";
/** Reason why overlay analysis was disabled. */
export enum OverlayDisabledReason {
/** Overlay analysis was disabled by the CODEQL_OVERLAY_DATABASE_MODE environment variable being set to "none". */
DisabledByEnvironmentVariable = "disabled-by-environment-variable",
/** Overlay analysis was disabled by a repository property. */
DisabledByRepositoryProperty = "disabled-by-repository-property",
/** Overlay analysis feature was not enabled. */
FeatureNotEnabled = "feature-not-enabled",
/** The build mode is incompatible with overlay analysis. */
IncompatibleBuildMode = "incompatible-build-mode",
/** The CodeQL CLI version is too old to support overlay analysis. */
IncompatibleCodeQl = "incompatible-codeql",
/** The Git version could not be determined or is too old. */
IncompatibleGit = "incompatible-git",
/** The runner does not have enough disk space or memory. */
InsufficientResources = "insufficient-resources",
/** The runner does not have enough disk space to perform overlay analysis. */
InsufficientDiskSpace = "insufficient-disk-space",
/** The runner does not have enough memory to perform overlay analysis. */
InsufficientMemory = "insufficient-memory",
/** Overlay analysis is not enabled for one or more of the configured languages. */
LanguageNotEnabled = "language-not-enabled",
/** The source root is not inside a git repository. */
NoGitRoot = "no-git-root",
/**
* For one or more of the configured languages, overlay analysis is only
* enabled when using the default query suite, but the config customises the
* queries by disabling default queries, specifying custom queries or packs,
* or adding query filters.
*/
NonDefaultQueries = "non-default-queries",
/** We are not analyzing a pull request or the default branch. */
NotPullRequestOrDefaultBranch = "not-pull-request-or-default-branch",
/** The top-level overlay analysis feature flag is not enabled. */
OverallFeatureNotEnabled = "overall-feature-not-enabled",
/** Overlay analysis was skipped because it previously failed with similar hardware resources. */
SkippedDueToCachedStatus = "skipped-due-to-cached-status",
/** Disk usage could not be determined during the overlay status check. */