Skip to content
Closed
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
89 changes: 78 additions & 11 deletions .github/workflows/release-rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,29 @@ jobs:
outputs:
matrix: ${{ steps.plan.outputs.matrix }}
steps:
- name: harden_runner
uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0
with:
egress-policy: block
allowed-endpoints: >
github.com:443
ziglang.org:443

- name: checkout_release_workflows
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
# This reusable workflow's own repo content (the reviewed mirror
# list, see #31) — not the caller's, which is what a bare
# checkout would default to. workflow_sha pins to the exact
# commit of release-workflows this run is executing from,
# regardless of what ref the caller specified.
repository: purpleclay/release-workflows
ref: ${{ github.workflow_sha }}
persist-credentials: false
sparse-checkout: |
.github/zig-mirrors.txt
sparse-checkout-cone-mode: false

- name: require_tag_ref
env:
REF_TYPE: ${{ github.ref_type }}
Expand All @@ -84,7 +107,40 @@ jobs:
TARGETS: ${{ inputs.targets }}
run: |
set -euo pipefail
if ! matrix=$(jq -c '

# cargo needs both the registry index and the actual crate-file
# host on every leg, plus the toolchain installer — verified
# against real harden-runner audit logs, not guessed (see #23).
common_endpoints="github.com:443 index.crates.io:443 static.crates.io:443 static.rust-lang.org:443"

# zigbuild legs additionally need taiki-e/install-action fetching
# the cargo-zigbuild binary from GitHub releases, and zig itself.
# mlugg/setup-zig picks a mirror at random per run (confirmed:
# two real runs picked two different ones), so a single fixed
# mirror isn't viable — but community-mirrors.txt is mutable,
# externally maintained content, not something we've reviewed;
# trusting it directly would let an upstream compromise silently
# authorize a new build-time egress target with no review on our
# side (#31). Only trust a mirror that's BOTH currently live
# upstream AND already reviewed into .github/zig-mirrors.txt: a
# mirror going away upstream drops out with no PR needed; a new
# one only becomes trusted once someone reviews and commits it.
live_mirrors=$(curl -fsSL https://ziglang.org/download/community-mirrors.txt \
| sed -E 's#^https://##; s#/.*$##' | sort -u)
reviewed_mirrors=$(sed -E 's#^https://##; s#/.*$##' .github/zig-mirrors.txt | sort -u)
zig_mirrors=$(comm -12 <(echo "$live_mirrors") <(echo "$reviewed_mirrors") | sed 's/$/:443/' | tr '\n' ' ')

if [ -z "$zig_mirrors" ]; then
echo "::error::no zig mirrors are both live upstream and reviewed in .github/zig-mirrors.txt — the reviewed list has likely gone stale, update it before retrying"
exit 1
fi

zigbuild_endpoints="${common_endpoints} release-assets.githubusercontent.com:443 ziglang.org:443 ${zig_mirrors}"

if ! matrix=$(jq -c \
--arg common "$common_endpoints" \
--arg zigbuild_endpoints "$zigbuild_endpoints" \
'
if type != "array" then
error("targets must be a JSON array of strings")
elif length == 0 then
Expand All @@ -96,10 +152,10 @@ jobs:
else . end
| [ .[] | . as $t |
{ target: $t } +
( { "x86_64-unknown-linux-musl": { "runs-on": "ubuntu-24.04", "zigbuild": true },
"aarch64-unknown-linux-musl": { "runs-on": "ubuntu-24.04", "zigbuild": true },
"x86_64-apple-darwin": { "runs-on": "macos-15", "zigbuild": false },
"aarch64-apple-darwin": { "runs-on": "macos-15", "zigbuild": false }
( { "x86_64-unknown-linux-musl": { "runs-on": "ubuntu-24.04", "zigbuild": true, "allowed-endpoints": $zigbuild_endpoints },
"aarch64-unknown-linux-musl": { "runs-on": "ubuntu-24.04", "zigbuild": true, "allowed-endpoints": $zigbuild_endpoints },
"x86_64-apple-darwin": { "runs-on": "macos-15", "zigbuild": false, "allowed-endpoints": $common },
"aarch64-apple-darwin": { "runs-on": "macos-15", "zigbuild": false, "allowed-endpoints": $common }
}[$t] // error("unsupported target: \($t)") )
]' <<<"$TARGETS"); then
echo "::error::release-rust supports: x86_64-unknown-linux-musl, aarch64-unknown-linux-musl, x86_64-apple-darwin, aarch64-apple-darwin"
Expand All @@ -120,9 +176,10 @@ jobs:
contents: read
steps:
- name: harden_runner
uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2
uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0
with:
egress-policy: audit
egress-policy: block
allowed-endpoints: ${{ matrix.allowed-endpoints }}

- name: checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
Expand Down Expand Up @@ -166,14 +223,14 @@ jobs:
tool: cargo-zigbuild@${{ env.ZIGBUILD_VERSION }}
checksum: true

- name: Build (zigbuild)
- name: build (zigbuild)
if: ${{ matrix.zigbuild }}
env:
BIN: ${{ inputs.bin }}
TARGET: ${{ matrix.target }}
run: cargo zigbuild --locked --release --target "$TARGET" --bin "$BIN"

- name: Build (native)
- name: build (native)
if: ${{ !matrix.zigbuild }}
env:
BIN: ${{ inputs.bin }}
Expand Down Expand Up @@ -232,9 +289,19 @@ jobs:
release-url: ${{ steps.publish.outputs.url }}
steps:
- name: harden_runner
uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2
uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0
with:
egress-policy: audit
egress-policy: block
allowed-endpoints: >
api.github.com:443
fulcio.sigstore.dev:443
github.com:443
rekor.sigstore.dev:443
release-assets.githubusercontent.com:443
tmaproduction.blob.core.windows.net:443
tuf-repo-cdn.sigstore.dev:443
tuf-repo.github.com:443
uploads.github.com:443

- name: checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
Expand Down
7 changes: 6 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,12 @@ jobs:
- name: harden_runner
uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0
with:
egress-policy: audit
egress-policy: block
allowed-endpoints: >
api.github.com:443
cafe.github.com:443
github.com:443
release-assets.githubusercontent.com:443

- name: checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
Expand Down
33 changes: 33 additions & 0 deletions .github/zig-mirrors.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Keeping `zig-mirrors.txt` in sync

`zig-mirrors.txt` is the reviewed set of Zig community mirrors trusted as a build-job egress target for zigbuild legs. `release-rust.yml` only allows a mirror through if it's *both* in this file *and* currently live upstream (see [issue #31](https://github.com/purpleclay/release-workflows/issues/31) for why it isn't just fetched from upstream directly).

This is a deliberately manual process — a bot opening a routine PR risks training reviewers to rubber-stamp it, which defeats the point of requiring review at all. Automating this is tracked as a possible future issue if the manual upkeep ever becomes a real burden; it isn't expected to.

## When to check

There's no fixed schedule. Worth checking when:

- A zigbuild release fails with "no zig mirrors are both live upstream and reviewed" (`compose_build_matrix`'s hard-failure path) — this means the two lists have diverged enough to share nothing at all, and needs attention immediately.
- Roughly every few months, or whenever you're touching this workflow for another reason anyway.

## How to check

```sh
diff <(curl -fsSL https://ziglang.org/download/community-mirrors.txt | sort) \
<(sort .github/zig-mirrors.txt)
```

Lines prefixed `<` are in the committed file but no longer live upstream — safe to drop, no review needed, they can't be reached anyway. Lines prefixed `>` are new upstream entries — this is the part that needs actual scrutiny, not a rubber stamp.

## Reviewing a new entry

For each newly-added host, actually verify it before adding it — don't just accept it because it's on the official list. At minimum:

1. Confirm it resolves and serves a real Zig release tarball. It should return `200`, not a redirect to somewhere unexpected or a `404`.
```sh
curl -sIL "https://<mirror>/zig-x86_64-linux-<version>.tar.xz"
```
2. Check who operates it, if that's discoverable (project README, DNS WHOIS, whether it's referenced elsewhere in the Zig community). Prefer mirrors run by identifiable people/organizations over anonymous ones.

Then update `.github/zig-mirrors.txt` to match upstream and commit — a normal PR, reviewed the same as any other change to this workflow.
16 changes: 16 additions & 0 deletions .github/zig-mirrors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
https://fs.liujiacai.net/zigbuilds
https://pkg.earth/zig
https://pkg.hexops.org/zig
https://zig-mirror.tsimnet.eu/zig
https://zig.bcr.ist
https://zig.chainsafe.dev
https://zig.karearl.com/zig
https://zig.linus.dev/zig
https://zig.mirror.mschae23.de/zig
https://zig.savalione.com
https://zig.squirl.dev
https://zig.tilok.dev
https://zig.vortan.dev/zig
https://ziglang.freetls.fastly.net
https://zigmirror.com
https://zigmirror.hryx.net/zig
8 changes: 4 additions & 4 deletions .github/zizmor.yml
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
rules:
superfluous-actions:
ignore:
# release-rust.yml:139 — dtolnay/rust-toolchain: explicit toolchain +
# release-rust.yml:196 — dtolnay/rust-toolchain: explicit toolchain +
# cross-target install; hand-rolling rustup here would just reimplement
# this action with less auditability.
- release-rust.yml:139
- release-rust.yml:196
stale-action-refs:
ignore:
# release-rust.yml:139 — dtolnay/rust-toolchain intentionally ships no
# release-rust.yml:196 — dtolnay/rust-toolchain intentionally ships no
# tags: master/stable/beta/nightly are branch aliases by design, so
# this pin is the tip of "master", not a stale reference.
- release-rust.yml:139
- release-rust.yml:196
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
!.envrc
!.github/renovate.json
!.github/workflows/*.yml
!.github/zig-mirrors.md
!.github/zig-mirrors.txt
!.github/zizmor.yml
!.gitignore
!.zed/settings.json
Expand All @@ -15,6 +17,7 @@
!README.md
!RELEASE.md
!SECURITY.md
!typos.toml

# Recurse through sub-directories applying the same patterns
!*/
5 changes: 5 additions & 0 deletions typos.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[files]
# A list of third-party hostnames, not prose — spellcheck will keep
# producing false positives here as mirrors are added/removed over time
# (e.g. "zig.bcr.ist", "zig.squirl.dev" are real domains, not typos).
extend-exclude = [".github/zig-mirrors.txt"]