Skip to content

chore(#648): every workflow action pin moves to a Node 24 major - #652

Merged
gsdali merged 3 commits into
refactor/381-pass1bfrom
chore/648-action-pin-bump
Aug 2, 2026
Merged

chore(#648): every workflow action pin moves to a Node 24 major#652
gsdali merged 3 commits into
refactor/381-pass1bfrom
chore/648-action-pin-bump

Conversation

@gsdali

@gsdali gsdali commented Aug 2, 2026

Copy link
Copy Markdown
Collaborator

Closes #648

#625 bumped only the job it introduced, so ci.yml was left mixed-version: actions/checkout@v7 in
gate-scripts, actions/checkout@v4 in build-and-test and ios-simulator-build. Deprecation
annotations are emitted per job, not per workflow, so each remaining v4 job raised its own
"Node.js 20 is deprecated" warning.

Full pin inventory — all 17, across all five workflow files

The issue named ci.yml. Sweeping only that file would have left the repo mixed-version while making
one file look consistent, which is the same defect one directory up. Every uses: in
.github/workflows/ is listed, whether or not it changed.

The runtime column is what that action's own action.yml declares in runs.using: at the pinned
ref
, read via gh api repos/<owner>/<action>/contents/action.yml?ref=<tag>, not inferred from the
version number.

file job action before after runtime (verified) changed
ci.yml gate-scripts actions/checkout v7 v7 node24 no
ci.yml gate-scripts actions/setup-python v7 v7 node24 no
ci.yml build-and-test actions/checkout v4 v7 node20node24 yes
ci.yml build-and-test maxim-lobanov/setup-xcode v1 v1 node24 no
ci.yml build-and-test actions/cache v4 v6 node20node24 yes
ci.yml ios-simulator-build actions/checkout v4 v7 node20node24 yes
ci.yml ios-simulator-build maxim-lobanov/setup-xcode v1 v1 node24 no
ci.yml ios-simulator-build actions/cache v4 v6 node20node24 yes
kernel-integration.yml build-and-test actions/checkout v4 v7 node20node24 yes
kernel-integration.yml build-and-test maxim-lobanov/setup-xcode v1 v1 node24 no
kernel-integration.yml build-and-test actions/cache (OCCT.xcframework) v4 v6 node20node24 yes
kernel-integration.yml build-and-test actions/cache (SwiftPM) v4 v6 node20node24 yes
occt-parallel-crash-test.yml test-windows actions/checkout v4 v7 node20node24 yes
occt-parallel-crash-test.yml test-macos actions/checkout v4 v7 node20node24 yes
release.yml verify-binary-pins actions/checkout v4 v7 node20node24 yes
release.yml verify-binary-pins maxim-lobanov/setup-xcode v1 v1 node24 no
require-issue-labels.yml check-labels actions/github-script v7 v9 node20node24 yes

11 changed, 6 left alone. After the sweep, no pin in the repo resolves to a Node 20 runtime.

The two deliberately left unchanged, with reasons

  • maxim-lobanov/setup-xcode@v1 (4 sites). The issue flagged this as third-party and possibly
    without a Node 24 major. It has one, in place. v1 is the only major tag that exists, and the
    moving tag resolves to commit ed7a3b1 (2026-03-18) — the same commit v1.7.0 resolves to —
    whose action.yml declares using: 'node24'. Bumping was neither possible nor needed, and the pin
    is not stale.
  • gate-scripts' own actions/checkout@v7 and actions/setup-python@v7 — already node24 from
    Neither new gate script is invoked by CI, a hook, or any script #625, and v7 is the current major for both. actions/setup-python was checked rather than assumed
    correct, because it shares a version number with the action that breaks the pattern below.

Bumping actions/checkout alone would have left the warning standing

The issue, and the annotation quoted in it, are both phrased around actions/checkout@v4. The actual
annotation GitHub emitted on the base commit names two actions:

warning: Node.js 20 is deprecated. The following actions target Node.js 20 but are being forced
to run on Node.js 24: actions/cache@v4, actions/checkout@v4.

One annotation per job, listing every Node 20 action in that job. So bumping only actions/checkout
the action the issue names — would have left actions/cache@v4 behind, the warning would still have
been emitted on both build-and-test and ios-simulator-build, and the diff would have looked like a
fix while changing nothing observable. That is the same trap as #647's, one level in: #647 got the
per-job granularity right and missed the sibling jobs; reading only the issue's headline action
would have got the per-job granularity right and missed the sibling action in the same job.

This is why the fix is 11 pins and not 6.

The version number is not the runtime

actions/github-script@v7 is using: node20, despite matching the v7 that actions/checkout
and actions/setup-python are correct at. node24 first arrives in github-script v8. A sweep
driven by "make everything say v7" — which the issue's own summary invites, naming v7 as the current
major — would have produced a repo where every pin agreed and require-issue-labels.yml was still
deprecated. That job goes to v9, the current major.

Each target tag was also confirmed to exist before pinning
(gh api repos/<owner>/<action>/git/matching-refs/tags/v): checkout v1v7, cache v1v6
(no v7 — v6 is the current major, which is why cache lands on a different number than checkout),
github-script v1v9, setup-python v1v7, setup-xcode v1 only.

Breaking-change check, per action

No bump changed an interface. action.yml at the new ref is byte-identical to the old ref apart
from the using: line, in all three cases — established by diffing the two files, not by trusting
release notes:

$ diff checkout-v4.yml checkout-v7.yml
116c116
<   using: node20
---
>   using: node24

The same single-line diff holds for cache v4→v6 and github-script v7→v9. So fetch-depth
(default 1), submodules (default false), persist-credentials (default true) and cache's
cache-hit output all keep their declared defaults. What the manifest proves is the declared
interface; the compiled JS behind it is covered by the behaviour-change review below, not by this
diff. kernel-integration.yml reads
steps.occt-cache.outputs.cache-hit to skip a 60-minute source build; that output is unchanged.

Behaviour changes that exist outside the interface, from each action's changelog:

  • actions/checkout v6.0.0 — "Persist creds to a separate file" (PR 2286).
    Credentials move out of .git/config into a separate credential file. Inert here:
    grep -rn "persist-credentials\|GITHUB_TOKEN\|git push\|git config\|\.git/config\|extraheader" .github/workflows/
    returns nothing — no workflow pushes, reads the persisted token, or re-uses the checkout's git
    credentials.
  • actions/checkout v7.0.0 — "Block checking out fork PR for pull_request_target and
    workflow_run"
    (PR 2454). A genuine refusal,
    not a warning. Inert here: neither trigger is used anywhere in the repo. The triggers in use are
    pull_request (ci, kernel-integration), push, workflow_dispatch, release and issues, and
    plain pull_request is unaffected.
  • actions/checkout v5 / actions/cache v5 / actions/github-script v8 — minimum runner
    v2.327.1.
    Every runs-on in the repo is GitHub-hosted (ubuntu-latest, macos-15,
    macos-latest, windows-latest); there are no self-hosted runners, so the floor is met.
  • actions/cache v6.0.0 — ESM migration. Packaging only; no documented behaviour change and no
    interface change per the diff above.
  • actions/github-script v9.0.0 — two breaking changes. require('@actions/github') no longer
    works inside a script, and getOctokit is now an injected parameter, so a script declaring
    const getOctokit gets a SyntaxError. Neither applies: the script in require-issue-labels.yml
    uses only the injected context and github.rest.issues.*, and greps clean for require(,
    getOctokit and @actions/github.

Nothing needed adapting, and nothing had to be held back at an old pin for a behavioural reason.

Evidence

  • All five workflow files re-parse as YAML after the edits.
  • The four gate-scripts gates run clean locally on this branch: check-bridge-index.py exit 0
    (728 symbols / 383 classes, 0 stale, 0 misfiled), check-null-handle-guards.py exit 0,
    check-docs-defaults.py exit 0 (0 drifted, 0 unverified), count-operations.py exit 0
    (4301 = README = API_REFERENCE).
  • Per-job annotation state from this PR's own CI run is posted as a comment below, since annotations
    are emitted per job and can only be read off a real run.

build-and-test (macOS) is red branch-wide on refactor/** from #585's pinned-kernel mismatch and
stays red here for that reason. What this PR is accountable for is that it still reaches swift test
rather than failing earlier, at checkout or cache restore.

Docs

docs/CHANGELOG.md gains an Unreleased entry under the Pass 1b heading. No version invented, no
operation counts touched.

🤖 Generated with Claude Code

gsdali and others added 2 commits August 3, 2026 05:59
#625 bumped only the job it introduced, leaving ci.yml mixed-version: actions/checkout@v7 in
gate-scripts, actions/checkout@v4 in build-and-test and ios-simulator-build. Deprecation
annotations are emitted per JOB, not per workflow, so each remaining v4 job raised its own
"Node.js 20 is deprecated" warning.

All 17 pins across the five workflow files were enumerated rather than just the two the issue
named. Changed: actions/checkout v4 -> v7 (six sites), actions/cache v4 -> v6 (four sites),
actions/github-script v7 -> v9 (one site). Unchanged: maxim-lobanov/setup-xcode@v1, whose moving
v1 tag already resolves to a node24 build, and gate-scripts' own checkout@v7 / setup-python@v7.

The target for each was read out of that action's action.yml at the pinned ref rather than
inferred from the version number, and one action does not follow the pattern:
actions/github-script@v7 is `using: node20` and node24 only arrives at v8. A sweep that made
every pin say v7 would have produced a repo that looked consistent while leaving
require-issue-labels.yml deprecated.

No bump changed an interface: action.yml at the new ref is byte-identical to the old apart from
the `using:` line in all three cases, so fetch-depth, submodules, persist-credentials and
cache-hit keep their defaults. The two real behaviour changes are inert here: checkout v6
persists credentials to a separate file rather than .git/config (no workflow reads them or
pushes), and checkout v7 blocks fork-PR checkout under pull_request_target / workflow_run
(neither trigger is used anywhere).

Closes #648

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ust checkout

The annotation GitHub emitted on the base commit lists every Node 20 action in the job:
"actions/cache@v4, actions/checkout@v4". Bumping only the action the issue names would have
left the warning standing while the diff looked like a fix.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@gsdali

gsdali commented Aug 2, 2026

Copy link
Copy Markdown
Collaborator Author

Per-job annotation evidence, before and after

Annotations are emitted per job, so this is read per job off real runs rather than per workflow.
Both runs are .github/workflows/ci.yml; annotations are reported regardless of job outcome, so a
red job still proves its own annotation state.

job before after
gate-scripts (none) none — unchanged
build-and-test warning: Node.js 20 is deprecated ... actions/cache@v4, actions/checkout@v4 + failure: exit code 1 failure: exit code 1 only — warning gone
ios-simulator-build warning: Node.js 20 is deprecated ... actions/cache@v4, actions/checkout@v4 zero annotations

The Node 20 deprecation warning is gone from both jobs that were bumped, and gate-scripts still has
none.

The annotation named actions/cache@v4 as well as actions/checkout@v4

Worth recording because it decided the scope of the change. The warning is emitted once per job and
lists every Node 20 action in that job. Bumping only actions/checkout — the action #648's title
and body name — would have left actions/cache@v4 in place, the warning would still have been
emitted on both jobs, and the diff would have read as a fix. Same trap as #647's, one level in: #647
got the per-job granularity right and missed the sibling jobs; following the issue's headline would
have got the per-job granularity right and missed the sibling action inside the same job.

No new failure introduced earlier than #585's

build-and-test is red here, as it is branch-wide on refactor/**#585, the pinned v1.15.18
xcframework predating the carried patches this branch's tests need. What this PR is accountable for
is not regressing anything ahead of that. Step-for-step, base vs PR:

# step base (@v4) PR (@v7/@v6)
1 Set up job success success
2 actions/checkout success success
3 Select Xcode success success
4 Cache SwiftPM artifacts success success
5 swift build success success
6 swift test failure failure
12 Post actions/checkout success success

Identical, including the failing step. Failure count is 56 recorded issues on both, and the
signature is #585's: Issue570HealingApproxTests failing on (fitted.uDegree → 1) > 1 and
deviation → 23.9999..., which is exactly the degree-1 collapse kernel patch 0019 (#522) fixes and
the pinned kernel does not carry.

ios-simulator-build is green and step-identical to base, including its post stepsPost Cache SwiftPM artifacts (that is actions/cache@v6's save half, skipped on the red job but exercised
here) and Post Run actions/checkout@v7 (the credential cleanup that v6 changed). Those are the two
places a bad cache or checkout bump would surface, and both pass.

What could not be verified in CI, and why

Three workflows are not reachable from a pull request, so their pins are verified by
action.yml runtime + tag existence + the same actions passing in ci.yml, not by a run of their
own:

  • require-issue-labels.yml (actions/github-script@v9) triggers on issues events. Workflows
    triggered by non-PR events always run the default branch's copy of the file, so this job cannot
    execute this branch's version until the change reaches main — not merely until this PR merges to
    refactor/381-pass1b. This is the one changed pin no run in this PR exercises. Its two v9 breaking
    changes were checked against the script by grep (require(, getOctokit, @actions/github: no
    matches); if v9 ever misbehaves, v8 is the zero-code-change fallback that is equally node24.
  • kernel-integration.yml runs only when Scripts/patches/** or Scripts/build-occt.sh changes,
    which this PR does not touch. Its actions/cache@v6 usage additionally reads
    steps.occt-cache.outputs.cache-hit; cache-hit is still a declared output at v6 with a
    byte-identical declaration, and ci.yml exercises the same action at the same ref.
  • occt-parallel-crash-test.yml is workflow_dispatch-only and builds OCCT from source on
    Windows and macOS. Not dispatched: it is a 60-minute two-platform source build, and its only change
    is actions/checkout@v4@v7, the identical pin proven green twice in ci.yml above.

release.yml likewise only runs on release publish; its change is the same checkout pin.

The byte-identical action.yml establishes that fetch-depth, submodules, persist-credentials and
cache-hit keep their DECLARED defaults. Semantics live in the compiled JS, and the very next
sentence names two real behaviour changes, so the clause claimed more than its own evidence.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@gsdali
gsdali merged commit da83350 into refactor/381-pass1b Aug 2, 2026
2 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant