mirror of
https://github.com/github/codeql-action
synced 2026-11-03 00:01:56 +03:00
Compare commits
48
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a048e8cfae | ||
|
|
09dec6cc8c | ||
|
|
ba61470b11 | ||
|
|
31d88dd791 | ||
|
|
2f90a82ab7 | ||
|
|
ee70cd5210 | ||
|
|
516447a110 | ||
|
|
839edd2b07 | ||
|
|
b3b14d6671 | ||
|
|
711c19c2dc | ||
|
|
4138e5a7fe | ||
|
|
0bae987e7d | ||
|
|
3b64eeeb9f | ||
|
|
49e524587f | ||
|
|
18e8398a57 | ||
|
|
80de837984 | ||
|
|
03dfa6fb02 | ||
|
|
3187067e93 | ||
|
|
52077a03d9 | ||
|
|
0337b99474 | ||
|
|
b2cc23e25d | ||
|
|
82036913c8 | ||
|
|
ae7c3ea645 | ||
|
|
a0276b7dc4 | ||
|
|
f57d3726c3 | ||
|
|
6f850b5022 | ||
|
|
c73f0fa0f1 | ||
|
|
98dfd09d4a | ||
|
|
c8f0aa4c67 | ||
|
|
f3f1edbedf | ||
|
|
9f2d709a33 | ||
|
|
5f5ed8735c | ||
|
|
7ba697a3ec | ||
|
|
74374a3893 | ||
|
|
3e85884434 | ||
|
|
5aed9f7d64 | ||
|
|
f9feddd874 | ||
|
|
15f19e1870 | ||
|
|
51cc08af6f | ||
|
|
71b697dd8b | ||
|
|
f5808271b0 | ||
|
|
da26a016ee | ||
|
|
4536424fcf | ||
|
|
d98bedfdea | ||
|
|
952a538c24 | ||
|
|
04d4fd51e9 | ||
|
|
c05837d3e8 | ||
|
|
3dc99aad86 |
No files matched your search
@@ -68,7 +68,7 @@ jobs:
|
||||
- name: Check out repository
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
- name: Install Java
|
||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||
uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5.3.0
|
||||
with:
|
||||
java-version: ${{ inputs.java-version || '17' }}
|
||||
distribution: temurin
|
||||
|
||||
+1
-1
@@ -68,7 +68,7 @@ jobs:
|
||||
- name: Check out repository
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
- name: Install Java
|
||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||
uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5.3.0
|
||||
with:
|
||||
java-version: ${{ inputs.java-version || '17' }}
|
||||
distribution: temurin
|
||||
|
||||
@@ -129,10 +129,6 @@ jobs:
|
||||
with:
|
||||
python-version: '3.13'
|
||||
|
||||
- name: Use Xcode 16
|
||||
if: runner.os == 'macOS' && matrix.version != 'nightly-latest'
|
||||
run: sudo xcode-select -s "/Applications/Xcode_16.app"
|
||||
|
||||
- uses: ./../action/init
|
||||
id: init
|
||||
with:
|
||||
|
||||
+1
-1
@@ -59,7 +59,7 @@ jobs:
|
||||
use-all-platform-bundle: 'false'
|
||||
setup-kotlin: 'true'
|
||||
- name: Set up Ruby
|
||||
uses: ruby/setup-ruby@afeafc3d1ab54a631816aba4c914a0081c12ff2f # v1.310.0
|
||||
uses: ruby/setup-ruby@12fd324f1d0b43274fdc8130f6980590a667c455 # v1.312.0
|
||||
with:
|
||||
ruby-version: 2.6
|
||||
- name: Install Code Scanning integration
|
||||
|
||||
-3
@@ -91,9 +91,6 @@ jobs:
|
||||
version: ${{ matrix.version }}
|
||||
use-all-platform-bundle: 'false'
|
||||
setup-kotlin: 'true'
|
||||
- name: Use Xcode 16
|
||||
if: runner.os == 'macOS' && matrix.version != 'nightly-latest'
|
||||
run: sudo xcode-select -s "/Applications/Xcode_16.app"
|
||||
- uses: ./../action/init
|
||||
id: init
|
||||
with:
|
||||
|
||||
@@ -9,7 +9,7 @@ on:
|
||||
- main
|
||||
paths:
|
||||
- .github/workflows/update-supported-enterprise-server-versions.yml
|
||||
- .github/workflows/update-supported-enterprise-server-versions/update.py
|
||||
- pr-checks/update-ghes-versions.ts
|
||||
|
||||
jobs:
|
||||
update-supported-enterprise-server-versions:
|
||||
@@ -26,8 +26,19 @@ jobs:
|
||||
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
||||
with:
|
||||
python-version: "3.13"
|
||||
|
||||
- name: Checkout CodeQL Action
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version: 24
|
||||
cache: 'npm'
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Checkout Enterprise Releases
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
@@ -35,18 +46,18 @@ jobs:
|
||||
token: ${{ secrets.ENTERPRISE_RELEASE_TOKEN }}
|
||||
path: ${{ github.workspace }}/enterprise-releases/
|
||||
sparse-checkout: releases.json
|
||||
|
||||
- name: Update Supported Enterprise Server Versions
|
||||
working-directory: pr-checks
|
||||
run: |
|
||||
cd ./.github/workflows/update-supported-enterprise-server-versions/
|
||||
python3 -m pip install pipenv
|
||||
pipenv install
|
||||
pipenv run ./update.py
|
||||
npx tsx update-ghes-versions.ts
|
||||
rm --recursive "$ENTERPRISE_RELEASES_PATH"
|
||||
npm ci
|
||||
npm run build
|
||||
env:
|
||||
ENTERPRISE_RELEASES_PATH: ${{ github.workspace }}/enterprise-releases/
|
||||
|
||||
- name: Rebuild
|
||||
run: npm run build
|
||||
|
||||
- name: Update git config
|
||||
run: |
|
||||
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
[[source]]
|
||||
name = "pypi"
|
||||
url = "https://pypi.org/simple"
|
||||
verify_ssl = true
|
||||
|
||||
[dev-packages]
|
||||
|
||||
[packages]
|
||||
semver = "*"
|
||||
@@ -1,27 +0,0 @@
|
||||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "e3ba923dcb4888e05de5448c18a732bf40197e80fabfa051a61c01b22c504879"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {},
|
||||
"sources": [
|
||||
{
|
||||
"name": "pypi",
|
||||
"url": "https://pypi.org/simple",
|
||||
"verify_ssl": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"default": {
|
||||
"semver": {
|
||||
"hashes": [
|
||||
"sha256:ced8b23dceb22134307c1b8abfa523da14198793d9787ac838e70e29e77458d4",
|
||||
"sha256:fa0fe2722ee1c3f57eac478820c3a5ae2f624af8264cbdf9000c980ff7f75e3f"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.13.0"
|
||||
}
|
||||
},
|
||||
"develop": {}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
import datetime
|
||||
import json
|
||||
import os
|
||||
import pathlib
|
||||
|
||||
import semver
|
||||
|
||||
_API_COMPATIBILITY_PATH = pathlib.Path(__file__).absolute().parents[3] / "src" / "api-compatibility.json"
|
||||
_ENTERPRISE_RELEASES_PATH = pathlib.Path(os.environ["ENTERPRISE_RELEASES_PATH"])
|
||||
_RELEASE_FILE_PATH = _ENTERPRISE_RELEASES_PATH / "releases.json"
|
||||
_FIRST_SUPPORTED_RELEASE = semver.VersionInfo.parse("2.22.0") # Versions older than this did not include Code Scanning.
|
||||
|
||||
def main():
|
||||
api_compatibility_data = json.loads(_API_COMPATIBILITY_PATH.read_text())
|
||||
|
||||
releases = json.loads(_RELEASE_FILE_PATH.read_text())
|
||||
|
||||
# Remove GHES version using a previous version numbering scheme.
|
||||
if "11.10" in releases:
|
||||
del releases["11.10"]
|
||||
|
||||
oldest_supported_release = None
|
||||
newest_supported_release = semver.VersionInfo.parse(api_compatibility_data["maximumVersion"] + ".0")
|
||||
|
||||
for release_version_string, release_data in releases.items():
|
||||
release_version = semver.VersionInfo.parse(release_version_string + ".0")
|
||||
if release_version < _FIRST_SUPPORTED_RELEASE:
|
||||
continue
|
||||
|
||||
if release_version > newest_supported_release:
|
||||
feature_freeze_date = datetime.date.fromisoformat(release_data["feature_freeze"])
|
||||
if feature_freeze_date < datetime.date.today() + datetime.timedelta(weeks=2):
|
||||
newest_supported_release = release_version
|
||||
|
||||
if oldest_supported_release is None or release_version < oldest_supported_release:
|
||||
end_of_life_date = datetime.date.fromisoformat(release_data["end"])
|
||||
# The GHES version is not actually end of life until the end of the day specified by
|
||||
# `end_of_life_date`. Wait an extra week to be safe.
|
||||
is_end_of_life = datetime.date.today() > end_of_life_date + datetime.timedelta(weeks=1)
|
||||
if not is_end_of_life:
|
||||
oldest_supported_release = release_version
|
||||
|
||||
api_compatibility_data = {
|
||||
"minimumVersion": f"{oldest_supported_release.major}.{oldest_supported_release.minor}",
|
||||
"maximumVersion": f"{newest_supported_release.major}.{newest_supported_release.minor}",
|
||||
}
|
||||
_API_COMPATIBILITY_PATH.write_text(json.dumps(api_compatibility_data, sort_keys=True) + "\n")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
+1
-1
@@ -4,7 +4,7 @@ See the [releases page](https://github.com/github/codeql-action/releases) for th
|
||||
|
||||
## [UNRELEASED]
|
||||
|
||||
- Organizations can create a custom repository property named `github-codeql-tools` to set a default CodeQL CLI tools value. You can optionally set `github-codeql-tools-mode` to control scope: use `enforce` (default) to apply to all workflows, or `dynamic` to apply only to dynamic workflows. If a workflow provides an explicit `tools:` input, that input takes precedence. For more information, see [Managing custom properties for repositories in your organization](https://docs.github.com/en/organizations/managing-organization-settings/managing-custom-properties-for-repositories-in-your-organization), [Repository properties for Code Scanning](https://docs.github.com/en/code-security/concepts/code-scanning/repository-properties) and [Customizing your advanced setup for code scanning](https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning).
|
||||
No user facing changes.
|
||||
|
||||
## 4.36.2 - 04 Jun 2026
|
||||
|
||||
|
||||
Generated
+12938
-8088
File diff suppressed because it is too large.
Load diff
Generated
+435
-254
File diff suppressed because it is too large.
Load diff
+8
-8
@@ -31,16 +31,16 @@
|
||||
"@actions/io": "^2.0.0",
|
||||
"@actions/tool-cache": "^3.0.1",
|
||||
"@octokit/plugin-retry": "^8.1.0",
|
||||
"archiver": "^7.0.1",
|
||||
"archiver": "^8.0.0",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"follow-redirects": "^1.16.0",
|
||||
"get-folder-size": "^5.0.0",
|
||||
"https-proxy-agent": "^7.0.6",
|
||||
"js-yaml": "^4.1.1",
|
||||
"js-yaml": "^5.0.0",
|
||||
"jsonschema": "1.5.0",
|
||||
"long": "^5.3.2",
|
||||
"node-forge": "^1.4.0",
|
||||
"semver": "^7.8.1",
|
||||
"semver": "^7.8.4",
|
||||
"uuid": "^14.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -48,18 +48,18 @@
|
||||
"@eslint/compat": "^2.1.0",
|
||||
"@microsoft/eslint-formatter-sarif": "^3.1.0",
|
||||
"@octokit/types": "^16.0.0",
|
||||
"@types/archiver": "^7.0.0",
|
||||
"@types/archiver": "^8.0.0",
|
||||
"@types/follow-redirects": "^1.14.4",
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
"@types/node": "^20.19.41",
|
||||
"@types/node": "^20.19.42",
|
||||
"@types/node-forge": "^1.3.14",
|
||||
"@types/sarif": "^2.1.7",
|
||||
"@types/semver": "^7.7.1",
|
||||
"@types/sinon": "^21.0.1",
|
||||
"ava": "^6.4.1",
|
||||
"esbuild": "^0.28.0",
|
||||
"esbuild": "^0.28.1",
|
||||
"eslint": "^9.39.4",
|
||||
"eslint-import-resolver-typescript": "^4.4.4",
|
||||
"eslint-import-resolver-typescript": "^4.4.5",
|
||||
"eslint-plugin-github": "^6.0.0",
|
||||
"eslint-plugin-import-x": "^4.16.2",
|
||||
"eslint-plugin-jsdoc": "^62.9.0",
|
||||
@@ -69,7 +69,7 @@
|
||||
"nock": "^14.0.15",
|
||||
"sinon": "^22.0.0",
|
||||
"typescript": "^6.0.3",
|
||||
"typescript-eslint": "^8.60.0"
|
||||
"typescript-eslint": "^8.61.0"
|
||||
},
|
||||
"overrides": {
|
||||
"@actions/tool-cache": {
|
||||
|
||||
@@ -17,10 +17,6 @@ steps:
|
||||
with:
|
||||
python-version: "3.13"
|
||||
|
||||
- name: Use Xcode 16
|
||||
if: runner.os == 'macOS' && matrix.version != 'nightly-latest'
|
||||
run: sudo xcode-select -s "/Applications/Xcode_16.app"
|
||||
|
||||
- uses: ./../action/init
|
||||
id: init
|
||||
with:
|
||||
|
||||
@@ -5,7 +5,7 @@ versions:
|
||||
- default
|
||||
steps:
|
||||
- name: Set up Ruby
|
||||
uses: ruby/setup-ruby@afeafc3d1ab54a631816aba4c914a0081c12ff2f # v1.310.0
|
||||
uses: ruby/setup-ruby@12fd324f1d0b43274fdc8130f6980590a667c455 # v1.312.0
|
||||
with:
|
||||
ruby-version: 2.6
|
||||
- name: Install Code Scanning integration
|
||||
|
||||
@@ -11,9 +11,6 @@ installDotNet: true
|
||||
env:
|
||||
DOTNET_GENERATE_ASPNET_CERTIFICATE: "false"
|
||||
steps:
|
||||
- name: Use Xcode 16
|
||||
if: runner.os == 'macOS' && matrix.version != 'nightly-latest'
|
||||
run: sudo xcode-select -s "/Applications/Xcode_16.app"
|
||||
- uses: ./../action/init
|
||||
id: init
|
||||
with:
|
||||
|
||||
@@ -24,3 +24,9 @@ export const BUILTIN_LANGUAGES_FILE = path.join(
|
||||
"languages",
|
||||
"builtin.json",
|
||||
);
|
||||
|
||||
/** Path to the api-compatibility.json file. */
|
||||
export const API_COMPATIBILITY_FILE = path.join(
|
||||
SOURCE_ROOT,
|
||||
"api-compatibility.json",
|
||||
);
|
||||
@@ -7,10 +7,11 @@
|
||||
"@octokit/core": "^7.0.6",
|
||||
"@octokit/plugin-paginate-rest": ">=9.2.2",
|
||||
"@octokit/plugin-rest-endpoint-methods": "^17.0.0",
|
||||
"semver": "^7.8.0",
|
||||
"yaml": "^2.9.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.19.41",
|
||||
"tsx": "^4.22.3"
|
||||
"@types/node": "^20.19.42",
|
||||
"tsx": "^4.22.4"
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -246,8 +246,8 @@ const languageSetups: LanguageSetups = {
|
||||
name: "Install Java",
|
||||
uses: pinnedUses(
|
||||
"actions/setup-java",
|
||||
"be666c2fcd27ec809703dec50e508c2fdc7f6654",
|
||||
"v5.2.0",
|
||||
"ad2b38190b15e4d6bdf0c97fb4fca8412226d287",
|
||||
"v5.3.0",
|
||||
),
|
||||
with: {
|
||||
"java-version": `\${{ inputs.java-version || '${defaultLanguageVersions.java}' }}`,
|
||||
|
||||
@@ -0,0 +1,204 @@
|
||||
#!/usr/bin/env npx tsx
|
||||
|
||||
/*
|
||||
* Tests for the update-ghes-versions.ts script
|
||||
*/
|
||||
|
||||
import * as assert from "node:assert/strict";
|
||||
import { describe, it } from "node:test";
|
||||
|
||||
import {
|
||||
addWeeks,
|
||||
determineSupportedRange,
|
||||
type EnterpriseReleases,
|
||||
parseEnterpriseVersion,
|
||||
printEnterpriseVersion,
|
||||
} from "./update-ghes-versions";
|
||||
|
||||
describe("parseEnterpriseVersion", async () => {
|
||||
await it("parses a two-component version string", () => {
|
||||
const ver = parseEnterpriseVersion("3.10");
|
||||
assert.notEqual(ver, null);
|
||||
assert.equal(ver!.major, 3);
|
||||
assert.equal(ver!.minor, 10);
|
||||
assert.equal(ver!.patch, 0);
|
||||
});
|
||||
|
||||
await it("parses a three-component version string", () => {
|
||||
const ver = parseEnterpriseVersion("3.10.2");
|
||||
assert.notEqual(ver, null);
|
||||
assert.equal(ver!.major, 3);
|
||||
assert.equal(ver!.minor, 10);
|
||||
assert.equal(ver!.patch, 2);
|
||||
});
|
||||
|
||||
await it("returns null for invalid input", () => {
|
||||
assert.equal(parseEnterpriseVersion("not-a-version"), null);
|
||||
});
|
||||
});
|
||||
|
||||
describe("printEnterpriseVersion", async () => {
|
||||
await it("prints only major.minor when patch is 0", () => {
|
||||
const ver = parseEnterpriseVersion("3.10")!;
|
||||
assert.equal(printEnterpriseVersion(ver), "3.10");
|
||||
});
|
||||
|
||||
await it("includes patch when non-zero", () => {
|
||||
const ver = parseEnterpriseVersion("3.10.2")!;
|
||||
assert.equal(printEnterpriseVersion(ver), "3.10.2");
|
||||
});
|
||||
});
|
||||
|
||||
describe("addWeeks", async () => {
|
||||
await it("adds weeks to a date", () => {
|
||||
const date = new Date("2025-01-01T00:00:00Z");
|
||||
const result = addWeeks(date, 2);
|
||||
assert.equal(result.toISOString(), "2025-01-15T00:00:00.000Z");
|
||||
});
|
||||
|
||||
await it("does not mutate the original date", () => {
|
||||
const date = new Date("2025-01-01T00:00:00Z");
|
||||
addWeeks(date, 2);
|
||||
assert.equal(date.toISOString(), "2025-01-01T00:00:00.000Z");
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Helper to build a release entry with a feature freeze and end-of-life date.
|
||||
* Dates are ISO date strings (e.g. "2025-06-01").
|
||||
*/
|
||||
function release(featureFreeze: string, end: string) {
|
||||
return { feature_freeze: featureFreeze, end };
|
||||
}
|
||||
|
||||
describe("determineSupportedRange", async () => {
|
||||
// A fixed "today" for deterministic tests.
|
||||
const today = new Date("2025-06-15");
|
||||
|
||||
const farPastEnd = "2020-01-01";
|
||||
const farFutureEnd = "2099-12-31";
|
||||
const farPastFreeze = "2020-01-01";
|
||||
const farFutureFreeze = "2099-12-31";
|
||||
|
||||
await it("returns the only supported release as both min and max", () => {
|
||||
const releases: EnterpriseReleases = {
|
||||
"3.10": release(farPastFreeze, farFutureEnd),
|
||||
};
|
||||
const result = determineSupportedRange(
|
||||
today,
|
||||
{ minimumVersion: "3.10", maximumVersion: "3.10" },
|
||||
releases,
|
||||
);
|
||||
assert.equal(result.minimumVersion, "3.10");
|
||||
assert.equal(result.maximumVersion, "3.10");
|
||||
});
|
||||
|
||||
await it("determines the range from multiple supported releases", () => {
|
||||
const releases: EnterpriseReleases = {
|
||||
"3.10": release(farPastFreeze, farFutureEnd),
|
||||
"3.11": release(farPastFreeze, farFutureEnd),
|
||||
"3.12": release(farPastFreeze, farFutureEnd),
|
||||
};
|
||||
const result = determineSupportedRange(
|
||||
today,
|
||||
{ minimumVersion: "3.10", maximumVersion: "3.12" },
|
||||
releases,
|
||||
);
|
||||
assert.equal(result.minimumVersion, "3.10");
|
||||
assert.equal(result.maximumVersion, "3.12");
|
||||
});
|
||||
|
||||
await it("drops an end-of-life release from the minimum", () => {
|
||||
const releases: EnterpriseReleases = {
|
||||
// 3.10 has been end of life for a long time.
|
||||
"3.10": release(farPastFreeze, farPastEnd),
|
||||
"3.11": release(farPastFreeze, farFutureEnd),
|
||||
"3.12": release(farPastFreeze, farFutureEnd),
|
||||
};
|
||||
const result = determineSupportedRange(
|
||||
today,
|
||||
{ minimumVersion: "3.10", maximumVersion: "3.12" },
|
||||
releases,
|
||||
);
|
||||
assert.equal(result.minimumVersion, "3.11");
|
||||
assert.equal(result.maximumVersion, "3.12");
|
||||
});
|
||||
|
||||
await it("bumps the maximum when a newer release's feature freeze has passed", () => {
|
||||
const releases: EnterpriseReleases = {
|
||||
"3.10": release(farPastFreeze, farFutureEnd),
|
||||
"3.11": release(farPastFreeze, farFutureEnd),
|
||||
// 3.12 has a feature freeze far in the past, so it should be picked up.
|
||||
"3.12": release(farPastFreeze, farFutureEnd),
|
||||
};
|
||||
const result = determineSupportedRange(
|
||||
today,
|
||||
// The stored maximum is 3.11, but 3.12 should be picked up.
|
||||
{ minimumVersion: "3.10", maximumVersion: "3.11" },
|
||||
releases,
|
||||
);
|
||||
assert.equal(result.minimumVersion, "3.10");
|
||||
assert.equal(result.maximumVersion, "3.12");
|
||||
});
|
||||
|
||||
await it("does not bump the maximum when feature freeze is far in the future", () => {
|
||||
const releases: EnterpriseReleases = {
|
||||
"3.10": release(farPastFreeze, farFutureEnd),
|
||||
"3.11": release(farPastFreeze, farFutureEnd),
|
||||
// 3.12 has a feature freeze far in the future, so it should NOT be picked up.
|
||||
"3.12": release(farFutureFreeze, farFutureEnd),
|
||||
};
|
||||
const result = determineSupportedRange(
|
||||
today,
|
||||
{ minimumVersion: "3.10", maximumVersion: "3.11" },
|
||||
releases,
|
||||
);
|
||||
assert.equal(result.minimumVersion, "3.10");
|
||||
assert.equal(result.maximumVersion, "3.11");
|
||||
});
|
||||
|
||||
await it("ignores releases older than the first supported release (2.22)", () => {
|
||||
const releases: EnterpriseReleases = {
|
||||
"2.21": release(farPastFreeze, farFutureEnd),
|
||||
"3.10": release(farPastFreeze, farFutureEnd),
|
||||
"3.11": release(farPastFreeze, farFutureEnd),
|
||||
};
|
||||
const result = determineSupportedRange(
|
||||
today,
|
||||
{ minimumVersion: "3.10", maximumVersion: "3.11" },
|
||||
releases,
|
||||
);
|
||||
// 2.21 is older than 2.22, so it should be ignored — 3.10 remains the minimum.
|
||||
assert.equal(result.minimumVersion, "3.10");
|
||||
assert.equal(result.maximumVersion, "3.11");
|
||||
});
|
||||
|
||||
await it("throws when no supported releases remain", () => {
|
||||
const releases: EnterpriseReleases = {
|
||||
// All releases are end of life.
|
||||
"3.10": release(farPastFreeze, farPastEnd),
|
||||
"3.11": release(farPastFreeze, farPastEnd),
|
||||
};
|
||||
assert.throws(
|
||||
() =>
|
||||
determineSupportedRange(
|
||||
today,
|
||||
{ minimumVersion: "3.10", maximumVersion: "3.11" },
|
||||
releases,
|
||||
),
|
||||
/Could not determine oldest supported release/,
|
||||
);
|
||||
});
|
||||
|
||||
await it("throws when maximumVersion is not a valid version", () => {
|
||||
assert.throws(
|
||||
() =>
|
||||
determineSupportedRange(
|
||||
today,
|
||||
{ minimumVersion: "3.10", maximumVersion: "invalid" },
|
||||
{},
|
||||
),
|
||||
/is not a valid semantic version/,
|
||||
);
|
||||
});
|
||||
});
|
||||
Executable
+243
@@ -0,0 +1,243 @@
|
||||
#!/usr/bin/env npx tsx
|
||||
|
||||
/**
|
||||
* Updates src/api-compatibility.json with the current range of supported
|
||||
* GitHub Enterprise Server versions by reading the releases.json file from
|
||||
* an `enterprise-releases` checkout.
|
||||
*/
|
||||
|
||||
import * as fs from "node:fs";
|
||||
import * as path from "node:path";
|
||||
|
||||
import { type SemVer } from "semver";
|
||||
import * as semver from "semver";
|
||||
|
||||
import * as json from "../src/json";
|
||||
|
||||
import { API_COMPATIBILITY_FILE } from "./config";
|
||||
|
||||
/** The first GHES version that included Code Scanning. */
|
||||
const FIRST_SUPPORTED_RELEASE: SemVer = new semver.SemVer("2.22.0");
|
||||
|
||||
/** Environment variables specific to this script. */
|
||||
export enum EnvVar {
|
||||
ENTERPRISE_RELEASES_PATH = "ENTERPRISE_RELEASES_PATH",
|
||||
}
|
||||
|
||||
/**
|
||||
* The semver specification requires three numeric components, but GHES release families
|
||||
* only have two. This function uses `semver.coerce` to first coerce the version string
|
||||
* into an acceptable input for `semver.parse`. E.g. `3.10` becomes `3.10.0`.
|
||||
*/
|
||||
export function parseEnterpriseVersion(val: string): SemVer | null {
|
||||
return semver.parse(semver.coerce(val));
|
||||
}
|
||||
|
||||
/**
|
||||
* Mirroring `parseEnterpriseVersion`, this function returns only the major and minor
|
||||
* version components from `ver`.
|
||||
*/
|
||||
export function printEnterpriseVersion(ver: SemVer) {
|
||||
if (ver.patch === 0) {
|
||||
return `${ver.major}.${ver.minor}`;
|
||||
}
|
||||
return ver.toString();
|
||||
}
|
||||
|
||||
/** The JSON schema for `API_COMPATIBILITY_FILE`. */
|
||||
const apiCompatibilitySchema = {
|
||||
minimumVersion: json.string,
|
||||
maximumVersion: json.string,
|
||||
} as const satisfies json.Schema;
|
||||
|
||||
/** The type representing the expected contents of `API_COMPATIBILITY_FILE`. */
|
||||
type ApiCompatibility = json.FromSchema<typeof apiCompatibilitySchema>;
|
||||
|
||||
/** Reads the current contents of the `API_COMPATIBILITY_FILE` file. */
|
||||
export function readApiCompatibility(): ApiCompatibility {
|
||||
const apiCompatibilityData: unknown = JSON.parse(
|
||||
fs.readFileSync(API_COMPATIBILITY_FILE, "utf8"),
|
||||
);
|
||||
|
||||
if (!json.isObject(apiCompatibilityData)) {
|
||||
throw new Error(
|
||||
`Expected '${API_COMPATIBILITY_FILE}' to contain an object.`,
|
||||
);
|
||||
}
|
||||
if (!json.validateSchema(apiCompatibilitySchema, apiCompatibilityData)) {
|
||||
throw new Error(
|
||||
`The contents of '${API_COMPATIBILITY_FILE}' do not match the expected JSON schema.`,
|
||||
);
|
||||
}
|
||||
|
||||
return apiCompatibilityData;
|
||||
}
|
||||
|
||||
/** The JSON schema for entries in the `releases.json` file. */
|
||||
const releaseDataSchema = {
|
||||
feature_freeze: json.string,
|
||||
end: json.string,
|
||||
} as const satisfies json.Schema;
|
||||
|
||||
/** The type representing entries in the `releases.json` file. */
|
||||
export type ReleaseData = json.FromSchema<typeof releaseDataSchema>;
|
||||
|
||||
/** A mapping from GHES releases to release information. */
|
||||
export type EnterpriseReleases = Record<string, ReleaseData>;
|
||||
|
||||
/** Reads information about GHES releases. */
|
||||
export function readEnterpriseReleases(
|
||||
enterpriseReleasesPath: string,
|
||||
): EnterpriseReleases {
|
||||
const releaseFilePath = path.join(enterpriseReleasesPath, "releases.json");
|
||||
const releases: unknown = JSON.parse(
|
||||
fs.readFileSync(releaseFilePath, "utf8"),
|
||||
);
|
||||
|
||||
if (!json.isObject(releases)) {
|
||||
throw new Error(`Expected '${releaseFilePath}' to contain an object.`);
|
||||
}
|
||||
|
||||
// Remove GHES version using a previous version numbering scheme.
|
||||
delete releases["11.10"];
|
||||
|
||||
// Validate that the object satisfies the schema.
|
||||
for (const [, releaseData] of Object.entries(releases)) {
|
||||
if (!json.isObject(releaseData)) {
|
||||
throw new Error(
|
||||
`Expected release data to be an object, but it is ${typeof releaseData}.`,
|
||||
);
|
||||
}
|
||||
if (!json.validateSchema(releaseDataSchema, releaseData)) {
|
||||
throw new Error("Expected release data to satisfy schema.");
|
||||
}
|
||||
}
|
||||
|
||||
return releases;
|
||||
}
|
||||
|
||||
/** Adds `weeks`-many weeks to the UTC date of `date`. */
|
||||
export function addWeeks(date: Date, weeks: number): Date {
|
||||
const result = new Date(date);
|
||||
result.setUTCDate(date.getUTCDate() + weeks * 7);
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Determines the current range of GHES versions we should support. */
|
||||
export function determineSupportedRange(
|
||||
today: Date,
|
||||
apiCompatibilityData: ApiCompatibility,
|
||||
releases: EnterpriseReleases,
|
||||
): ApiCompatibility {
|
||||
// We only care about the UTC date component.
|
||||
today.setUTCHours(0, 0, 0, 0);
|
||||
|
||||
// Our goal is to identify the oldest and newest GHES release we should support.
|
||||
// We begin with `oldestSupportRelease = undefined` so that we determine the
|
||||
// minimum from scratch and don't stick to `apiCompatibilityData.minimumVersion`
|
||||
// when it is no longer supported.
|
||||
// For `newestSupportedRelease`, we assume that `apiCompatibilityData.maximumVersion`
|
||||
// is guaranteed to not be outdated.
|
||||
let oldestSupportedRelease: SemVer | undefined;
|
||||
let newestSupportedRelease = parseEnterpriseVersion(
|
||||
apiCompatibilityData.maximumVersion,
|
||||
);
|
||||
|
||||
if (newestSupportedRelease === null) {
|
||||
throw new Error(
|
||||
`${apiCompatibilityData.maximumVersion} is not a valid semantic version.`,
|
||||
);
|
||||
}
|
||||
|
||||
// NOTE: We deliberately omit including any data from `releases` in the error messages below.
|
||||
|
||||
for (const [releaseVersionString, releaseData] of Object.entries(releases)) {
|
||||
const releaseVersion = parseEnterpriseVersion(releaseVersionString);
|
||||
|
||||
if (releaseVersion === null) {
|
||||
throw new Error("Invalid enterprise release version.");
|
||||
}
|
||||
|
||||
// Ignore GHES releases older than `FIRST_SUPPORTED_RELEASE`.
|
||||
if (semver.compare(releaseVersion, FIRST_SUPPORTED_RELEASE) < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Set `newestSupportedRelease` to a GHES release if it has a greater version
|
||||
// than the current `newestSupportedRelease` and the feature freeze has
|
||||
// already happened or will be in the next two weeks.
|
||||
if (semver.compare(releaseVersion, newestSupportedRelease) > 0) {
|
||||
const featureFreezeDate = new Date(releaseData.feature_freeze);
|
||||
if (featureFreezeDate < addWeeks(today, 2)) {
|
||||
newestSupportedRelease = releaseVersion;
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
oldestSupportedRelease === undefined ||
|
||||
semver.compare(releaseVersion, oldestSupportedRelease) < 0
|
||||
) {
|
||||
const endOfLifeDate = new Date(releaseData.end);
|
||||
// The GHES version is not actually end of life until the end of the day
|
||||
// specified by `endOfLifeDate`. Wait an extra week to be safe.
|
||||
const isEndOfLife = today > addWeeks(endOfLifeDate, 1);
|
||||
if (!isEndOfLife) {
|
||||
oldestSupportedRelease = releaseVersion;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!oldestSupportedRelease) {
|
||||
throw new Error("Could not determine oldest supported release.");
|
||||
}
|
||||
|
||||
return {
|
||||
maximumVersion: printEnterpriseVersion(newestSupportedRelease),
|
||||
minimumVersion: printEnterpriseVersion(oldestSupportedRelease),
|
||||
};
|
||||
}
|
||||
|
||||
function main() {
|
||||
const enterpriseReleasesPath = process.env[EnvVar.ENTERPRISE_RELEASES_PATH];
|
||||
if (!enterpriseReleasesPath) {
|
||||
throw new Error(
|
||||
`${EnvVar.ENTERPRISE_RELEASES_PATH} environment variable must be set`,
|
||||
);
|
||||
}
|
||||
|
||||
// Get the version compatibility data stored in the repo.
|
||||
const apiCompatibilityData = readApiCompatibility();
|
||||
|
||||
// Get the GHES release information.
|
||||
const releases = readEnterpriseReleases(enterpriseReleasesPath);
|
||||
|
||||
// Determine the supported range.
|
||||
const newCompatibilityData: ApiCompatibility = determineSupportedRange(
|
||||
new Date(),
|
||||
apiCompatibilityData,
|
||||
releases,
|
||||
);
|
||||
|
||||
// If the version range has changed, write the updates to `API_COMPATIBILITY_FILE`.
|
||||
if (
|
||||
newCompatibilityData.minimumVersion !==
|
||||
apiCompatibilityData.minimumVersion ||
|
||||
newCompatibilityData.maximumVersion !== apiCompatibilityData.maximumVersion
|
||||
) {
|
||||
const data = JSON.stringify(newCompatibilityData);
|
||||
fs.writeFileSync(API_COMPATIBILITY_FILE, `${data}\n`);
|
||||
|
||||
console.log(
|
||||
`Updated '${path.basename(API_COMPATIBILITY_FILE)}': ${newCompatibilityData.minimumVersion} - ${newCompatibilityData.maximumVersion}`,
|
||||
);
|
||||
} else {
|
||||
console.log(
|
||||
`No changes, not writing to '${path.basename(API_COMPATIBILITY_FILE)}'.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Only call `main` if this script was run directly.
|
||||
if (require.main === module) {
|
||||
main();
|
||||
}
|
||||
@@ -21,6 +21,21 @@ import {
|
||||
*/
|
||||
declare const __CODEQL_ACTION_VERSION__: string;
|
||||
|
||||
/**
|
||||
* Abstracts over GitHub Actions functions so that we do not have to stub
|
||||
* global functions in tests.
|
||||
*/
|
||||
export interface ActionsEnv {
|
||||
getOptionalInput: (name: string) => string | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the real `ActionsEnv` used by production code.
|
||||
*/
|
||||
export function getActionsEnv(): ActionsEnv {
|
||||
return { getOptionalInput };
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around core.getInput for inputs that always have a value.
|
||||
* Also see getOptionalInput.
|
||||
|
||||
@@ -30,7 +30,7 @@ import {
|
||||
} from "./dependency-caching";
|
||||
import { EnvVar } from "./environment";
|
||||
import { initFeatures } from "./feature-flags";
|
||||
import { BuiltInLanguage } from "./languages/index";
|
||||
import { BuiltInLanguage } from "./languages";
|
||||
import { getActionsLogger, Logger } from "./logging";
|
||||
import { cleanupAndUploadOverlayBaseDatabaseToCache } from "./overlay/caching";
|
||||
import { getRepositoryNwo } from "./repository";
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ import {
|
||||
} from "./analyze";
|
||||
import { createStubCodeQL } from "./codeql";
|
||||
import { Feature } from "./feature-flags";
|
||||
import { BuiltInLanguage } from "./languages/index";
|
||||
import { BuiltInLanguage } from "./languages";
|
||||
import { getRunnerLogger } from "./logging";
|
||||
import {
|
||||
setupTests,
|
||||
|
||||
+3
-3
@@ -21,7 +21,7 @@ import {
|
||||
} from "./diff-informed-analysis-utils";
|
||||
import { EnvVar } from "./environment";
|
||||
import { FeatureEnablement, Feature } from "./feature-flags";
|
||||
import { BuiltInLanguage, Language } from "./languages/index";
|
||||
import { BuiltInLanguage, Language } from "./languages";
|
||||
import { Logger, withGroupAsync } from "./logging";
|
||||
import { OverlayDatabaseMode } from "./overlay/overlay-database-mode";
|
||||
import type * as sarif from "./sarif";
|
||||
@@ -281,11 +281,11 @@ extensions:
|
||||
.join(checkoutPath, range.path)
|
||||
.replaceAll(path.sep, "/");
|
||||
|
||||
// Using yaml.dump() with `forceQuotes: true` ensures that all special
|
||||
// Using yaml.dump() with `quoteStyle: "double"` ensures that all special
|
||||
// characters are escaped, and that the path is always rendered as a
|
||||
// quoted string on a single line.
|
||||
return (
|
||||
` - [${yaml.dump(filename, { forceQuotes: true }).trim()}, ` +
|
||||
` - [${yaml.dump(filename, { quoteStyle: "single" }).trim()}, ` +
|
||||
`${range.startLine}, ${range.endLine}]\n`
|
||||
);
|
||||
})
|
||||
|
||||
@@ -10,7 +10,7 @@ import { determineAutobuildLanguages, runAutobuild } from "./autobuild";
|
||||
import { getCodeQL } from "./codeql";
|
||||
import { Config, getConfig } from "./config-utils";
|
||||
import { EnvVar } from "./environment";
|
||||
import { Language } from "./languages/index";
|
||||
import { Language } from "./languages";
|
||||
import { Logger, getActionsLogger } from "./logging";
|
||||
import {
|
||||
StatusReportBase,
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ import * as configUtils from "./config-utils";
|
||||
import { DocUrl } from "./doc-url";
|
||||
import { EnvVar } from "./environment";
|
||||
import { Feature, featureConfig, initFeatures } from "./feature-flags";
|
||||
import { BuiltInLanguage, Language } from "./languages/index";
|
||||
import { BuiltInLanguage, Language } from "./languages";
|
||||
import { Logger } from "./logging";
|
||||
import { getRepositoryNwo } from "./repository";
|
||||
import { asyncFilter, BuildMode } from "./util";
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@ import {
|
||||
import type { Config } from "./config-utils";
|
||||
import * as defaults from "./defaults.json";
|
||||
import { DocUrl } from "./doc-url";
|
||||
import { BuiltInLanguage } from "./languages/index";
|
||||
import { BuiltInLanguage } from "./languages";
|
||||
import { getRunnerLogger } from "./logging";
|
||||
import { ToolsSource } from "./setup-codeql";
|
||||
import {
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ import {
|
||||
FeatureEnablement,
|
||||
} from "./feature-flags";
|
||||
import { isAnalyzingDefaultBranch } from "./git-utils";
|
||||
import { Language } from "./languages/index";
|
||||
import { Language } from "./languages";
|
||||
import { Logger } from "./logging";
|
||||
import { writeBaseDatabaseOidsFile, writeOverlayChangesFile } from "./overlay";
|
||||
import { OverlayDatabaseMode } from "./overlay/overlay-database-mode";
|
||||
|
||||
@@ -18,7 +18,7 @@ import { Feature } from "./feature-flags";
|
||||
import { RepositoryProperties } from "./feature-flags/properties";
|
||||
import * as gitUtils from "./git-utils";
|
||||
import { GitVersionInfo } from "./git-utils";
|
||||
import { BuiltInLanguage, Language } from "./languages/index";
|
||||
import { BuiltInLanguage, Language } from "./languages";
|
||||
import { getRunnerLogger } from "./logging";
|
||||
import { CODEQL_OVERLAY_MINIMUM_VERSION } from "./overlay";
|
||||
import * as overlayDiagnostics from "./overlay/diagnostics";
|
||||
|
||||
+1
-1
@@ -48,7 +48,7 @@ import {
|
||||
hasSubmodules,
|
||||
isAnalyzingDefaultBranch,
|
||||
} from "./git-utils";
|
||||
import { BuiltInLanguage, Language } from "./languages/index";
|
||||
import { BuiltInLanguage, Language } from "./languages";
|
||||
import { Logger } from "./logging";
|
||||
import { CODEQL_OVERLAY_MINIMUM_VERSION } from "./overlay";
|
||||
import {
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
import test from "ava";
|
||||
import sinon from "sinon";
|
||||
|
||||
import { RepositoryPropertyName } from "../feature-flags/properties";
|
||||
import {
|
||||
getTestActionsEnv,
|
||||
RecordingLogger,
|
||||
setupTests,
|
||||
} from "../testing-utils";
|
||||
|
||||
import { getConfigFileInput } from "./file";
|
||||
|
||||
setupTests(test);
|
||||
|
||||
test("getConfigFileInput returns undefined by default", async (t) => {
|
||||
const logger = new RecordingLogger();
|
||||
const actionsEnv = getTestActionsEnv();
|
||||
const result = getConfigFileInput(logger, actionsEnv, {});
|
||||
t.is(result, undefined);
|
||||
});
|
||||
|
||||
const repositoryProperties = {
|
||||
[RepositoryPropertyName.CONFIG_FILE]: "/path/from/property",
|
||||
};
|
||||
|
||||
test("getConfigFileInput returns input value", async (t) => {
|
||||
const logger = new RecordingLogger();
|
||||
const actionsEnv = getTestActionsEnv();
|
||||
const testInput = "/some/path";
|
||||
sinon
|
||||
.stub(actionsEnv, "getOptionalInput")
|
||||
.withArgs("config-file")
|
||||
.returns(testInput);
|
||||
|
||||
// Even though both an input and repository property are configured,
|
||||
// we prefer the direct input to the Action.
|
||||
const result = getConfigFileInput(logger, actionsEnv, repositoryProperties);
|
||||
t.is(result, testInput);
|
||||
|
||||
// Check for the expected log message.
|
||||
t.true(logger.hasMessage("Using configuration file input from workflow"));
|
||||
});
|
||||
|
||||
test("getConfigFileInput returns repository property value", async (t) => {
|
||||
const logger = new RecordingLogger();
|
||||
const actionsEnv = getTestActionsEnv();
|
||||
|
||||
// Since there is no direct input, we should use the repository property.
|
||||
const result = getConfigFileInput(logger, actionsEnv, repositoryProperties);
|
||||
t.is(result, repositoryProperties[RepositoryPropertyName.CONFIG_FILE]);
|
||||
|
||||
// Check for the expected log message.
|
||||
t.true(
|
||||
logger.hasMessage(
|
||||
"Using configuration file input from repository property",
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
test("getConfigFileInput ignores empty repository property value", async (t) => {
|
||||
const logger = new RecordingLogger();
|
||||
const actionsEnv = getTestActionsEnv();
|
||||
|
||||
// Since the repository property value is an empty/whitespace string, we should ignore it.
|
||||
const result = getConfigFileInput(logger, actionsEnv, {
|
||||
[RepositoryPropertyName.CONFIG_FILE]: " ",
|
||||
});
|
||||
t.is(result, undefined);
|
||||
});
|
||||
@@ -0,0 +1,34 @@
|
||||
import { ActionsEnv } from "../actions-util";
|
||||
import {
|
||||
RepositoryProperties,
|
||||
RepositoryPropertyName,
|
||||
} from "../feature-flags/properties";
|
||||
import { Logger } from "../logging";
|
||||
|
||||
/**
|
||||
* Gets the value that is configured for the configuration file, if any.
|
||||
*/
|
||||
export function getConfigFileInput(
|
||||
logger: Logger,
|
||||
actions: ActionsEnv,
|
||||
repositoryProperties: Partial<RepositoryProperties>,
|
||||
): string | undefined {
|
||||
const input = actions.getOptionalInput("config-file");
|
||||
|
||||
if (input !== undefined) {
|
||||
logger.info(`Using configuration file input from workflow: ${input}`);
|
||||
return input;
|
||||
}
|
||||
|
||||
const propertyValue =
|
||||
repositoryProperties[RepositoryPropertyName.CONFIG_FILE];
|
||||
|
||||
if (propertyValue !== undefined && propertyValue.trim().length > 0) {
|
||||
logger.info(
|
||||
`Using configuration file input from repository property: ${propertyValue}`,
|
||||
);
|
||||
return propertyValue;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
@@ -1,280 +0,0 @@
|
||||
import test from "ava";
|
||||
|
||||
import {
|
||||
EffectiveToolsInputSource,
|
||||
resolveToolsInput,
|
||||
resolveToolsInputWithMetadata,
|
||||
} from "../config/resolve-tools-input";
|
||||
import {
|
||||
RepositoryPropertyName,
|
||||
ToolsModeRepositoryPropertyValue,
|
||||
} from "../feature-flags/properties";
|
||||
import type { RepositoryProperties } from "../feature-flags/properties";
|
||||
import {
|
||||
getRecordingLogger,
|
||||
LoggedMessage,
|
||||
setupTests,
|
||||
} from "../testing-utils";
|
||||
|
||||
setupTests(test);
|
||||
|
||||
test("resolveToolsInput returns undefined when no tools input or repository property is set", (t) => {
|
||||
const loggedMessages: LoggedMessage[] = [];
|
||||
const logger = getRecordingLogger(loggedMessages);
|
||||
|
||||
const result = resolveToolsInput(undefined, false, {}, logger);
|
||||
|
||||
t.is(result, undefined);
|
||||
t.is(loggedMessages.length, 0);
|
||||
});
|
||||
|
||||
test("resolveToolsInput returns workflow input when only workflow input is provided", (t) => {
|
||||
const loggedMessages: LoggedMessage[] = [];
|
||||
const logger = getRecordingLogger(loggedMessages);
|
||||
|
||||
const result = resolveToolsInput("latest", false, {}, logger);
|
||||
|
||||
t.is(result, "latest");
|
||||
t.is(loggedMessages.length, 1);
|
||||
t.is(
|
||||
loggedMessages[0].message,
|
||||
"Setting tools: latest based on workflow input.",
|
||||
);
|
||||
});
|
||||
|
||||
test("resolveToolsInput returns repository property when only repository property is provided", (t) => {
|
||||
const loggedMessages: LoggedMessage[] = [];
|
||||
const logger = getRecordingLogger(loggedMessages);
|
||||
|
||||
const repositoryProperties: RepositoryProperties = {
|
||||
[RepositoryPropertyName.TOOLS]: "toolcache",
|
||||
};
|
||||
const result = resolveToolsInput(
|
||||
undefined,
|
||||
false,
|
||||
repositoryProperties,
|
||||
logger,
|
||||
);
|
||||
|
||||
t.is(result, "toolcache");
|
||||
t.is(loggedMessages.length, 1);
|
||||
t.is(
|
||||
loggedMessages[0].message,
|
||||
"Setting tools: toolcache based on the 'github-codeql-tools' repository property (mode: 'enforce').",
|
||||
);
|
||||
});
|
||||
|
||||
test("resolveToolsInput prioritizes workflow input over repository property", (t) => {
|
||||
const loggedMessages: LoggedMessage[] = [];
|
||||
const logger = getRecordingLogger(loggedMessages);
|
||||
|
||||
const repositoryProperties: RepositoryProperties = {
|
||||
[RepositoryPropertyName.TOOLS]: "toolcache",
|
||||
};
|
||||
const result = resolveToolsInput(
|
||||
"nightly",
|
||||
false,
|
||||
repositoryProperties,
|
||||
logger,
|
||||
);
|
||||
|
||||
t.is(result, "nightly");
|
||||
t.is(loggedMessages.length, 1);
|
||||
t.is(
|
||||
loggedMessages[0].message,
|
||||
"Setting tools: nightly based on workflow input.",
|
||||
);
|
||||
});
|
||||
|
||||
test("resolveToolsInput treats empty string workflow input as not set", (t) => {
|
||||
const loggedMessages: LoggedMessage[] = [];
|
||||
const logger = getRecordingLogger(loggedMessages);
|
||||
|
||||
const repositoryProperties: RepositoryProperties = {
|
||||
[RepositoryPropertyName.TOOLS]: "toolcache",
|
||||
};
|
||||
const result = resolveToolsInput("", false, repositoryProperties, logger);
|
||||
|
||||
t.is(result, "toolcache");
|
||||
t.is(loggedMessages.length, 1);
|
||||
t.is(
|
||||
loggedMessages[0].message,
|
||||
"Setting tools: toolcache based on the 'github-codeql-tools' repository property (mode: 'enforce').",
|
||||
);
|
||||
});
|
||||
|
||||
test("resolveToolsInput returns undefined when repository property is undefined", (t) => {
|
||||
const loggedMessages: LoggedMessage[] = [];
|
||||
const logger = getRecordingLogger(loggedMessages);
|
||||
|
||||
const repositoryProperties: RepositoryProperties = {
|
||||
[RepositoryPropertyName.TOOLS]: undefined,
|
||||
};
|
||||
const result = resolveToolsInput(
|
||||
undefined,
|
||||
false,
|
||||
repositoryProperties,
|
||||
logger,
|
||||
);
|
||||
|
||||
t.is(result, undefined);
|
||||
t.is(loggedMessages.length, 0);
|
||||
});
|
||||
|
||||
test("resolveToolsInput returns repository property when workflow input is not set", (t) => {
|
||||
const loggedMessages: LoggedMessage[] = [];
|
||||
const logger = getRecordingLogger(loggedMessages);
|
||||
|
||||
const repositoryProperties: RepositoryProperties = {
|
||||
[RepositoryPropertyName.TOOLS]: "toolcache",
|
||||
};
|
||||
const result = resolveToolsInput(
|
||||
undefined,
|
||||
false,
|
||||
repositoryProperties,
|
||||
logger,
|
||||
);
|
||||
|
||||
t.is(result, "toolcache");
|
||||
t.is(loggedMessages.length, 1);
|
||||
t.is(
|
||||
loggedMessages[0].message,
|
||||
"Setting tools: toolcache based on the 'github-codeql-tools' repository property (mode: 'enforce').",
|
||||
);
|
||||
});
|
||||
|
||||
test("resolveToolsInput does not log when workflow input and repository property are not set", (t) => {
|
||||
const loggedMessages: LoggedMessage[] = [];
|
||||
const logger = getRecordingLogger(loggedMessages);
|
||||
|
||||
const result = resolveToolsInput(undefined, false, {}, logger);
|
||||
|
||||
t.is(result, undefined);
|
||||
t.is(loggedMessages.length, 0);
|
||||
});
|
||||
|
||||
test("resolveToolsInput applies tools property in enforce mode for static workflows", (t) => {
|
||||
const loggedMessages: LoggedMessage[] = [];
|
||||
const logger = getRecordingLogger(loggedMessages);
|
||||
|
||||
const repositoryProperties: RepositoryProperties = {
|
||||
[RepositoryPropertyName.TOOLS]: "toolcache",
|
||||
[RepositoryPropertyName.TOOLS_MODE]:
|
||||
ToolsModeRepositoryPropertyValue.Enforce,
|
||||
};
|
||||
const result = resolveToolsInput(
|
||||
undefined,
|
||||
false,
|
||||
repositoryProperties,
|
||||
logger,
|
||||
);
|
||||
|
||||
t.is(result, "toolcache");
|
||||
t.is(loggedMessages.length, 1);
|
||||
t.is(
|
||||
loggedMessages[0].message,
|
||||
"Setting tools: toolcache based on the 'github-codeql-tools' repository property (mode: 'enforce').",
|
||||
);
|
||||
});
|
||||
|
||||
test("resolveToolsInput applies tools property in dynamic mode for dynamic workflows", (t) => {
|
||||
const loggedMessages: LoggedMessage[] = [];
|
||||
const logger = getRecordingLogger(loggedMessages);
|
||||
|
||||
const repositoryProperties: RepositoryProperties = {
|
||||
[RepositoryPropertyName.TOOLS]: "toolcache",
|
||||
[RepositoryPropertyName.TOOLS_MODE]:
|
||||
ToolsModeRepositoryPropertyValue.Dynamic,
|
||||
};
|
||||
const result = resolveToolsInput(
|
||||
undefined,
|
||||
true,
|
||||
repositoryProperties,
|
||||
logger,
|
||||
);
|
||||
|
||||
t.is(result, "toolcache");
|
||||
t.is(loggedMessages.length, 1);
|
||||
t.is(
|
||||
loggedMessages[0].message,
|
||||
"Setting tools: toolcache based on the 'github-codeql-tools' repository property (mode: 'dynamic').",
|
||||
);
|
||||
});
|
||||
|
||||
test("resolveToolsInput ignores tools property in dynamic mode for static workflows", (t) => {
|
||||
const loggedMessages: LoggedMessage[] = [];
|
||||
const logger = getRecordingLogger(loggedMessages);
|
||||
|
||||
const repositoryProperties: RepositoryProperties = {
|
||||
[RepositoryPropertyName.TOOLS]: "toolcache",
|
||||
[RepositoryPropertyName.TOOLS_MODE]:
|
||||
ToolsModeRepositoryPropertyValue.Dynamic,
|
||||
};
|
||||
const result = resolveToolsInput(
|
||||
undefined,
|
||||
false,
|
||||
repositoryProperties,
|
||||
logger,
|
||||
);
|
||||
|
||||
t.is(result, undefined);
|
||||
t.is(loggedMessages.length, 1);
|
||||
t.is(
|
||||
loggedMessages[0].message,
|
||||
"Ignoring 'github-codeql-tools' repository property because 'github-codeql-tools-mode' is set to 'dynamic' and this is not a dynamic workflow.",
|
||||
);
|
||||
});
|
||||
|
||||
test("resolveToolsInputWithMetadata reports workflow input source", (t) => {
|
||||
const logger = getRecordingLogger([]);
|
||||
|
||||
const result = resolveToolsInputWithMetadata("latest", false, {}, logger);
|
||||
|
||||
t.is(result.effectiveToolsInput, "latest");
|
||||
t.is(
|
||||
result.effectiveToolsInputSource,
|
||||
EffectiveToolsInputSource.WorkflowInput,
|
||||
);
|
||||
t.is(result.toolsRepoPropertyMode, undefined);
|
||||
});
|
||||
|
||||
test("resolveToolsInputWithMetadata reports repository property source and mode", (t) => {
|
||||
const logger = getRecordingLogger([]);
|
||||
|
||||
const result = resolveToolsInputWithMetadata(
|
||||
undefined,
|
||||
false,
|
||||
{
|
||||
[RepositoryPropertyName.TOOLS]: "toolcache",
|
||||
[RepositoryPropertyName.TOOLS_MODE]:
|
||||
ToolsModeRepositoryPropertyValue.Enforce,
|
||||
},
|
||||
logger,
|
||||
);
|
||||
|
||||
t.is(result.effectiveToolsInput, "toolcache");
|
||||
t.is(
|
||||
result.effectiveToolsInputSource,
|
||||
EffectiveToolsInputSource.RepositoryProperty,
|
||||
);
|
||||
t.is(result.toolsRepoPropertyMode, ToolsModeRepositoryPropertyValue.Enforce);
|
||||
});
|
||||
|
||||
test("resolveToolsInputWithMetadata reports dynamic-mode skip on static workflows", (t) => {
|
||||
const logger = getRecordingLogger([]);
|
||||
|
||||
const result = resolveToolsInputWithMetadata(
|
||||
undefined,
|
||||
false,
|
||||
{
|
||||
[RepositoryPropertyName.TOOLS]: "toolcache",
|
||||
[RepositoryPropertyName.TOOLS_MODE]:
|
||||
ToolsModeRepositoryPropertyValue.Dynamic,
|
||||
},
|
||||
logger,
|
||||
);
|
||||
|
||||
t.is(result.effectiveToolsInput, undefined);
|
||||
t.is(result.effectiveToolsInputSource, EffectiveToolsInputSource.None);
|
||||
t.is(result.toolsRepoPropertyMode, ToolsModeRepositoryPropertyValue.Dynamic);
|
||||
});
|
||||
@@ -1,100 +0,0 @@
|
||||
import {
|
||||
RepositoryProperties,
|
||||
RepositoryPropertyName,
|
||||
ToolsModeRepositoryPropertyValue,
|
||||
} from "../feature-flags/properties";
|
||||
import { Logger } from "../logging";
|
||||
|
||||
export enum EffectiveToolsInputSource {
|
||||
WorkflowInput = "workflow-input",
|
||||
RepositoryProperty = "repository-property",
|
||||
None = "none",
|
||||
}
|
||||
|
||||
export type ResolvedToolsInput = {
|
||||
effectiveToolsInput: string | undefined;
|
||||
effectiveToolsInputSource: EffectiveToolsInputSource;
|
||||
toolsRepoPropertyMode: ToolsModeRepositoryPropertyValue | undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
* Resolves the effective tools input by combining the workflow input and repository properties.
|
||||
* The explicit `tools` workflow input takes precedence. If none is provided,
|
||||
* falls back to the repository property (if set). The optional
|
||||
* `github-codeql-tools-mode` repository property controls whether this fallback
|
||||
* applies to all workflows (`enforce`) or only dynamic workflows (`dynamic`).
|
||||
*
|
||||
* @param toolsWorkflowInput - The value of the `tools` workflow input, if provided.
|
||||
* @param isDynamicWorkflow - Whether the current workflow is dynamic.
|
||||
* @param repositoryProperties - The parsed repository properties.
|
||||
* @param logger - Logger for outputting resolution messages.
|
||||
* @returns The effective tools input value.
|
||||
*/
|
||||
export function resolveToolsInput(
|
||||
toolsWorkflowInput: string | undefined,
|
||||
isDynamicWorkflow: boolean,
|
||||
repositoryProperties: RepositoryProperties,
|
||||
logger: Logger,
|
||||
): string | undefined {
|
||||
return resolveToolsInputWithMetadata(
|
||||
toolsWorkflowInput,
|
||||
isDynamicWorkflow,
|
||||
repositoryProperties,
|
||||
logger,
|
||||
).effectiveToolsInput;
|
||||
}
|
||||
|
||||
export function resolveToolsInputWithMetadata(
|
||||
toolsWorkflowInput: string | undefined,
|
||||
isDynamicWorkflow: boolean,
|
||||
repositoryProperties: RepositoryProperties,
|
||||
logger: Logger,
|
||||
): ResolvedToolsInput {
|
||||
if (toolsWorkflowInput) {
|
||||
logger.info(
|
||||
`Setting tools: ${toolsWorkflowInput} based on workflow input.`,
|
||||
);
|
||||
return {
|
||||
effectiveToolsInput: toolsWorkflowInput,
|
||||
effectiveToolsInputSource: EffectiveToolsInputSource.WorkflowInput,
|
||||
toolsRepoPropertyMode: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
const toolsPropertyValue = repositoryProperties[RepositoryPropertyName.TOOLS];
|
||||
const toolsMode =
|
||||
repositoryProperties[RepositoryPropertyName.TOOLS_MODE] ??
|
||||
ToolsModeRepositoryPropertyValue.Enforce;
|
||||
|
||||
if (
|
||||
toolsPropertyValue &&
|
||||
toolsMode === ToolsModeRepositoryPropertyValue.Dynamic &&
|
||||
!isDynamicWorkflow
|
||||
) {
|
||||
logger.info(
|
||||
`Ignoring '${RepositoryPropertyName.TOOLS}' repository property because '${RepositoryPropertyName.TOOLS_MODE}' is set to '${toolsMode}' and this is not a dynamic workflow.`,
|
||||
);
|
||||
return {
|
||||
effectiveToolsInput: undefined,
|
||||
effectiveToolsInputSource: EffectiveToolsInputSource.None,
|
||||
toolsRepoPropertyMode: toolsMode,
|
||||
};
|
||||
}
|
||||
|
||||
if (toolsPropertyValue) {
|
||||
logger.info(
|
||||
`Setting tools: ${toolsPropertyValue} based on the '${RepositoryPropertyName.TOOLS}' repository property (mode: '${toolsMode}').`,
|
||||
);
|
||||
return {
|
||||
effectiveToolsInput: toolsPropertyValue,
|
||||
effectiveToolsInputSource: EffectiveToolsInputSource.RepositoryProperty,
|
||||
toolsRepoPropertyMode: toolsMode,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
effectiveToolsInput: undefined,
|
||||
effectiveToolsInputSource: EffectiveToolsInputSource.None,
|
||||
toolsRepoPropertyMode: undefined,
|
||||
};
|
||||
}
|
||||
@@ -12,7 +12,7 @@ import { createStubCodeQL } from "./codeql";
|
||||
import { Config } from "./config-utils";
|
||||
import { cleanupAndUploadDatabases } from "./database-upload";
|
||||
import * as gitUtils from "./git-utils";
|
||||
import { BuiltInLanguage } from "./languages/index";
|
||||
import { BuiltInLanguage } from "./languages";
|
||||
import { RepositoryNwo } from "./repository";
|
||||
import {
|
||||
checkExpectedLogMessages,
|
||||
|
||||
@@ -4,7 +4,7 @@ import * as path from "path";
|
||||
import * as artifact from "@actions/artifact";
|
||||
import * as artifactLegacy from "@actions/artifact-legacy";
|
||||
import * as core from "@actions/core";
|
||||
import archiver from "archiver";
|
||||
import { ZipArchive } from "archiver";
|
||||
|
||||
import { getOptionalInput, getTemporaryDirectory } from "./actions-util";
|
||||
import { dbIsFinalized } from "./analyze";
|
||||
@@ -13,7 +13,7 @@ import { type CodeQL } from "./codeql";
|
||||
import { Config } from "./config-utils";
|
||||
import { EnvVar } from "./environment";
|
||||
import * as json from "./json";
|
||||
import { Language } from "./languages/index";
|
||||
import { Language } from "./languages";
|
||||
import { Logger, withGroup } from "./logging";
|
||||
import {
|
||||
isSafeArtifactUpload,
|
||||
@@ -397,7 +397,7 @@ async function createPartialDatabaseBundle(
|
||||
await fs.promises.rm(databaseBundlePath, { force: true });
|
||||
}
|
||||
const output = fs.createWriteStream(databaseBundlePath);
|
||||
const zip = archiver("zip");
|
||||
const zip = new ZipArchive();
|
||||
|
||||
zip.on("error", (err) => {
|
||||
throw err;
|
||||
|
||||
@@ -27,7 +27,7 @@ import {
|
||||
CacheStoreResult,
|
||||
} from "./dependency-caching";
|
||||
import { Feature } from "./feature-flags";
|
||||
import { BuiltInLanguage } from "./languages/index";
|
||||
import { BuiltInLanguage } from "./languages";
|
||||
import {
|
||||
setupTests,
|
||||
createFeatures,
|
||||
|
||||
@@ -11,7 +11,7 @@ import { CodeQL } from "./codeql";
|
||||
import { Config } from "./config-utils";
|
||||
import { EnvVar } from "./environment";
|
||||
import { Feature, FeatureEnablement } from "./feature-flags";
|
||||
import { BuiltInLanguage, Language } from "./languages/index";
|
||||
import { BuiltInLanguage, Language } from "./languages";
|
||||
import { Logger } from "./logging";
|
||||
import { getErrorMessage, getRequiredEnvParam } from "./util";
|
||||
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ import { existsSync, mkdirSync, writeFileSync } from "fs";
|
||||
import path from "path";
|
||||
|
||||
import type { Config } from "./config-utils";
|
||||
import { Language } from "./languages/index";
|
||||
import { Language } from "./languages";
|
||||
import { getActionsLogger } from "./logging";
|
||||
import { getCodeQLDatabasePath } from "./util";
|
||||
|
||||
|
||||
@@ -134,7 +134,6 @@ export enum Feature {
|
||||
QaTelemetryEnabled = "qa_telemetry_enabled",
|
||||
/** Note that this currently only disables baseline file coverage information. */
|
||||
SkipFileCoverageOnPrs = "skip_file_coverage_on_prs",
|
||||
StartProxyRemoveUnusedRegistries = "start_proxy_remove_unused_registries",
|
||||
StartProxyUseFeaturesRelease = "start_proxy_use_features_release",
|
||||
UploadOverlayDbToApi = "upload_overlay_db_to_api",
|
||||
ValidateDbConfig = "validate_db_config",
|
||||
@@ -375,11 +374,6 @@ export const featureConfig = {
|
||||
minimumVersion: undefined,
|
||||
toolsFeature: ToolsFeature.SuppressesMissingFileBaselineWarning,
|
||||
},
|
||||
[Feature.StartProxyRemoveUnusedRegistries]: {
|
||||
defaultValue: false,
|
||||
envVar: "CODEQL_ACTION_START_PROXY_REMOVE_UNUSED_REGISTRIES",
|
||||
minimumVersion: undefined,
|
||||
},
|
||||
[Feature.StartProxyUseFeaturesRelease]: {
|
||||
defaultValue: false,
|
||||
envVar: "CODEQL_ACTION_START_PROXY_USE_FEATURES_RELEASE",
|
||||
|
||||
@@ -77,9 +77,8 @@ test.serial("loadPropertiesFromApi loads known properties", async (t) => {
|
||||
status: 200,
|
||||
url: "",
|
||||
data: [
|
||||
{ property_name: "github-codeql-config-file", value: "owner/repo" },
|
||||
{ property_name: "github-codeql-extra-queries", value: "+queries" },
|
||||
{ property_name: "github-codeql-tools", value: "toolcache" },
|
||||
{ property_name: "github-codeql-tools-mode", value: "dynamic" },
|
||||
{ property_name: "unknown-property", value: "something" },
|
||||
] satisfies properties.GitHubPropertiesResponse,
|
||||
});
|
||||
@@ -90,44 +89,11 @@ test.serial("loadPropertiesFromApi loads known properties", async (t) => {
|
||||
mockRepositoryNwo,
|
||||
);
|
||||
t.deepEqual(response, {
|
||||
"github-codeql-config-file": "owner/repo",
|
||||
"github-codeql-extra-queries": "+queries",
|
||||
"github-codeql-tools": "toolcache",
|
||||
"github-codeql-tools-mode": "dynamic",
|
||||
});
|
||||
});
|
||||
|
||||
test.serial(
|
||||
"loadPropertiesFromApi warns if tools mode property has unexpected value",
|
||||
async (t) => {
|
||||
sinon.stub(api, "getRepositoryProperties").resolves({
|
||||
headers: {},
|
||||
status: 200,
|
||||
url: "",
|
||||
data: [
|
||||
{
|
||||
property_name: "github-codeql-tools-mode",
|
||||
value: "all",
|
||||
},
|
||||
] satisfies properties.GitHubPropertiesResponse,
|
||||
});
|
||||
const logger = getRunnerLogger(true);
|
||||
const warningSpy = sinon.spy(logger, "warning");
|
||||
const mockRepositoryNwo = parseRepositoryNwo("owner/repo");
|
||||
const response = await properties.loadPropertiesFromApi(
|
||||
logger,
|
||||
mockRepositoryNwo,
|
||||
);
|
||||
t.deepEqual(response, {
|
||||
"github-codeql-tools-mode": "enforce",
|
||||
});
|
||||
t.true(warningSpy.calledOnce);
|
||||
t.is(
|
||||
warningSpy.firstCall.args[0],
|
||||
"Repository property 'github-codeql-tools-mode' has unexpected value 'all'. Expected 'dynamic' or 'enforce'. Defaulting to 'enforce'.",
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
test.serial("loadPropertiesFromApi parses true boolean property", async (t) => {
|
||||
sinon.stub(api, "getRepositoryProperties").resolves({
|
||||
headers: {},
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import * as github from "@actions/github";
|
||||
|
||||
import { isDynamicWorkflow } from "../actions-util";
|
||||
import { getRepositoryProperties } from "../api-client";
|
||||
import { Logger } from "../logging";
|
||||
import { RepositoryNwo } from "../repository";
|
||||
import { getErrorMessage, Result, Success, Failure } from "../util";
|
||||
|
||||
/** The common prefix that we expect all of our repository properties to have. */
|
||||
export const GITHUB_CODEQL_PROPERTY_PREFIX = "github-codeql-";
|
||||
@@ -13,25 +10,18 @@ export const GITHUB_CODEQL_PROPERTY_PREFIX = "github-codeql-";
|
||||
* Enumerates repository property names that have some meaning to us.
|
||||
*/
|
||||
export enum RepositoryPropertyName {
|
||||
CONFIG_FILE = "github-codeql-config-file",
|
||||
DISABLE_OVERLAY = "github-codeql-disable-overlay",
|
||||
EXTRA_QUERIES = "github-codeql-extra-queries",
|
||||
FILE_COVERAGE_ON_PRS = "github-codeql-file-coverage-on-prs",
|
||||
TOOLS = "github-codeql-tools",
|
||||
TOOLS_MODE = "github-codeql-tools-mode",
|
||||
}
|
||||
|
||||
export enum ToolsModeRepositoryPropertyValue {
|
||||
Dynamic = "dynamic",
|
||||
Enforce = "enforce",
|
||||
}
|
||||
|
||||
/** Parsed types of the known repository properties. */
|
||||
export type AllRepositoryProperties = {
|
||||
[RepositoryPropertyName.CONFIG_FILE]: string;
|
||||
[RepositoryPropertyName.DISABLE_OVERLAY]: boolean;
|
||||
[RepositoryPropertyName.EXTRA_QUERIES]: string;
|
||||
[RepositoryPropertyName.FILE_COVERAGE_ON_PRS]: boolean;
|
||||
[RepositoryPropertyName.TOOLS]: string;
|
||||
[RepositoryPropertyName.TOOLS_MODE]: ToolsModeRepositoryPropertyValue;
|
||||
};
|
||||
|
||||
/** Parsed repository properties. */
|
||||
@@ -39,11 +29,10 @@ export type RepositoryProperties = Partial<AllRepositoryProperties>;
|
||||
|
||||
/** Maps known repository properties to the type we expect to get from the API. */
|
||||
export type RepositoryPropertyApiType = {
|
||||
[RepositoryPropertyName.CONFIG_FILE]: string;
|
||||
[RepositoryPropertyName.DISABLE_OVERLAY]: string;
|
||||
[RepositoryPropertyName.EXTRA_QUERIES]: string;
|
||||
[RepositoryPropertyName.FILE_COVERAGE_ON_PRS]: string;
|
||||
[RepositoryPropertyName.TOOLS]: string;
|
||||
[RepositoryPropertyName.TOOLS_MODE]: string;
|
||||
};
|
||||
|
||||
/** The type of functions which take the `value` from the API and try to convert it to the type we want. */
|
||||
@@ -88,14 +77,10 @@ const booleanProperty = {
|
||||
const repositoryPropertyParsers: {
|
||||
[K in RepositoryPropertyName]: PropertyInfo<K>;
|
||||
} = {
|
||||
[RepositoryPropertyName.CONFIG_FILE]: stringProperty,
|
||||
[RepositoryPropertyName.DISABLE_OVERLAY]: booleanProperty,
|
||||
[RepositoryPropertyName.EXTRA_QUERIES]: stringProperty,
|
||||
[RepositoryPropertyName.FILE_COVERAGE_ON_PRS]: booleanProperty,
|
||||
[RepositoryPropertyName.TOOLS]: stringProperty,
|
||||
[RepositoryPropertyName.TOOLS_MODE]: {
|
||||
validate: isString,
|
||||
parse: parseToolsModeRepositoryProperty,
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -191,38 +176,6 @@ export async function loadPropertiesFromApi(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads [repository properties](https://docs.github.com/en/organizations/managing-organization-settings/managing-custom-properties-for-repositories-in-your-organization) if applicable.
|
||||
*/
|
||||
export async function loadRepositoryProperties(
|
||||
repositoryNwo: RepositoryNwo,
|
||||
logger: Logger,
|
||||
): Promise<Result<RepositoryProperties, unknown>> {
|
||||
// See if we can skip loading repository properties early. In particular,
|
||||
// repositories owned by users cannot have repository properties, so we can
|
||||
// skip the API call entirely in that case.
|
||||
const repositoryOwnerType = github.context.payload.repository?.owner.type;
|
||||
logger.debug(
|
||||
`Repository owner type is '${repositoryOwnerType ?? "unknown"}'.`,
|
||||
);
|
||||
if (repositoryOwnerType === "User") {
|
||||
logger.debug(
|
||||
"Skipping loading repository properties because the repository is owned by a user and " +
|
||||
"therefore cannot have repository properties.",
|
||||
);
|
||||
return new Success({});
|
||||
}
|
||||
|
||||
try {
|
||||
return new Success(await loadPropertiesFromApi(logger, repositoryNwo));
|
||||
} catch (error) {
|
||||
logger.info(
|
||||
`Failed to load repository properties: ${getErrorMessage(error)}`,
|
||||
);
|
||||
return new Failure(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that `value` has the correct type for `K` and, if so, update the partial set of repository
|
||||
* properties with the parsed value of the specified property.
|
||||
@@ -268,25 +221,6 @@ function parseStringRepositoryProperty(_name: string, value: string): string {
|
||||
return value;
|
||||
}
|
||||
|
||||
/** Parse the tools mode repository property. */
|
||||
function parseToolsModeRepositoryProperty(
|
||||
name: string,
|
||||
value: string,
|
||||
logger: Logger,
|
||||
): ToolsModeRepositoryPropertyValue {
|
||||
if (
|
||||
value !== ToolsModeRepositoryPropertyValue.Dynamic &&
|
||||
value !== ToolsModeRepositoryPropertyValue.Enforce
|
||||
) {
|
||||
logger.warning(
|
||||
`Repository property '${name}' has unexpected value '${value}'. Expected 'dynamic' or 'enforce'. Defaulting to 'enforce'.`,
|
||||
);
|
||||
return ToolsModeRepositoryPropertyValue.Enforce;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/** Set of known repository property names, for fast lookups. */
|
||||
const KNOWN_REPOSITORY_PROPERTY_NAMES = new Set<string>(
|
||||
Object.values(RepositoryPropertyName),
|
||||
|
||||
+45
-41
@@ -2,13 +2,14 @@ import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
|
||||
import * as core from "@actions/core";
|
||||
import * as github from "@actions/github";
|
||||
import * as io from "@actions/io";
|
||||
import * as semver from "semver";
|
||||
import { v4 as uuidV4 } from "uuid";
|
||||
|
||||
import {
|
||||
isDynamicWorkflow,
|
||||
FileCmdNotFoundError,
|
||||
getActionsEnv,
|
||||
getActionVersion,
|
||||
getFileType,
|
||||
getOptionalInput,
|
||||
@@ -24,10 +25,7 @@ import {
|
||||
shouldRestoreCache,
|
||||
} from "./caching-utils";
|
||||
import { CodeQL } from "./codeql";
|
||||
import {
|
||||
EffectiveToolsInputSource,
|
||||
resolveToolsInputWithMetadata,
|
||||
} from "./config/resolve-tools-input";
|
||||
import { getConfigFileInput } from "./config/file";
|
||||
import * as configUtils from "./config-utils";
|
||||
import {
|
||||
DependencyCacheRestoreStatusReport,
|
||||
@@ -44,8 +42,8 @@ import {
|
||||
import { EnvVar } from "./environment";
|
||||
import { Feature, FeatureEnablement, initFeatures } from "./feature-flags";
|
||||
import {
|
||||
loadRepositoryProperties,
|
||||
ToolsModeRepositoryPropertyValue,
|
||||
loadPropertiesFromApi,
|
||||
RepositoryProperties,
|
||||
} from "./feature-flags/properties";
|
||||
import {
|
||||
checkInstallPython311,
|
||||
@@ -57,14 +55,14 @@ import {
|
||||
initConfig,
|
||||
runDatabaseInitCluster,
|
||||
} from "./init";
|
||||
import { JavaEnvVars, BuiltInLanguage } from "./languages/index";
|
||||
import { JavaEnvVars, BuiltInLanguage } from "./languages";
|
||||
import { getActionsLogger, Logger, withGroupAsync } from "./logging";
|
||||
import {
|
||||
downloadOverlayBaseDatabaseFromCache,
|
||||
OverlayBaseDatabaseDownloadStats,
|
||||
} from "./overlay/caching";
|
||||
import { OverlayDatabaseMode } from "./overlay/overlay-database-mode";
|
||||
import { getRepositoryNwo } from "./repository";
|
||||
import { getRepositoryNwo, RepositoryNwo } from "./repository";
|
||||
import { ToolsSource } from "./setup-codeql";
|
||||
import {
|
||||
ActionName,
|
||||
@@ -97,7 +95,10 @@ import {
|
||||
checkActionVersion,
|
||||
getErrorMessage,
|
||||
BuildMode,
|
||||
Result,
|
||||
getOptionalEnvVar,
|
||||
Success,
|
||||
Failure,
|
||||
} from "./util";
|
||||
import { checkWorkflow } from "./workflow";
|
||||
|
||||
@@ -141,9 +142,6 @@ async function sendCompletedStatusReport(
|
||||
toolsFeatureFlagsValid: boolean | undefined,
|
||||
toolsSource: ToolsSource,
|
||||
toolsVersion: string,
|
||||
effectiveToolsInput: string | undefined,
|
||||
effectiveToolsInputSource: EffectiveToolsInputSource,
|
||||
toolsRepoPropertyMode: ToolsModeRepositoryPropertyValue | undefined,
|
||||
overlayBaseDatabaseStats: OverlayBaseDatabaseDownloadStats | undefined,
|
||||
dependencyCachingResults: DependencyCacheRestoreStatusReport | undefined,
|
||||
logger: Logger,
|
||||
@@ -169,9 +167,6 @@ async function sendCompletedStatusReport(
|
||||
const initStatusReport: InitStatusReport = {
|
||||
...statusReportBase,
|
||||
tools_input: getOptionalInput("tools") || "",
|
||||
effective_tools_input: effectiveToolsInput || "",
|
||||
effective_tools_input_source: effectiveToolsInputSource,
|
||||
tools_repo_property_mode: toolsRepoPropertyMode || "",
|
||||
tools_resolved_version: toolsVersion,
|
||||
tools_source: toolsSource || ToolsSource.Unknown,
|
||||
workflow_languages: workflowLanguages || "",
|
||||
@@ -214,6 +209,7 @@ async function run(startedAt: Date) {
|
||||
// possible, and only use safe functions outside.
|
||||
|
||||
const logger = getActionsLogger();
|
||||
const actionsEnv = getActionsEnv();
|
||||
|
||||
let apiDetails: GitHubApiCombinedDetails;
|
||||
let config: configUtils.Config | undefined;
|
||||
@@ -226,9 +222,6 @@ async function run(startedAt: Date) {
|
||||
let toolsSource: ToolsSource;
|
||||
let toolsVersion: string;
|
||||
let zstdAvailability: ZstdAvailability | undefined;
|
||||
let effectiveToolsInput: string | undefined;
|
||||
let effectiveToolsInputSource: EffectiveToolsInputSource;
|
||||
let toolsRepoPropertyMode: ToolsModeRepositoryPropertyValue | undefined;
|
||||
|
||||
try {
|
||||
initializeEnvironment(getActionVersion());
|
||||
@@ -270,7 +263,7 @@ async function run(startedAt: Date) {
|
||||
|
||||
core.exportVariable(EnvVar.INIT_ACTION_HAS_RUN, "true");
|
||||
|
||||
configFile = getOptionalInput("config-file");
|
||||
configFile = getConfigFileInput(logger, actionsEnv, repositoryProperties);
|
||||
|
||||
// path.resolve() respects the intended semantics of source-root. If
|
||||
// source-root is relative, it is relative to the GITHUB_WORKSPACE. If
|
||||
@@ -307,21 +300,6 @@ async function run(startedAt: Date) {
|
||||
const codeQLDefaultVersionInfo =
|
||||
await features.getEnabledDefaultCliVersions(gitHubVersion.type);
|
||||
toolsFeatureFlagsValid = codeQLDefaultVersionInfo.toolsFeatureFlagsValid;
|
||||
|
||||
// Determine the effective tools input.
|
||||
// The explicit `tools` workflow input takes precedence. If none is provided,
|
||||
// fall back to the 'github-codeql-tools' repository property (if set).
|
||||
// If 'github-codeql-tools-mode' is set to 'dynamic', this fallback applies
|
||||
// only to dynamic workflows. Otherwise, it applies to all workflows.
|
||||
const resolvedToolsInput = resolveToolsInputWithMetadata(
|
||||
getOptionalInput("tools"),
|
||||
isDynamicWorkflow(),
|
||||
repositoryProperties,
|
||||
logger,
|
||||
);
|
||||
effectiveToolsInput = resolvedToolsInput.effectiveToolsInput;
|
||||
effectiveToolsInputSource = resolvedToolsInput.effectiveToolsInputSource;
|
||||
toolsRepoPropertyMode = resolvedToolsInput.toolsRepoPropertyMode;
|
||||
const rawLanguages = configUtils.getRawLanguagesNoAutodetect(
|
||||
getOptionalInput("languages"),
|
||||
);
|
||||
@@ -329,7 +307,7 @@ async function run(startedAt: Date) {
|
||||
analysisKinds?.length === 1 &&
|
||||
analysisKinds[0] === AnalysisKind.CodeScanning;
|
||||
const initCodeQLResult = await initCodeQL(
|
||||
effectiveToolsInput,
|
||||
getOptionalInput("tools"),
|
||||
apiDetails,
|
||||
getTemporaryDirectory(),
|
||||
gitHubVersion.type,
|
||||
@@ -799,9 +777,6 @@ async function run(startedAt: Date) {
|
||||
toolsFeatureFlagsValid,
|
||||
toolsSource,
|
||||
toolsVersion,
|
||||
effectiveToolsInput,
|
||||
effectiveToolsInputSource,
|
||||
toolsRepoPropertyMode,
|
||||
overlayBaseDatabaseStats,
|
||||
dependencyCachingStatus,
|
||||
logger,
|
||||
@@ -819,15 +794,44 @@ async function run(startedAt: Date) {
|
||||
toolsFeatureFlagsValid,
|
||||
toolsSource,
|
||||
toolsVersion,
|
||||
effectiveToolsInput,
|
||||
effectiveToolsInputSource,
|
||||
toolsRepoPropertyMode,
|
||||
overlayBaseDatabaseStats,
|
||||
dependencyCachingStatus,
|
||||
logger,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads [repository properties](https://docs.github.com/en/organizations/managing-organization-settings/managing-custom-properties-for-repositories-in-your-organization) if applicable.
|
||||
*/
|
||||
async function loadRepositoryProperties(
|
||||
repositoryNwo: RepositoryNwo,
|
||||
logger: Logger,
|
||||
): Promise<Result<RepositoryProperties, unknown>> {
|
||||
// See if we can skip loading repository properties early. In particular,
|
||||
// repositories owned by users cannot have repository properties, so we can
|
||||
// skip the API call entirely in that case.
|
||||
const repositoryOwnerType = github.context.payload.repository?.owner.type;
|
||||
logger.debug(
|
||||
`Repository owner type is '${repositoryOwnerType ?? "unknown"}'.`,
|
||||
);
|
||||
if (repositoryOwnerType === "User") {
|
||||
logger.debug(
|
||||
"Skipping loading repository properties because the repository is owned by a user and " +
|
||||
"therefore cannot have repository properties.",
|
||||
);
|
||||
return new Success({});
|
||||
}
|
||||
|
||||
try {
|
||||
return new Success(await loadPropertiesFromApi(logger, repositoryNwo));
|
||||
} catch (error) {
|
||||
logger.warning(
|
||||
`Failed to load repository properties: ${getErrorMessage(error)}`,
|
||||
);
|
||||
return new Failure(error);
|
||||
}
|
||||
}
|
||||
|
||||
async function recordZstdAvailability(
|
||||
config: configUtils.Config,
|
||||
zstdAvailability: ZstdAvailability,
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ import {
|
||||
getFileCoverageInformationEnabled,
|
||||
logFileCoverageOnPrsDeprecationWarning,
|
||||
} from "./init";
|
||||
import { BuiltInLanguage } from "./languages/index";
|
||||
import { BuiltInLanguage } from "./languages";
|
||||
import {
|
||||
createFeatures,
|
||||
LoggedMessage,
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ import {
|
||||
RepositoryProperties,
|
||||
RepositoryPropertyName,
|
||||
} from "./feature-flags/properties";
|
||||
import { BuiltInLanguage, Language } from "./languages/index";
|
||||
import { BuiltInLanguage, Language } from "./languages";
|
||||
import { Logger, withGroupAsync } from "./logging";
|
||||
import { ToolsSource } from "./setup-codeql";
|
||||
import { ZstdAvailability } from "./tar";
|
||||
|
||||
@@ -2,7 +2,6 @@ import * as core from "@actions/core";
|
||||
import { v4 as uuidV4 } from "uuid";
|
||||
|
||||
import {
|
||||
isDynamicWorkflow,
|
||||
getActionVersion,
|
||||
getOptionalInput,
|
||||
getRequiredInput,
|
||||
@@ -11,17 +10,9 @@ import {
|
||||
import { AnalysisKind, getAnalysisKinds } from "./analyses";
|
||||
import { getGitHubVersion } from "./api-client";
|
||||
import { CodeQL } from "./codeql";
|
||||
import {
|
||||
EffectiveToolsInputSource,
|
||||
resolveToolsInputWithMetadata,
|
||||
} from "./config/resolve-tools-input";
|
||||
import { getRawLanguagesNoAutodetect } from "./config-utils";
|
||||
import { EnvVar } from "./environment";
|
||||
import { initFeatures } from "./feature-flags";
|
||||
import {
|
||||
loadRepositoryProperties,
|
||||
ToolsModeRepositoryPropertyValue,
|
||||
} from "./feature-flags/properties";
|
||||
import { initCodeQL } from "./init";
|
||||
import { getActionsLogger, Logger } from "./logging";
|
||||
import { getRepositoryNwo } from "./repository";
|
||||
@@ -57,9 +48,6 @@ async function sendCompletedStatusReport(
|
||||
toolsFeatureFlagsValid: boolean | undefined,
|
||||
toolsSource: ToolsSource,
|
||||
toolsVersion: string,
|
||||
effectiveToolsInput: string | undefined,
|
||||
effectiveToolsInputSource: EffectiveToolsInputSource,
|
||||
toolsRepoPropertyMode: ToolsModeRepositoryPropertyValue | undefined,
|
||||
logger: Logger,
|
||||
error?: Error,
|
||||
): Promise<void> {
|
||||
@@ -81,9 +69,6 @@ async function sendCompletedStatusReport(
|
||||
const initStatusReport: InitStatusReport = {
|
||||
...statusReportBase,
|
||||
tools_input: getOptionalInput("tools") || "",
|
||||
effective_tools_input: effectiveToolsInput || "",
|
||||
effective_tools_input_source: effectiveToolsInputSource,
|
||||
tools_repo_property_mode: toolsRepoPropertyMode || "",
|
||||
tools_resolved_version: toolsVersion,
|
||||
tools_source: toolsSource || ToolsSource.Unknown,
|
||||
workflow_languages: "",
|
||||
@@ -114,9 +99,6 @@ async function run(startedAt: Date): Promise<void> {
|
||||
let toolsFeatureFlagsValid: boolean | undefined;
|
||||
let toolsSource: ToolsSource;
|
||||
let toolsVersion: string;
|
||||
let effectiveToolsInput: string | undefined;
|
||||
let effectiveToolsInputSource: EffectiveToolsInputSource;
|
||||
let toolsRepoPropertyMode: ToolsModeRepositoryPropertyValue | undefined;
|
||||
|
||||
try {
|
||||
initializeEnvironment(getActionVersion());
|
||||
@@ -159,35 +141,12 @@ async function run(startedAt: Date): Promise<void> {
|
||||
const codeQLDefaultVersionInfo =
|
||||
await features.getEnabledDefaultCliVersions(gitHubVersion.type);
|
||||
toolsFeatureFlagsValid = codeQLDefaultVersionInfo.toolsFeatureFlagsValid;
|
||||
|
||||
// Fetch the values of known repository properties that affect us.
|
||||
const repositoryPropertiesResult = await loadRepositoryProperties(
|
||||
repositoryNwo,
|
||||
logger,
|
||||
);
|
||||
const repositoryProperties = repositoryPropertiesResult.orElse({});
|
||||
|
||||
// Determine the effective tools input.
|
||||
// The explicit `tools` workflow input takes precedence. If none is provided,
|
||||
// fall back to the 'github-codeql-tools' repository property (if set).
|
||||
// If 'github-codeql-tools-mode' is set to 'dynamic', this fallback applies
|
||||
// only to dynamic workflows. Otherwise, it applies to all workflows.
|
||||
const resolvedToolsInput = resolveToolsInputWithMetadata(
|
||||
getOptionalInput("tools"),
|
||||
isDynamicWorkflow(),
|
||||
repositoryProperties,
|
||||
logger,
|
||||
);
|
||||
effectiveToolsInput = resolvedToolsInput.effectiveToolsInput;
|
||||
effectiveToolsInputSource = resolvedToolsInput.effectiveToolsInputSource;
|
||||
toolsRepoPropertyMode = resolvedToolsInput.toolsRepoPropertyMode;
|
||||
const rawLanguages = getRawLanguagesNoAutodetect(
|
||||
getOptionalInput("languages"),
|
||||
);
|
||||
const analysisKinds = await getAnalysisKinds(logger, features);
|
||||
|
||||
const initCodeQLResult = await initCodeQL(
|
||||
effectiveToolsInput,
|
||||
getOptionalInput("tools"),
|
||||
apiDetails,
|
||||
getTemporaryDirectory(),
|
||||
gitHubVersion.type,
|
||||
@@ -232,9 +191,6 @@ async function run(startedAt: Date): Promise<void> {
|
||||
toolsFeatureFlagsValid,
|
||||
toolsSource,
|
||||
toolsVersion,
|
||||
effectiveToolsInput,
|
||||
effectiveToolsInputSource,
|
||||
toolsRepoPropertyMode,
|
||||
logger,
|
||||
);
|
||||
}
|
||||
|
||||
+1
-1
@@ -559,7 +559,7 @@ export async function getCodeQLSource(
|
||||
);
|
||||
} else {
|
||||
if (allowToolcacheValueFF) {
|
||||
logger.info(
|
||||
logger.warning(
|
||||
`Ignoring 'tools: ${toolsInput}' because the workflow was not triggered dynamically.`,
|
||||
);
|
||||
} else {
|
||||
|
||||
@@ -5,8 +5,8 @@ import * as core from "@actions/core";
|
||||
|
||||
import * as actionsUtil from "./actions-util";
|
||||
import { getGitHubVersion } from "./api-client";
|
||||
import { Feature, FeatureEnablement, initFeatures } from "./feature-flags";
|
||||
import { BuiltInLanguage, parseBuiltInLanguage } from "./languages/index";
|
||||
import { FeatureEnablement, initFeatures } from "./feature-flags";
|
||||
import { BuiltInLanguage, parseBuiltInLanguage } from "./languages";
|
||||
import { getActionsLogger, Logger } from "./logging";
|
||||
import { getRepositoryNwo } from "./repository";
|
||||
import {
|
||||
@@ -57,18 +57,12 @@ async function run(startedAt: Date) {
|
||||
const languageInput = actionsUtil.getOptionalInput("language");
|
||||
language = languageInput ? parseBuiltInLanguage(languageInput) : undefined;
|
||||
|
||||
// Query the FF for whether we should use the reduced registry mapping.
|
||||
const skipUnusedRegistries = await features.getValue(
|
||||
Feature.StartProxyRemoveUnusedRegistries,
|
||||
);
|
||||
|
||||
// Get the registry configurations from one of the inputs.
|
||||
const credentials = getCredentials(
|
||||
logger,
|
||||
actionsUtil.getOptionalInput("registry_secrets"),
|
||||
actionsUtil.getOptionalInput("registries_credentials"),
|
||||
language,
|
||||
skipUnusedRegistries,
|
||||
);
|
||||
|
||||
if (credentials.length === 0) {
|
||||
|
||||
+5
-23
@@ -10,7 +10,7 @@ import * as defaults from "./defaults.json";
|
||||
import { setUpFeatureFlagTests } from "./feature-flags/testing-util";
|
||||
import { UnvalidatedObject, validateSchema } from "./json";
|
||||
import { makeFromSchema } from "./json/testing-util";
|
||||
import { BuiltInLanguage } from "./languages/index";
|
||||
import { BuiltInLanguage } from "./languages";
|
||||
import { getRunnerLogger, Logger } from "./logging";
|
||||
import * as startProxyExports from "./start-proxy";
|
||||
import * as statusReport from "./status-report";
|
||||
@@ -585,7 +585,6 @@ test("getCredentials validates 'replaces-base' correctly", async (t) => {
|
||||
undefined,
|
||||
credentialsInput,
|
||||
BuiltInLanguage.java,
|
||||
false,
|
||||
);
|
||||
|
||||
t.is(credentials.length, 3);
|
||||
@@ -604,8 +603,7 @@ test("getCredentials validates 'replaces-base' correctly", async (t) => {
|
||||
getRunnerLogger(true),
|
||||
undefined,
|
||||
toEncodedJSON([{ ...baseInvalid, "replaces-base": null }]),
|
||||
BuiltInLanguage.actions,
|
||||
false,
|
||||
BuiltInLanguage.java,
|
||||
),
|
||||
);
|
||||
t.throws(() =>
|
||||
@@ -613,8 +611,7 @@ test("getCredentials validates 'replaces-base' correctly", async (t) => {
|
||||
getRunnerLogger(true),
|
||||
undefined,
|
||||
toEncodedJSON([{ ...baseInvalid, "replaces-base": 123 }]),
|
||||
BuiltInLanguage.actions,
|
||||
false,
|
||||
BuiltInLanguage.java,
|
||||
),
|
||||
);
|
||||
t.throws(() =>
|
||||
@@ -622,13 +619,12 @@ test("getCredentials validates 'replaces-base' correctly", async (t) => {
|
||||
getRunnerLogger(true),
|
||||
undefined,
|
||||
toEncodedJSON([{ ...baseInvalid, "replaces-base": "true" }]),
|
||||
BuiltInLanguage.actions,
|
||||
false,
|
||||
BuiltInLanguage.java,
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
test("getCredentials returns all credentials for Actions when using LANGUAGE_TO_REGISTRY_TYPE", async (t) => {
|
||||
test("getCredentials returns no credentials for Actions", async (t) => {
|
||||
const credentialsInput = toEncodedJSON(mixedCredentials);
|
||||
|
||||
const credentials = startProxyExports.getCredentials(
|
||||
@@ -636,20 +632,6 @@ test("getCredentials returns all credentials for Actions when using LANGUAGE_TO_
|
||||
undefined,
|
||||
credentialsInput,
|
||||
BuiltInLanguage.actions,
|
||||
false,
|
||||
);
|
||||
t.is(credentials.length, mixedCredentials.length);
|
||||
});
|
||||
|
||||
test("getCredentials returns no credentials for Actions when using NEW_LANGUAGE_TO_REGISTRY_TYPE", async (t) => {
|
||||
const credentialsInput = toEncodedJSON(mixedCredentials);
|
||||
|
||||
const credentials = startProxyExports.getCredentials(
|
||||
getRunnerLogger(true),
|
||||
undefined,
|
||||
credentialsInput,
|
||||
BuiltInLanguage.actions,
|
||||
true,
|
||||
);
|
||||
t.deepEqual(credentials, []);
|
||||
});
|
||||
|
||||
+3
-17
@@ -18,7 +18,7 @@ import {
|
||||
FeatureEnablement,
|
||||
} from "./feature-flags";
|
||||
import * as json from "./json";
|
||||
import { BuiltInLanguage } from "./languages/index";
|
||||
import { BuiltInLanguage } from "./languages";
|
||||
import { Logger } from "./logging";
|
||||
import {
|
||||
Address,
|
||||
@@ -189,17 +189,7 @@ function isPAT(value: string) {
|
||||
|
||||
type RegistryMapping = Partial<Record<BuiltInLanguage, string[]>>;
|
||||
|
||||
const LANGUAGE_TO_REGISTRY_TYPE: RegistryMapping = {
|
||||
java: ["maven_repository"],
|
||||
csharp: ["nuget_feed"],
|
||||
javascript: ["npm_registry"],
|
||||
python: ["python_index"],
|
||||
ruby: ["rubygems_server"],
|
||||
rust: ["cargo_registry"],
|
||||
go: ["goproxy_server", "git_source"],
|
||||
} as const;
|
||||
|
||||
const NEW_LANGUAGE_TO_REGISTRY_TYPE: Required<RegistryMapping> = {
|
||||
const LANGUAGE_TO_REGISTRY_TYPE: Required<RegistryMapping> = {
|
||||
actions: [],
|
||||
cpp: [],
|
||||
java: ["maven_repository"],
|
||||
@@ -251,13 +241,9 @@ export function getCredentials(
|
||||
registrySecrets: string | undefined,
|
||||
registriesCredentials: string | undefined,
|
||||
language: BuiltInLanguage | undefined,
|
||||
skipUnusedRegistries: boolean = false,
|
||||
): Credential[] {
|
||||
const registryMapping = skipUnusedRegistries
|
||||
? NEW_LANGUAGE_TO_REGISTRY_TYPE
|
||||
: LANGUAGE_TO_REGISTRY_TYPE;
|
||||
const registryTypeForLanguage = language
|
||||
? registryMapping[language]
|
||||
? LANGUAGE_TO_REGISTRY_TYPE[language]
|
||||
: undefined;
|
||||
|
||||
let credentialsStr: string;
|
||||
|
||||
@@ -2,10 +2,9 @@ import test from "ava";
|
||||
import * as sinon from "sinon";
|
||||
|
||||
import * as actionsUtil from "./actions-util";
|
||||
import { EffectiveToolsInputSource } from "./config/resolve-tools-input";
|
||||
import { Config } from "./config-utils";
|
||||
import { EnvVar } from "./environment";
|
||||
import { BuiltInLanguage } from "./languages/index";
|
||||
import { BuiltInLanguage } from "./languages";
|
||||
import { getRunnerLogger } from "./logging";
|
||||
import { ToolsSource } from "./setup-codeql";
|
||||
import {
|
||||
@@ -317,9 +316,6 @@ const testCreateInitWithConfigStatusReport = makeMacro({
|
||||
const initStatusReport: InitStatusReport = {
|
||||
...statusReportBase,
|
||||
tools_input: "",
|
||||
effective_tools_input: "",
|
||||
effective_tools_input_source: EffectiveToolsInputSource.None,
|
||||
tools_repo_property_mode: "",
|
||||
tools_resolved_version: "foo",
|
||||
tools_source: ToolsSource.Unknown,
|
||||
workflow_languages: "actions",
|
||||
@@ -351,8 +347,6 @@ testCreateInitWithConfigStatusReport.serial(
|
||||
languages: [BuiltInLanguage.java, BuiltInLanguage.swift],
|
||||
}),
|
||||
{
|
||||
effective_tools_input_source: EffectiveToolsInputSource.None,
|
||||
tools_repo_property_mode: "",
|
||||
trap_cache_download_size_bytes: 1024,
|
||||
registries: "[]",
|
||||
query_filters: "[]",
|
||||
@@ -360,63 +354,6 @@ testCreateInitWithConfigStatusReport.serial(
|
||||
},
|
||||
);
|
||||
|
||||
test.serial(
|
||||
"createInitWithConfigStatusReport preserves tools telemetry fields",
|
||||
async (t) => {
|
||||
await withTmpDir(async (tmpDir: string) => {
|
||||
setupEnvironmentAndStub(tmpDir);
|
||||
|
||||
const config = createTestConfig({
|
||||
buildMode: BuildMode.None,
|
||||
languages: [BuiltInLanguage.java],
|
||||
});
|
||||
|
||||
const statusReportBase = await createStatusReportBase(
|
||||
ActionName.Init,
|
||||
"failure",
|
||||
new Date("May 19, 2023 05:19:00"),
|
||||
config,
|
||||
{ numAvailableBytes: 100, numTotalBytes: 500 },
|
||||
getRunnerLogger(false),
|
||||
"failure cause",
|
||||
"exception stack trace",
|
||||
);
|
||||
|
||||
if (t.truthy(statusReportBase)) {
|
||||
const initStatusReport: InitStatusReport = {
|
||||
...statusReportBase,
|
||||
tools_input: "",
|
||||
effective_tools_input: "toolcache",
|
||||
effective_tools_input_source:
|
||||
EffectiveToolsInputSource.RepositoryProperty,
|
||||
tools_repo_property_mode: "dynamic",
|
||||
tools_resolved_version: "foo",
|
||||
tools_source: ToolsSource.Unknown,
|
||||
workflow_languages: "actions",
|
||||
};
|
||||
|
||||
const initWithConfigStatusReport =
|
||||
await createInitWithConfigStatusReport(
|
||||
config,
|
||||
initStatusReport,
|
||||
undefined,
|
||||
1024,
|
||||
undefined,
|
||||
undefined,
|
||||
);
|
||||
|
||||
if (t.truthy(initWithConfigStatusReport)) {
|
||||
t.is(
|
||||
initWithConfigStatusReport.effective_tools_input_source,
|
||||
EffectiveToolsInputSource.RepositoryProperty,
|
||||
);
|
||||
t.is(initWithConfigStatusReport.tools_repo_property_mode, "dynamic");
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
testCreateInitWithConfigStatusReport.serial(
|
||||
"includes packs for a single language",
|
||||
createTestConfig({
|
||||
|
||||
@@ -12,7 +12,6 @@ import {
|
||||
isSelfHostedRunner,
|
||||
} from "./actions-util";
|
||||
import { getAnalysisKey, getApiClient } from "./api-client";
|
||||
import { EffectiveToolsInputSource } from "./config/resolve-tools-input";
|
||||
import { parseRegistriesWithoutCredentials, type Config } from "./config-utils";
|
||||
import { DependencyCacheRestoreStatusReport } from "./dependency-caching";
|
||||
import { DocUrl } from "./doc-url";
|
||||
@@ -483,12 +482,6 @@ export async function sendStatusReport<S extends StatusReportBase>(
|
||||
export interface InitStatusReport extends StatusReportBase {
|
||||
/** Value given by the user as the "tools" input. */
|
||||
tools_input: string;
|
||||
/** The effective tools input that was used, after applying defaults and repository properties. */
|
||||
effective_tools_input: string;
|
||||
/** Indicates where the effective tools input was resolved from. */
|
||||
effective_tools_input_source: EffectiveToolsInputSource;
|
||||
/** The value of the tools repository property mode, if relevant. */
|
||||
tools_repo_property_mode: string;
|
||||
/** Version of the bundle used. */
|
||||
tools_resolved_version: string;
|
||||
/** Where the bundle originated from. */
|
||||
|
||||
+10
-1
@@ -10,7 +10,7 @@ import test, {
|
||||
import nock from "nock";
|
||||
import * as sinon from "sinon";
|
||||
|
||||
import { getActionVersion } from "./actions-util";
|
||||
import { ActionsEnv, getActionVersion } from "./actions-util";
|
||||
import { AnalysisKind } from "./analyses";
|
||||
import * as apiClient from "./api-client";
|
||||
import { GitHubApiDetails } from "./api-client";
|
||||
@@ -172,6 +172,15 @@ export function makeMacro<Args extends unknown[]>(
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an `ActionsEnv` instance for use in tests.
|
||||
*/
|
||||
export function getTestActionsEnv(): ActionsEnv {
|
||||
return {
|
||||
getOptionalInput: () => undefined,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Default values for environment variables typically set in an Actions
|
||||
* environment. Tests can override individual variables by passing them in the
|
||||
|
||||
@@ -6,7 +6,7 @@ import * as sinon from "sinon";
|
||||
|
||||
import { CodeQL, getCodeQLForTesting } from "./codeql";
|
||||
import * as configUtils from "./config-utils";
|
||||
import { BuiltInLanguage } from "./languages/index";
|
||||
import { BuiltInLanguage } from "./languages";
|
||||
import { createTestConfig, makeVersionInfo, setupTests } from "./testing-utils";
|
||||
import { ToolsFeature } from "./tools-features";
|
||||
import { getCombinedTracerConfig } from "./tracer-config";
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
import * as configUtils from "./config-utils";
|
||||
import { Feature } from "./feature-flags";
|
||||
import * as gitUtils from "./git-utils";
|
||||
import { BuiltInLanguage } from "./languages/index";
|
||||
import { BuiltInLanguage } from "./languages";
|
||||
import { getRunnerLogger } from "./logging";
|
||||
import {
|
||||
createFeatures,
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ import { type Config } from "./config-utils";
|
||||
import { DocUrl } from "./doc-url";
|
||||
import { Feature, FeatureEnablement } from "./feature-flags";
|
||||
import * as gitUtils from "./git-utils";
|
||||
import { Language } from "./languages/index";
|
||||
import { Language } from "./languages";
|
||||
import { Logger } from "./logging";
|
||||
import {
|
||||
asHTTPError,
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ import type { Pack } from "./config/db-config";
|
||||
import type { Config } from "./config-utils";
|
||||
import { EnvVar } from "./environment";
|
||||
import * as json from "./json";
|
||||
import { Language } from "./languages/index";
|
||||
import { Language } from "./languages";
|
||||
import { Logger } from "./logging";
|
||||
|
||||
/**
|
||||
|
||||
Reference in new issue
Block a user