Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/conformance.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, ubuntu-24.04-arm]
os: [ubuntu-24.04, ubuntu-24.04-arm]
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: erlef/setup-beam@54075bcc5e249e4758d363f27d099f55d843f124 # v1.24.1
Expand Down
21 changes: 12 additions & 9 deletions .github/workflows/gate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ on:
# quotes it. `fail-fast: false` so one leg's red does not hide another's measurement.
#
# THE BOX LEG: the pair a fielded edge box runs (OTP 28 at a patched release, Elixir 1.20), on
# the architecture it runs on (aarch64, GitHub's arm64 runner, free for public repositories).
# the architecture it runs on (aarch64, GitHub's arm64 runner, free for public repositories),
# over the OpenSSL line the box's image pins (3.5 or later): Ubuntu 26.04's runner ships 3.5.5,
# and builds.hex.pm's OTP for it links that, where 24.04's links 3.0.13. Every runner is named
# by its image, never `-latest`, so an image moves only in a commit (governance_test.exs).
# Its versions are exact (`version-type: strict`), not "the newest 28": the box pins its
# runtime, so the leg does too. Every leg's gate runs the OTP floor step (tools/otp_floor.sh),
# so a resolution below a line's patched floor is a red leg, not a green one on a vulnerable
Expand All @@ -49,10 +52,10 @@ jobs:
fail-fast: false
matrix:
include:
- { leg: floor, otp: "27", elixir: "1.17", os: ubuntu-latest, version-type: loose }
- { leg: pinned, otp: "28", elixir: "1.18", os: ubuntu-latest, version-type: loose }
- { leg: head, otp: "29", elixir: "1.20", os: ubuntu-latest, version-type: loose }
- { leg: box, otp: "28.5.0.8", elixir: "1.20.4", os: ubuntu-24.04-arm, version-type: strict }
- { leg: floor, otp: "27", elixir: "1.17", os: ubuntu-24.04, version-type: loose }
- { leg: pinned, otp: "28", elixir: "1.18", os: ubuntu-24.04, version-type: loose }
- { leg: head, otp: "29", elixir: "1.20", os: ubuntu-24.04, version-type: loose }
- { leg: box, otp: "28.5.0.8", elixir: "1.20.4", os: ubuntu-26.04-arm, version-type: strict }
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
Expand Down Expand Up @@ -93,7 +96,7 @@ jobs:
# script, so its contents cannot be run as a command.
pr-body:
name: Gate - pull-request body
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
if: github.event_name == 'pull_request'
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
Expand All @@ -114,7 +117,7 @@ jobs:

summary:
name: Gate - quality gate
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
needs: [gate, pr-body]
if: always()
# The edited path may wait up to 20 minutes for running legs (tools/ci_legs_verdict.sh's
Expand All @@ -139,7 +142,7 @@ jobs:
# the REQUIRED context, and a fresh green under that name on a commit whose legs
# this run never read would let a title edit stand a green beside a red (a review
# lane's finding). So the legs' verdicts for this head SHA are read from the checks
# API -- the latest run of each leg name -- and all three must be success. The
# API -- the latest run of each leg name -- and all four must be success. The
# read WAITS for legs still running (G-079: an edit during the synchronize run read
# null conclusions, failed, and the ruleset blocked on that red beside the later
# green); tools/ci_legs_verdict.sh polls to a bound, and its probe runs offline.
Expand All @@ -152,7 +155,7 @@ jobs:

dco:
name: Gate - DCO sign-off
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/provenance.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ permissions:
jobs:
attest:
name: Provenance - build, attest, verify
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
permissions:
contents: read
id-token: write
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/scorecard.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ permissions: read-all
jobs:
analysis:
name: Scorecard - the measurement
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
permissions:
# The action's documented needs: SARIF upload and the OIDC token that publish_results
# signs the result with. Nothing writes to the tree.
Expand Down
2 changes: 1 addition & 1 deletion docs/assurance-case.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ partial scheme.

## How the case is kept true

- The gate runs on every push on four OTP/Elixir legs (three pairs on x86_64, and the box pair, OTP 28.5.0.8 with Elixir 1.20.4, on aarch64), each held to its line's patched OTP floor; the censuses above fail when the code
- The gate runs on every push on four OTP/Elixir legs (three pairs on x86_64, and the box pair, OTP 28.5.0.8 with Elixir 1.20.4, on aarch64 over the OpenSSL 3.5 line the box's image pins; every runner named by its image, so none moves without a commit), each held to its line's patched OTP floor; the censuses above fail when the code
they describe changes.
- The threat model's citations and the will-not-implement page's entries are each held to the
test suite by a census of their own.
Expand Down
21 changes: 21 additions & 0 deletions test/beam_mcp/governance_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,27 @@ defmodule BeamMCP.GovernanceTest do
end
end

# A `-latest` label is moved by GitHub, not by a commit: `ubuntu-latest` becomes 26.04 in
# November 2026 (actions/runner-images#14748), which changes the OpenSSL every such leg links
# (3.0.13 to 3.5.5) with nothing in this tree's history to say so. Named images move only in
# a commit, as the pins on actions do.
test "every job names its runner image by version: no -latest label, and a matrix supplies only named images" do
for wf <- @workflows,
{line, n} <- Enum.with_index(String.split(File.read!(wf), "\n"), 1) do
refute line =~ ~r/\b[a-z]+-latest\b/,
"#{wf}:#{n} names a moving runner label: #{String.trim(line)}"

if line =~ ~r/^\s*runs-on:/ do
assert line =~ ~r/runs-on:\s+(ubuntu-\d\d\.\d\d(-arm)?|\$\{\{ matrix\.os \}\})\s*$/,
"#{wf}:#{n} runs on something other than a named image: #{String.trim(line)}"
end

for [image] <- Regex.scan(~r/\bubuntu-[a-z0-9.]+(?:-arm)?\b/, line) do
assert image =~ ~r/^ubuntu-\d\d\.\d\d(-arm)?$/, "#{wf}:#{n} names #{image}"
end
end
end

test "every workflow declares top-level permissions, and write permissions live on the job that needs them" do
for wf <- @workflows do
text = File.read!(wf)
Expand Down
Loading