diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f2d595e..02701a3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -101,7 +101,20 @@ jobs: echo "commit subject ok: $subject" test: - name: Julia ${{ matrix.julia-version }} / ${{ matrix.os }} + # The name is a fixed string, and deliberately carries no version. + # + # A branch ruleset matches a required status check by the DISPLAY NAME of the job + # that posts it. When that name was `Julia ${{ matrix.julia-version }} / ${{ matrix.os }}` + # a pin bump renamed the check, and a renamed check does not report at all -- it + # does not fail, it is simply absent, and an absent required check can never be + # satisfied. Every pull request would have deadlocked until an admin bypassed the + # rule, triggered by nothing more alarming than editing a version number. + # + # The versions have not been hidden, only moved somewhere that does not double as + # an identifier: `strategy.matrix` below, and the `Set up Julia` step's own log. + # `test/unit/test_install_pins.jl` asserts that no job name in this file + # interpolates anything, so this cannot regress silently. + name: Julia tests runs-on: ${{ matrix.os }} # The repository's root .Rprofile sources renv/activate.R, so R started from # the checkout auto-activates renv and rewrites the library paths. renv is a diff --git a/test/unit/test_install_pins.jl b/test/unit/test_install_pins.jl index 762e4f8..c25c79c 100644 --- a/test/unit/test_install_pins.jl +++ b/test/unit/test_install_pins.jl @@ -181,4 +181,26 @@ is_sha256(s) = s isa AbstractString && occursin(r"^[0-9a-f]{64}$", s) @test !occursin(pins["tools"][tool]["version"], runs) end end + + @testset "no job name interpolates a pin" begin + ci = YAML.load_file(CI_PATH) + + # A required status check is matched by the DISPLAY NAME of the job that posts + # it. `name: Julia ${{ matrix.julia-version }} / ${{ matrix.os }}` therefore + # renames the check on every pin bump, and a renamed check does not report at + # all -- it does not fail, it is simply absent, and a required check that never + # reports can never be satisfied. Every pull request then deadlocks until an + # admin bypasses the rule, which is the one outcome branch protection exists to + # prevent. The bump that triggers it is a one-line edit to this very file's + # subject matter, so the deadlock arrives by way of an ordinary maintenance + # change that looks safe. + # + # Asserting this over EVERY job rather than over the one job we happen to + # require today is deliberate: the rule is "a job name is a stable identifier", + # and a rule enforced at each door in turn is a rule that a new door escapes. + for (id, job) in ci["jobs"] + name = get(job, "name", id) + @test !occursin("\${{", name) + end + end end