Skip to content

v2.6.0

Choose a tag to compare

@github-actions github-actions released this 27 Aug 16:49
· 102 commits to main since this release
v2.6.0
3413db4

Added

  • The release-path workflow guards now run in CI. release.yml triggers
    only on tag push — the tag push is the publish — so it never appeared in a
    PR check, and every guard that protected the last change to it was run by
    hand, once. Two new test.yml jobs close that:

    workflows runs scripts/check_workflows.py, which asserts by invariant
    that release.yml's trigger set is exactly push.tags: ["v*"] — no other
    event and no branch or path filter alongside it, since publish holds
    id-token: write against an environment with no protection rules, so any
    extra trigger makes trusted publishing reachable from it — and that no
    workflow file other than release.yml/docker.yml is tag-triggered at all;
    the build → publish → github-release ordering; environment: release by
    name
    ; no if: or continue-on-error at job or step level on any of the
    three jobs (skipping build skips publish through needs, and the tag push
    still concludes green); the
    exact committed permissions: maps at both workflow and job level, the exact
    committed uses: references, and exactly the three expected jobs; that every
    job in every workflow carries a timeout-minutes of 10–30 (a bare
    timeout-minutes: 360 re-creates the six-hour default); and by drift
    that no job present in the PR base has disappeared from any changed workflow,
    including one deleted outright. Drift fails closed: a base ref that does
    not resolve, and a git diff that fails for any other reason (a base sharing
    no history with HEAD exits 128), are failures, never "nothing changed". It
    also runs a digest-pinned actionlint.

    release-diff-ack covers by acknowledgement what an allowlist cannot
    cover by enumeration: any PR touching release.yml fails unless it carries a
    release-change-approved label, read live from the API rather than from the
    event payload frozen at trigger time.

    Not covered, deliberately: a timeout above the 10-minute floor but below a
    job's real p100; environment protection rules, which live in GitHub settings
    and are invisible to any file check; and if:-skipping the guard job itself,
    which GitHub counts as satisfying a required check. Per-mutant exit codes,
    including the ones that stay green, are in
    .consiliency/evidence/mutation-189.md.

    Because the uses: and permissions: allowlists are exact, a legitimate
    edit to release.yml — bumping an action, granting a scope — must update the
    constants in scripts/check_workflows.py in the same PR. That is intended.

Fixed

  • npm package identity now comes from npm's own parser where it is certain, and
    is refused otherwise.
    The hand-written npm flag tables have been repaired five
    times (#180 → #192 → #194 → #195 → the 2.5.2 nullable-boolean
    spelling), and every defect was in the rules around the tables rather than a
    missing entry — so every repair produced a confident wrong answer, which the
    freshness gate reads as positive confirmation that a cached tool description
    still describes the configured package.

    For an npx/npm server the gateway now asks the host npm's own nopt, its
    own @npmcli/config definitions and its own npm-package-arg, through a
    faithful port of npm's npx-cli.js pre-scan, and accepts the answer only when
    nothing in the invocation could redirect resolution. It refuses when:

    • the parsed configuration contains any key beyond --yes and --package
      (--registry, --userconfig, --prefix, --cache, --call, --workspace,
      a shorthand such as --silent that expands to --loglevel, or an unknown
      flag) — this is an allowlist of plain shapes, not a denylist of dangerous
      ones;
    • the server's environment overlay, or the gateway's own process
      environment, sets npm_config_* (case-insensitive), PATH, HOME,
      NODE_PATH, NODE_OPTIONS, PREFIX or NVM_*;
    • walking up from the effective working directory, npm would set a local
      prefix (a package.json or node_modules in any ancestor), because a
      project .npmrc can rename the package and a local node_modules/.bin entry
      means npm never reaches the registry at all;
    • npm-package-arg reports anything but a registry spec — notably an alias
      (npx -y myalias@npm:left-pad really runs left-pad, and the alias name is a
      squattable different package);
    • the npm subcommand has no package operand (npm run, npm start, npm test,
      npm create, a typo, bare npm -y pkg, and now npm dlx, which is
      pnpm/yarn spelling and is not an npm command at all);
    • the spawn-time self-test against the host's own parser fails, bin/npx-cli.js
      is not one this port was verified against, or npm's parser cannot be loaded.
      A failed self-test refuses — it does not fall back to the tables, because
      a failed self-test is precisely the evidence that the tables' model of npm is
      wrong. One WARNING is logged.

    Refusing costs auto-update coverage for an unusual configuration: the server
    keeps running, but its package is reported as unknown, so its descriptions
    refresh every cycle and gateway.update_server cannot name a package for it.
    Measured cost on the shipped manifest: zero — all 79 npm-family servers use
    the plain npx -y <pkg> shape and all 79 resolve to the same package the real
    npx binary fetches.

    Where node is not installed the flag tables remain in use unchanged, which is
    the behaviour every release through 2.5.1 shipped.

    Known residual: a package= or registry= line in a user or global
    .npmrc changes what npm resolves and the gateway cannot see it. Project-level
    .npmrc is covered by the local-prefix refusal, and npm_config_* in the
    gateway's own environment is covered by the process-environment check; the
    user/global rc file is the one input that remains unguarded.

    detect_package_type, _npm_package_arg, get_package_version and
    gateway.update_server's pin detection all take the server's environment
    overlay and working directory as required parameters now, since both are
    identity inputs.