diff --git a/.github/workflows/test-suite-e2e-tests.yml b/.github/workflows/test-suite-e2e-tests.yml index 7a8942d845..de5eb3c156 100644 --- a/.github/workflows/test-suite-e2e-tests.yml +++ b/.github/workflows/test-suite-e2e-tests.yml @@ -151,6 +151,7 @@ jobs: RELAYER_MIGRATE_VERSION: ${{ inputs.relayer-migrate-version }} RELAYER_VERSION: ${{ inputs.relayer-version }} CORE_VERSION: ${{ inputs.kms-core-version }} + ORCHESTRATED: ${{ inputs.orchestrated && 'true' || 'false' }} SCENARIO: ${{ inputs.scenario || 'two-of-two' }} TEST_PROFILE: ${{ inputs.test-profile || 'standard' }} runs-on: large_ubuntu_32 @@ -267,6 +268,12 @@ jobs: run: | echo "COMPAT_OVERRIDES=test-suite" >> "$GITHUB_ENV" + - name: Require frozen baseline lock for orchestrated runs + if: ${{ inputs.compat-test == '' && inputs.orchestrated && inputs.lock-artifact-name == '' && github.event_name != 'workflow_dispatch' }} + run: | + echo "Orchestrated e2e requires a frozen baseline lock artifact" >&2 + exit 1 + - name: Resolve latest-main lock once if: ${{ inputs.compat-test == '' && inputs.lock-artifact-name == '' && !inputs.orchestrated }} working-directory: test-suite/fhevm @@ -278,12 +285,26 @@ jobs: fi echo "LOCK_FILE=$lock_file" >> "$GITHUB_ENV" + - name: Resolve orchestrated manual baseline lock once + if: ${{ inputs.compat-test == '' && inputs.lock-artifact-name == '' && inputs.orchestrated && github.event_name == 'workflow_dispatch' }} + working-directory: test-suite/fhevm + run: | + lock_file="$(./fhevm-cli resolve --target latest-main | tail -n1)" + if [ ! -f "$lock_file" ]; then + echo "Could not locate resolved lock file: $lock_file" >&2 + exit 1 + fi + echo "LOCK_FILE=$lock_file" >> "$GITHUB_ENV" + - name: Resolve stack plan and display effective component versions working-directory: test-suite/fhevm run: | args=(--scenario "$SCENARIO") if [ -n "${LOCK_FILE:-}" ]; then args+=(--lock-file "$LOCK_FILE") + elif [ "$ORCHESTRATED" = "true" ]; then + echo "Orchestrated e2e requires a frozen baseline lock artifact" >&2 + exit 1 else args+=(--target latest-main) fi @@ -304,6 +325,9 @@ jobs: args=(--scenario "$SCENARIO") if [ -n "${LOCK_FILE:-}" ]; then args+=(--lock-file "$LOCK_FILE") + elif [ "$ORCHESTRATED" = "true" ]; then + echo "Orchestrated e2e requires a frozen baseline lock artifact" >&2 + exit 1 else args+=(--target latest-main) fi diff --git a/.github/workflows/test-suite-orchestrate-e2e-tests.yml b/.github/workflows/test-suite-orchestrate-e2e-tests.yml index ba64b7c78f..e467084431 100644 --- a/.github/workflows/test-suite-orchestrate-e2e-tests.yml +++ b/.github/workflows/test-suite-orchestrate-e2e-tests.yml @@ -14,9 +14,48 @@ concurrency: cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} jobs: - coprocessor-docker-build: + resolve-baseline: + name: resolve-baseline if: &build-trigger-condition | startsWith(github.head_ref, 'mergify/merge-queue/') || startsWith(github.base_ref, 'release/') || contains(github.event.pull_request.labels.*.name, 'e2e orchestrate test') + runs-on: ubuntu-latest + permissions: + contents: 'read' # Required to checkout repository code + env: + BASE_SHA: ${{ github.event.pull_request.base.sha }} + outputs: + lock-artifact-name: ${{ steps.resolve-baseline.outputs.lock-artifact-name }} + steps: + - name: Checkout code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: 'false' + + - name: Setup Bun + uses: oven-sh/setup-bun@ecf28ddc73e819eb6fa29df6b34ef8921c743461 # v2.1.3 + + - name: Install CLI deps + working-directory: test-suite/fhevm + run: bun install --frozen-lockfile + + - id: resolve-baseline + name: Resolve base-sha baseline lock + working-directory: test-suite/fhevm + run: | + set -euo pipefail + lock_path="$(./fhevm-cli resolve --target sha --sha "$BASE_SHA" --reset | tail -n 1)" + artifact_name="fhevm-base-lock-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}" + echo "lock-path=$lock_path" >> "$GITHUB_OUTPUT" + echo "lock-artifact-name=$artifact_name" >> "$GITHUB_OUTPUT" + + - name: Upload baseline lock + uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + with: + name: ${{ steps.resolve-baseline.outputs.lock-artifact-name }} + path: ${{ steps.resolve-baseline.outputs.lock-path }} + + coprocessor-docker-build: + if: *build-trigger-condition uses: ./.github/workflows/coprocessor-docker-build.yml permissions: &docker_permissions actions: 'read' # Required to read workflow run information @@ -61,6 +100,7 @@ jobs: create-e2e-tests-input: name: create-e2e-tests-input needs: + - resolve-baseline - coprocessor-docker-build - gateway-contracts-docker-build - host-contracts-docker-build @@ -68,12 +108,8 @@ jobs: - relayer-docker-build - test-suite-docker-build if: ${{ (success() || failure()) && (startsWith(github.head_ref, 'mergify/merge-queue/') || startsWith(github.base_ref, 'release/') || contains(github.event.pull_request.labels.*.name, 'e2e orchestrate test')) }} - permissions: - contents: 'read' # Required to read the checked-in release baseline defaults env: - BASE_COMMIT_HASH: ${{ github.event.pull_request.base.sha }} NEW_COMMIT_HASH: ${{ github.event.pull_request.head.sha }} - IS_RELEASE_PR: ${{ startsWith(github.base_ref, 'release/') }} DOCKER_BUILD_RESULTS: ${{ toJSON(needs) }} runs-on: ubuntu-latest outputs: @@ -91,99 +127,50 @@ jobs: gateway-version: ${{ steps.create-e2e-tests-input.outputs.gateway-version }} host-version: ${{ steps.create-e2e-tests-input.outputs.host-version }} kms-core-version: ${{ steps.create-e2e-tests-input.outputs.kms-core-version }} + lock-artifact-name: ${{ needs.resolve-baseline.outputs.lock-artifact-name }} relayer-migrate-version: ${{ steps.create-e2e-tests-input.outputs.relayer-migrate-version }} relayer-version: ${{ steps.create-e2e-tests-input.outputs.relayer-version }} test-suite-version: ${{ steps.create-e2e-tests-input.outputs.test-suite-version }} steps: - - name: Checkout code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - persist-credentials: 'false' - fetch-depth: 0 - - id: create-e2e-tests-input - run: | - set -euo pipefail - short_tag="$(git rev-parse --short=7 "$NEW_COMMIT_HASH")" - base_short_tag="$(git rev-parse --short=7 "$BASE_COMMIT_HASH")" - needs_file="$RUNNER_TEMP/docker-build-results.json" - printf '%s' "$DOCKER_BUILD_RESULTS" > "$needs_file" - - outputs=( - coprocessor-db-migration-version - coprocessor-gw-listener-version - coprocessor-host-listener-version - coprocessor-sns-worker-version - coprocessor-tfhe-worker-version - coprocessor-tx-sender-version - coprocessor-zkproof-worker-version - connector-db-migration-version - connector-gw-listener-version - connector-kms-worker-version - connector-tx-sender-version - gateway-version - host-version - kms-core-version - relayer-migrate-version - relayer-version - test-suite-version - ) - for key in "${outputs[@]}"; do - echo "$key=" >> "$GITHUB_OUTPUT" - done - - if [ "$IS_RELEASE_PR" = "true" ]; then - if git cat-file -e "${BASE_COMMIT_HASH}:test-suite/fhevm/profiles/latest-supported.json" 2>/dev/null; then - core_version="$(git show "${BASE_COMMIT_HASH}:test-suite/fhevm/profiles/latest-supported.json" | python3 -c 'import json,sys; print(json.load(sys.stdin)["env"]["CORE_VERSION"])')" - elif git cat-file -e "${BASE_COMMIT_HASH}:test-suite/fhevm/fhevm-cli" 2>/dev/null; then - core_version="$(git show "${BASE_COMMIT_HASH}:test-suite/fhevm/fhevm-cli" | sed -n 's/^export CORE_VERSION=.*:-"\\([^"]*\\)".*/\\1/p' | head -n 1)" - else - core_version="" - fi - if [ -z "$core_version" ]; then - echo "Could not extract CORE_VERSION from release base ${BASE_COMMIT_HASH}" >&2 - exit 1 - fi - echo "kms-core-version=$core_version" >> "$GITHUB_OUTPUT" - fi - - failures=() - while IFS='|' read -r job result_key output_key; do - result="$(jq -r --arg job "$job" --arg key "$result_key" '.[$job].outputs[$key] // "missing"' "$needs_file")" - case "$result" in - success) - echo "$output_key=$short_tag" >> "$GITHUB_OUTPUT" - ;; - skipped) - echo "$output_key=$base_short_tag" >> "$GITHUB_OUTPUT" - ;; - *) - failures+=("$job.$result_key=$result") - ;; - esac - done <<'EOF' - coprocessor-docker-build|db_migration_build_result|coprocessor-db-migration-version - coprocessor-docker-build|gw_listener_build_result|coprocessor-gw-listener-version - coprocessor-docker-build|host_listener_build_result|coprocessor-host-listener-version - coprocessor-docker-build|sns_worker_build_result|coprocessor-sns-worker-version - coprocessor-docker-build|tfhe_worker_build_result|coprocessor-tfhe-worker-version - coprocessor-docker-build|tx_sender_build_result|coprocessor-tx-sender-version - coprocessor-docker-build|zkproof_worker_build_result|coprocessor-zkproof-worker-version - kms-connector-docker-build|db_migration_build_result|connector-db-migration-version - kms-connector-docker-build|gw_listener_build_result|connector-gw-listener-version - kms-connector-docker-build|kms_worker_build_result|connector-kms-worker-version - kms-connector-docker-build|tx_sender_build_result|connector-tx-sender-version - gateway-contracts-docker-build|build_result|gateway-version - host-contracts-docker-build|build_result|host-version - relayer-docker-build|relayer_migrate_build_result|relayer-migrate-version - relayer-docker-build|relayer_build_result|relayer-version - test-suite-docker-build|build_result|test-suite-version - EOF + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v0.8.0 + with: + script: | + const headTag = process.env.NEW_COMMIT_HASH.slice(0, 7); + const buildResults = JSON.parse(process.env.DOCKER_BUILD_RESULTS); + core.setOutput("kms-core-version", ""); + const mappings = [ + ["coprocessor-docker-build", "db_migration_build_result", "coprocessor-db-migration-version"], + ["coprocessor-docker-build", "gw_listener_build_result", "coprocessor-gw-listener-version"], + ["coprocessor-docker-build", "host_listener_build_result", "coprocessor-host-listener-version"], + ["coprocessor-docker-build", "sns_worker_build_result", "coprocessor-sns-worker-version"], + ["coprocessor-docker-build", "tfhe_worker_build_result", "coprocessor-tfhe-worker-version"], + ["coprocessor-docker-build", "tx_sender_build_result", "coprocessor-tx-sender-version"], + ["coprocessor-docker-build", "zkproof_worker_build_result", "coprocessor-zkproof-worker-version"], + ["kms-connector-docker-build", "db_migration_build_result", "connector-db-migration-version"], + ["kms-connector-docker-build", "gw_listener_build_result", "connector-gw-listener-version"], + ["kms-connector-docker-build", "kms_worker_build_result", "connector-kms-worker-version"], + ["kms-connector-docker-build", "tx_sender_build_result", "connector-tx-sender-version"], + ["gateway-contracts-docker-build", "build_result", "gateway-version"], + ["host-contracts-docker-build", "build_result", "host-version"], + ["relayer-docker-build", "relayer_migrate_build_result", "relayer-migrate-version"], + ["relayer-docker-build", "relayer_build_result", "relayer-version"], + ["test-suite-docker-build", "build_result", "test-suite-version"], + ]; - if [ "${#failures[@]}" -gt 0 ]; then - echo "Required repo-owned build outputs failed: ${failures[*]}" >&2 - exit 1 - fi + for (const [job, resultKey, outputName] of mappings) { + const result = + buildResults[job] && buildResults[job].outputs && buildResults[job].outputs[resultKey] + ? buildResults[job].outputs[resultKey] + : "missing"; + if (result === "success") { + core.setOutput(outputName, headTag); + } else if (result === "skipped") { + core.setOutput(outputName, ""); + } else { + throw new Error(`Required repo-owned build output failed: ${job}.${resultKey}=${result}`); + } + } run-e2e-tests: needs: [create-e2e-tests-input] @@ -215,6 +202,7 @@ jobs: gateway-version: ${{ needs.create-e2e-tests-input.outputs.gateway-version }} host-version: ${{ needs.create-e2e-tests-input.outputs.host-version }} kms-core-version: ${{ needs.create-e2e-tests-input.outputs.kms-core-version }} + lock-artifact-name: ${{ needs.create-e2e-tests-input.outputs.lock-artifact-name }} relayer-migrate-version: ${{ needs.create-e2e-tests-input.outputs.relayer-migrate-version }} relayer-version: ${{ needs.create-e2e-tests-input.outputs.relayer-version }} scenario: ${{ startsWith(github.base_ref, 'release/') && 'two-of-two' || 'two-of-two-multi-chain' }} diff --git a/test-suite/fhevm/README.md b/test-suite/fhevm/README.md index 2cd0d4c401..477059b52d 100644 --- a/test-suite/fhevm/README.md +++ b/test-suite/fhevm/README.md @@ -146,7 +146,7 @@ Runtime resolution is intentionally fixed: Only `devnet`, `testnet`, and `mainnet` resolve from GitOps today. Non-network targets do not. `latest-main` is intentionally modern-only; if the resolver cannot find a complete image set after the floor, it fails instead of walking into older protocol behavior. -`sha` requires `--sha ` and fails fast unless every repo-owned package is available at that 7-character SHA tag, the SHA is on `main`, and it is at or after the simple-ACL floor. +`sha` requires `--sha ` and resolves every repo-owned image to that 7-character SHA tag. The CLI does not query GitHub or prove branch ancestry for this target; Docker pull or boot-time validation reports missing images or incompatible stacks. ## Pinning an Exact Version Bundle @@ -228,15 +228,15 @@ After resolving a target bundle, the CLI applies **environment variable override This is how CI works. The merge queue workflow: -1. Builds repo-owned Docker images for touched components -2. Sets `*_VERSION=` only for repo-owned components whose build succeeded -3. Falls back to the PR base short SHA for skipped repo-owned components -4. Pins `CORE_VERSION` from the release base branch when the PR targets `release/*` -5. Runs `./fhevm-cli up` with `two-of-two-multi-chain` for non-release orchestrate and `two-of-two` for `release/*` -6. Passes `build=false` explicitly because merge queue is validating selected registry images, while direct PR e2e uses `build=true` +1. Resolves a frozen baseline lock from `github.event.pull_request.base.sha` +2. Uploads that lock as a workflow artifact +3. Builds repo-owned Docker images for touched components +4. Sets `*_VERSION=` only for repo-owned components whose build succeeded +5. Leaves skipped component outputs empty so the reusable e2e workflow keeps the frozen lock value +6. Runs `./fhevm-cli up --lock-file ` with `two-of-two-multi-chain` for non-release orchestrate and `two-of-two` for `release/*` +7. Passes `build=false` explicitly because merge queue is validating selected registry images, while direct PR e2e uses `build=true` -Orchestrate does not resolve a new bundle in workflow YAML. It follows the historical base/head per-service image selection model, then passes those explicit versions into the reusable e2e workflow. -Release orchestrate keeps one extra compatibility bridge: it carries over the base branch `CORE_VERSION` from the release branch’s own checked-in defaults. +Orchestrate resolves the baseline once from the PR base SHA, then passes that lock artifact into the reusable e2e workflow. Head-image overrides are applied only for components rebuilt by the PR. The reusable workflow now runs on `pull_request` directly and treats PR e2e as source validation with `build=true`. Orchestrate passes `build=false` explicitly because it is validating selected registry images rather than rebuilding from source. @@ -294,8 +294,8 @@ The matrix has three sections: | `anchors` | Git history reference points | simple-ACL cutover commit | Merge-queue e2e explicitly keeps `build=false`. -For non-release PRs it boots `two-of-two-multi-chain` and uses the historical base/head per-service version selection. -For `release/*` PRs it boots `two-of-two`, restores the old base/head per-service version selection, and carries forward the base branch `CORE_VERSION`. +For non-release PRs it boots `two-of-two-multi-chain` from the frozen base lock plus any successful head-image overrides. +For `release/*` PRs it boots `two-of-two` from the same frozen-lock model. ### How to update diff --git a/test-suite/fhevm/src/resolve.test.ts b/test-suite/fhevm/src/resolve.test.ts index 9cbcf1bef5..c4d75a7fe9 100644 --- a/test-suite/fhevm/src/resolve.test.ts +++ b/test-suite/fhevm/src/resolve.test.ts @@ -15,8 +15,6 @@ import { SIMPLE_ACL_MIN_SHA, SHA_RUNTIME_COMPAT_MIN_SHA, applyVersionEnvOverrides, - assertSupportedShaBundle, - missingRepoPackages, presetBundle, shaRuntimeCompatFloor, simpleAclFloor, @@ -52,42 +50,6 @@ describe("resolve", () => { expect(bundle.env.CORE_VERSION).not.toBe("abcdef0"); }); - test("reports missing repo packages for a tag", () => { - const missing = missingRepoPackages( - { - GATEWAY_VERSION: new Set(["1234567"]), - } as Record>, - "1234567", - ); - expect(missing).toContain("fhevm/host-contracts"); - }); - - test("rejects locked sha bundles that predate the compat floor", () => { - const commits = [ - "head0000000000000000000000000000000000000", - SHA_RUNTIME_COMPAT_MIN_SHA, - SIMPLE_ACL_MIN_SHA, - "0000000aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - ]; - expect(() => assertSupportedShaBundle(presetBundle("sha", "0000000", "sha-0000000.json"), commits)).toThrow( - "unsupported", - ); - }); - - test("accepts locked sha bundles at the compat floor", () => { - const commits = [ - "head0000000000000000000000000000000000000", - SHA_RUNTIME_COMPAT_MIN_SHA, - SIMPLE_ACL_MIN_SHA, - ]; - expect(() => - assertSupportedShaBundle( - presetBundle("sha", SHA_RUNTIME_COMPAT_MIN_SHA.slice(0, 7), "sha-floor.json"), - commits, - ), - ).not.toThrow(); - }); - test("only caches immutable sha targets", () => { expect(targetUsesCache("latest-supported")).toBe(false); expect(targetUsesCache("latest-main")).toBe(false); diff --git a/test-suite/fhevm/src/resolve/bundle-store.ts b/test-suite/fhevm/src/resolve/bundle-store.ts index f4d5ba61d7..6cee8267fe 100644 --- a/test-suite/fhevm/src/resolve/bundle-store.ts +++ b/test-suite/fhevm/src/resolve/bundle-store.ts @@ -11,10 +11,8 @@ import { GitHubApiError } from "../errors"; import { PACKAGE_TO_REPOSITORY, applyVersionEnvOverrides, - assertSupportedShaBundle, resolveTarget, } from "./target"; -import { mainCommits } from "./github"; const VERSION_KEYS = Object.keys(PACKAGE_TO_REPOSITORY); const SAFE_LOCK_NAME = /^[A-Za-z0-9._-]+\.json$/; @@ -67,15 +65,7 @@ const validateBundleCompat = async (bundle: VersionBundle) => { }; const validateRuntimeCompat = async (bundle: VersionBundle) => { - await validateBundleCompat(bundle); - if (bundle.target === "sha") { - try { - assertSupportedShaBundle(bundle, await mainCommits(5000)); - } catch (error) { - throw new GitHubApiError(error instanceof Error ? error.message : String(error)); - } - } - return bundle; + return validateBundleCompat(bundle); }; /** Writes a resolved bundle into the persistent lock directory. */ @@ -154,7 +144,7 @@ const cachedResolve = async (options: CachedResolveOptions) => { } console.log(`[resolve] resolving ${options.target} bundle`); - if (options.target === "latest-main" || options.target === "sha") { + if (options.target === "latest-main") { console.log("[resolve] fetching main commits and published image tags"); } const bundle = await withProgressLogs(resolveTarget(options.target, { sha: options.sha }), `still fetching ${options.target} metadata`); diff --git a/test-suite/fhevm/src/resolve/target.ts b/test-suite/fhevm/src/resolve/target.ts index 15e08469d4..d447c0762d 100644 --- a/test-suite/fhevm/src/resolve/target.ts +++ b/test-suite/fhevm/src/resolve/target.ts @@ -184,45 +184,6 @@ export const shaRuntimeCompatFloor = (commits: string[]) => { return floor; }; -/** Rejects locked/cached sha bundles that fall below the supported main-history floors. */ -export const assertSupportedShaBundle = (bundle: VersionBundle, commits: string[]) => { - if (bundle.target !== "sha") { - return; - } - const floor = simpleAclFloor(commits); - const compatFloor = shaRuntimeCompatFloor(commits); - const refs = [ - ...new Set( - Object.entries(bundle.env) - .filter(([key, value]) => REPO_KEYS.has(key) && SHA_REF.test(value)) - .map(([, value]) => value.toLowerCase()), - ), - ]; - for (const ref of refs) { - const tag = ref.slice(0, 7); - const index = commits.findIndex((sha) => ref.length === 40 ? sha.toLowerCase() === ref : sha.startsWith(tag)); - if (index < 0) { - throw new Error( - `sha target ${ref.length === 40 ? ref : tag} is unsupported; only main commits at or after ${SIMPLE_ACL_MIN_SHA.slice(0, 7)} are supported`, - ); - } - if (index > floor) { - throw new Error(`sha target ${tag} predates the simple-ACL cutover and is unsupported`); - } - if (index > compatFloor) { - throw new Error( - `sha target ${tag} predates the modern gw-listener drift-address cutover (${SHA_RUNTIME_COMPAT_MIN_SHA.slice(0, 7)}) and is unsupported by the current CLI; use latest-supported or a newer sha`, - ); - } - } -}; - -/** Reports which repo-owned packages are missing a given tag in GHCR. */ -export const missingRepoPackages = (packageTagsMap: Record>, tag: string) => - Object.entries(REPO_PACKAGES) - .filter(([key]) => !packageTagsMap[key]?.has(tag)) - .map(([, pkg]) => decodeURIComponent(pkg)); - /** Builds a preset bundle for floating local targets and their companion pins. */ export const presetBundle = ( target: "latest-main" | "sha", @@ -352,35 +313,6 @@ export const resolveTarget = async ( throw new GitHubApiError(`Invalid sha ${requested}; expected 7 or 40 hex characters`); } const tag = shortSha(requested); - const [packageTagsMap, commits] = await Promise.all([repoPackageTags(tag), mainCommits(5000)]); - const missing = missingRepoPackages(packageTagsMap, tag); - if (missing.length) { - throw new GitHubApiError(`Could not find a complete sha image set for ${tag}; missing: ${missing.join(", ")}`); - } - let floor: number; - let compatFloor: number; - try { - floor = simpleAclFloor(commits); - compatFloor = shaRuntimeCompatFloor(commits); - } catch (error) { - throw new GitHubApiError(error instanceof Error ? error.message : String(error)); - } - const index = commits.findIndex((sha) => - requested.length === 40 ? sha.toLowerCase() === requested.toLowerCase() : sha.startsWith(tag), - ); - if (index < 0) { - throw new GitHubApiError( - `sha target ${requested.length === 40 ? requested.toLowerCase() : tag} is unsupported; only main commits at or after ${SIMPLE_ACL_MIN_SHA.slice(0, 7)} are supported`, - ); - } - if (index > floor) { - throw new GitHubApiError(`sha target ${tag} predates the simple-ACL cutover and is unsupported`); - } - if (index > compatFloor) { - throw new GitHubApiError( - `sha target ${tag} predates the modern gw-listener drift-address cutover (${SHA_RUNTIME_COMPAT_MIN_SHA.slice(0, 7)}) and is unsupported by the current CLI; use latest-supported or a newer sha`, - ); - } return presetBundle(target, tag, `sha-${tag}.json`, [`requested-sha=${requested.toLowerCase()}`]); }