-
Notifications
You must be signed in to change notification settings - Fork 315
feat: add version update check for compile-agentic activation #23575
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
c6a9a2f
11f139c
75c692d
dbd3916
ba800d8
e45dd40
2d03440
9849ac9
41796ed
3df3708
33997a7
04475fd
4c4d242
9363d66
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| { | ||
| "$schema": "./releases.schema.json", | ||
| "blockedVersions": [], | ||
| "minimumVersion": "v0.64.4", | ||
| "minRecommendedVersion": "v0.64.4" | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| { | ||
| "$schema": "http://json-schema.org/draft-07/schema#", | ||
| "title": "gh-aw update check configuration", | ||
| "description": "Configuration for the compile-agentic version update check. This file is fetched at runtime by the activation job to validate that the compiled version is still supported.", | ||
| "type": "object", | ||
| "additionalProperties": false, | ||
| "properties": { | ||
| "$schema": { | ||
| "type": "string", | ||
| "description": "JSON Schema reference" | ||
| }, | ||
| "blockedVersions": { | ||
| "type": "array", | ||
| "description": "List of blocked compile-agentic versions that are not allowed to run (e.g. due to a security compromise). Workflows compiled with a blocked version will fail at activation.", | ||
| "items": { | ||
| "type": "string", | ||
| "pattern": "^v[0-9]+\\.[0-9]+\\.[0-9]+$", | ||
| "description": "A blocked version string in vMAJOR.MINOR.PATCH format (e.g. 'v1.2.3')" | ||
| }, | ||
| "uniqueItems": true, | ||
| "default": [] | ||
| }, | ||
| "minimumVersion": { | ||
| "type": "string", | ||
| "description": "The minimum supported compile-agentic version in vMAJOR.MINOR.PATCH format. Workflows compiled with a version below this will fail at activation. Use an empty string to disable this check.", | ||
| "pattern": "^(v[0-9]+\\.[0-9]+\\.[0-9]+)?$", | ||
| "default": "" | ||
| }, | ||
| "minRecommendedVersion": { | ||
| "type": "string", | ||
| "description": "The minimum recommended compile-agentic version in vMAJOR.MINOR.PATCH format. Workflows compiled with a version below this will emit a warning (but not fail) at activation, nudging users to upgrade. Use an empty string to disable this check.", | ||
| "pattern": "^(v[0-9]+\\.[0-9]+\\.[0-9]+)?$", | ||
| "default": "" | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,158 @@ | ||
| // @ts-check | ||
| /// <reference types="@actions/github-script" /> | ||
|
|
||
| /** | ||
| * Check compile-agentic version against the remote update configuration. | ||
| * | ||
| * This script: | ||
| * 1. Reads the compiled version from GH_AW_COMPILED_VERSION env var. | ||
| * 2. Skips the check if the version is not in vMAJOR.MINOR.PATCH official release format. | ||
| * 3. Fetches .github/aw/releases.json from the gh-aw repository via raw.githubusercontent.com. | ||
| * - Uses withRetry to handle transient network failures. | ||
| * 4. If the download fails or config is invalid JSON, the check is skipped (soft failure). | ||
| * 5. Validates that the compiled version is not in the blocked list. | ||
| * 6. Validates that the compiled version meets the minimum supported version. | ||
| * | ||
| * Fails the activation job when validation fails. | ||
| */ | ||
|
|
||
| const { withRetry, isTransientError } = require("./error_recovery.cjs"); | ||
|
|
||
| const CONFIG_URL = "https://raw.githubusercontent.com/github/gh-aw/main/.github/aw/releases.json"; | ||
|
|
||
| /** | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @copilot do not handle both format, if the version is not vmajor.minor.patch, it should NOT be treated as an official release and ignore.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done in 9849ac9 — removed |
||
| * Parse an official version string (must be in vMAJOR.MINOR.PATCH format). | ||
| * Versions without a leading "v" are not treated as official releases and return null. | ||
| * Versions with unknown syntax also return null. | ||
| * | ||
| * @param {string} version | ||
| * @returns {number[]|null} | ||
| */ | ||
| function parseVersion(version) { | ||
| if (!version.startsWith("v")) return null; | ||
| const parts = version.slice(1).split("."); | ||
| if (parts.length !== 3) return null; | ||
| const nums = parts.map(Number); | ||
| if (nums.some(isNaN)) return null; | ||
| return nums; | ||
|
Comment on lines
+31
to
+37
|
||
| } | ||
|
|
||
| /** | ||
| * Compare two official version strings (both must be in vMAJOR.MINOR.PATCH format). | ||
| * Returns a negative number if a < b, 0 if equal, positive if a > b. | ||
| * Returns 0 (treat as equal/unknown) if either version cannot be parsed. | ||
| * | ||
| * @param {string} a | ||
| * @param {string} b | ||
| * @returns {number} | ||
| */ | ||
| function compareVersions(a, b) { | ||
| const pa = parseVersion(a); | ||
| const pb = parseVersion(b); | ||
| if (!pa || !pb) return 0; | ||
| for (let i = 0; i < 3; i++) { | ||
| if (pa[i] !== pb[i]) return pa[i] - pb[i]; | ||
| } | ||
| return 0; | ||
| } | ||
|
|
||
| /** | ||
| * @typedef {object} UpdateConfig | ||
| * @property {string[]} [blockedVersions] | ||
| * @property {string} [minimumVersion] | ||
| * @property {string} [minRecommendedVersion] | ||
| */ | ||
|
|
||
| /** | ||
| * Main entry point. | ||
| */ | ||
| async function main() { | ||
| const compiledVersion = process.env.GH_AW_COMPILED_VERSION || ""; | ||
|
|
||
| if (!compiledVersion || compiledVersion === "dev") { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @copilot ignore unknown versions, or unknown syntax
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done in 9849ac9 — unknown/non-parseable versions are now ignored at every point: compiled version with unknown syntax skips the check early; blocked version entries with unknown syntax are filtered out; a |
||
| core.info(`Skipping version update check: version is '${compiledVersion || "(empty)"}' (dev build)`); | ||
| return; | ||
| } | ||
|
|
||
| // Only check official releases in vMAJOR.MINOR.PATCH format; ignore unknown syntax | ||
| if (!parseVersion(compiledVersion)) { | ||
| core.info(`Skipping version update check: '${compiledVersion}' is not an official release version (expected vMAJOR.MINOR.PATCH format)`); | ||
| return; | ||
| } | ||
|
|
||
| core.info(`Checking compile-agentic version: ${compiledVersion}`); | ||
| core.info(`Fetching update configuration from: ${CONFIG_URL}`); | ||
|
|
||
| /** @type {UpdateConfig} */ | ||
| let config; | ||
| try { | ||
| config = await withRetry( | ||
| async () => { | ||
| const res = await fetch(CONFIG_URL); | ||
| if (!res.ok) { | ||
| const err = new Error(`HTTP ${res.status} fetching ${CONFIG_URL}`); | ||
| // @ts-ignore - Attach status so the retry predicate can inspect it | ||
| err.status = res.status; | ||
| throw err; | ||
| } | ||
|
Comment on lines
+89
to
+97
|
||
| const parsed = JSON.parse(await res.text()); | ||
| // Guard: JSON.parse("null") returns null; treat non-object/null/array as empty config | ||
| return parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {}; | ||
| }, | ||
| { | ||
| shouldRetry: err => | ||
| isTransientError(err) || | ||
| // Retry on any HTTP 5xx response (server errors) | ||
| (err !== null && typeof err === "object" && "status" in err && Number(err.status) >= 500), | ||
| }, | ||
| "fetch update configuration" | ||
| ); | ||
| } catch (err) { | ||
| const message = err instanceof Error ? err.message : String(err); | ||
| core.info(`Could not fetch update configuration (${message}). Skipping version check.`); | ||
| return; | ||
| } | ||
|
|
||
| const blockedVersions = Array.isArray(config.blockedVersions) ? config.blockedVersions : []; | ||
| const minimumVersion = typeof config.minimumVersion === "string" ? config.minimumVersion : ""; | ||
| const minRecommendedVersion = typeof config.minRecommendedVersion === "string" ? config.minRecommendedVersion : ""; | ||
|
|
||
| // Check blocked versions — only consider entries in vMAJOR.MINOR.PATCH format; ignore unknown syntax | ||
| const isBlocked = blockedVersions.some(v => parseVersion(v) !== null && compareVersions(compiledVersion, v) === 0); | ||
| if (isBlocked) { | ||
| core.summary | ||
| .addRaw("### ❌ Blocked compile-agentic version\n\n") | ||
| .addRaw(`The compile-agentic version \`${compiledVersion}\` is **blocked** and cannot be used to run workflows.\n\n`) | ||
| .addRaw("This version has been revoked, typically due to a security issue.\n\n") | ||
| .addRaw("**Action required:** Update `gh-aw` to the latest version and recompile your workflow with `gh aw compile`.\n"); | ||
| await core.summary.write(); | ||
| core.setFailed(`Blocked compile-agentic version: ${compiledVersion} is in the blocked versions list. Update gh-aw to the latest version and recompile your workflow.`); | ||
| return; | ||
| } | ||
|
|
||
| // Check minimum version — skip if minimumVersion is absent, empty, or has unknown syntax | ||
| if (minimumVersion && parseVersion(minimumVersion) !== null) { | ||
| if (compareVersions(compiledVersion, minimumVersion) < 0) { | ||
| core.summary | ||
| .addRaw("### ❌ Outdated compile-agentic version\n\n") | ||
| .addRaw(`The compile-agentic version \`${compiledVersion}\` is below the minimum supported version \`${minimumVersion}\`.\n\n`) | ||
| .addRaw("**Action required:** Update `gh-aw` to the latest version and recompile your workflow with `gh aw compile`.\n"); | ||
| await core.summary.write(); | ||
| core.setFailed(`Outdated compile-agentic version: ${compiledVersion} is below the minimum supported version ${minimumVersion}. Update gh-aw to the latest version and recompile your workflow.`); | ||
| return; | ||
| } | ||
| } | ||
|
|
||
| // Check recommended version — skip if minRecommendedVersion is absent, empty, or has unknown syntax | ||
| if (minRecommendedVersion && parseVersion(minRecommendedVersion) !== null) { | ||
| if (compareVersions(compiledVersion, minRecommendedVersion) < 0) { | ||
| core.warning( | ||
| `Recommended upgrade: compile-agentic version ${compiledVersion} is below the recommended version ${minRecommendedVersion}. Consider updating gh-aw to the latest version and recompiling your workflow with \`gh aw compile\`.` | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| core.info(`✅ Version check passed: ${compiledVersion}`); | ||
| } | ||
|
|
||
| module.exports = { main }; | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@copilot rename this file to "releases.json" and rename json schema accordingly. Update all references.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in 04475fd — renamed
config.json→releases.jsonandconfig.schema.json→releases.schema.json. Updated the$schemareference insidereleases.json, theraw.githubusercontent.comURL incheck_version_updates.cjs, the path triggers and validation step inci.yml, and the comment incompiler_activation_job.go.