|
| 1 | +name: 'Mirror Zig toolchain' |
| 2 | +description: 'Fetch a pinned Zig toolchain, verify it against the official minisign key, and upload it to a GitHub Release for restricted-egress or long-retention use.' |
| 3 | +author: 'Sergey Rubanov' |
| 4 | +branding: |
| 5 | + icon: 'hard-drive' |
| 6 | + color: 'orange' |
| 7 | + |
| 8 | +inputs: |
| 9 | + version: |
| 10 | + description: > |
| 11 | + Zig version to mirror (e.g. "0.14.1" or |
| 12 | + "0.17.0-dev.1275+59a628c6d"). Leave empty to read |
| 13 | + `.minimum_zig_version` from `version-file`. |
| 14 | + required: false |
| 15 | + default: '' |
| 16 | + version-file: |
| 17 | + description: 'Path to a build.zig.zon to read the version from when `version` is empty.' |
| 18 | + required: false |
| 19 | + default: 'build.zig.zon' |
| 20 | + platforms: |
| 21 | + description: > |
| 22 | + Space- or newline-separated <arch>-<os> targets to mirror, |
| 23 | + e.g. "x86_64-linux aarch64-macos". Arch/OS use Zig's tarball |
| 24 | + naming (x86_64, aarch64 / linux, macos, windows). |
| 25 | + required: false |
| 26 | + default: | |
| 27 | + x86_64-linux |
| 28 | + aarch64-linux |
| 29 | + x86_64-macos |
| 30 | + aarch64-macos |
| 31 | + release-tag: |
| 32 | + description: 'GitHub Release tag to upload assets to. Created if it does not exist.' |
| 33 | + required: false |
| 34 | + default: 'zig-toolchain' |
| 35 | + release-title: |
| 36 | + description: 'Title for the release when it is first created.' |
| 37 | + required: false |
| 38 | + default: 'Zig toolchain mirror' |
| 39 | + mirror: |
| 40 | + description: > |
| 41 | + Optional single mirror base URL to try first |
| 42 | + (e.g. "https://pkg.machengine.org/zig"). Cannot be ziglang.org. |
| 43 | + required: false |
| 44 | + default: '' |
| 45 | + token: |
| 46 | + description: 'GitHub token with `contents: write` to manage the release.' |
| 47 | + required: false |
| 48 | + default: ${{ github.token }} |
| 49 | + |
| 50 | +runs: |
| 51 | + using: composite |
| 52 | + steps: |
| 53 | + - name: Ensure minisign is available |
| 54 | + shell: bash |
| 55 | + run: | |
| 56 | + set -euo pipefail |
| 57 | + if command -v minisign >/dev/null 2>&1; then exit 0; fi |
| 58 | + if command -v apt-get >/dev/null 2>&1; then |
| 59 | + sudo apt-get update && sudo apt-get install -y minisign |
| 60 | + elif command -v brew >/dev/null 2>&1; then |
| 61 | + brew install minisign |
| 62 | + else |
| 63 | + echo "mirror-zig-toolchain: minisign is required but not installed, and no apt-get/brew found." >&2 |
| 64 | + exit 1 |
| 65 | + fi |
| 66 | +
|
| 67 | + - name: Fetch, verify, and upload the pinned toolchain |
| 68 | + shell: bash |
| 69 | + env: |
| 70 | + GH_TOKEN: ${{ inputs.token }} |
| 71 | + IN_VERSION: ${{ inputs.version }} |
| 72 | + IN_VERSION_FILE: ${{ inputs.version-file }} |
| 73 | + IN_PLATFORMS: ${{ inputs.platforms }} |
| 74 | + IN_RELEASE_TAG: ${{ inputs.release-tag }} |
| 75 | + IN_RELEASE_TITLE: ${{ inputs.release-title }} |
| 76 | + IN_MIRROR: ${{ inputs.mirror }} |
| 77 | + run: | |
| 78 | + set -euo pipefail |
| 79 | +
|
| 80 | + # Upstream's minisign public key, from https://ziglang.org/download |
| 81 | + # (the same constant mlugg/setup-zig and xyzzylabs/setup-zig pin). |
| 82 | + MINISIGN_KEY='RWSGOq2NVecA2UPNdBUZykf1CCb147pkmdtYxgb3Ti+JO/wCYvhbAb/U' |
| 83 | + SOURCE_TAG='mirror-zig-toolchain' |
| 84 | +
|
| 85 | + log() { printf '%s\n' "$*" >&2; } |
| 86 | + die() { log "mirror-zig-toolchain: ERROR: $*"; exit 1; } |
| 87 | +
|
| 88 | + if [ -n "$IN_MIRROR" ]; then |
| 89 | + case "$IN_MIRROR" in |
| 90 | + *ziglang.org*) die "ziglang.org cannot be used as a mirror override" ;; |
| 91 | + esac |
| 92 | + fi |
| 93 | +
|
| 94 | + VERSION="$IN_VERSION" |
| 95 | + if [ -z "$VERSION" ]; then |
| 96 | + [ -f "$IN_VERSION_FILE" ] || die "version is empty and $IN_VERSION_FILE not found" |
| 97 | + VERSION="$(grep -m1 '\.minimum_zig_version' "$IN_VERSION_FILE" | sed 's/.*"\(.*\)".*/\1/')" |
| 98 | + [ -n "$VERSION" ] || die "could not read .minimum_zig_version from $IN_VERSION_FILE" |
| 99 | + fi |
| 100 | + log "Zig version to mirror: $VERSION" |
| 101 | +
|
| 102 | + # Live community mirror list, falling back to the hardcoded set |
| 103 | + # (mlugg/setup-zig's fallback list) if ziglang.org is unreachable. |
| 104 | + MIRRORS="$(curl -fsSL --max-time 15 https://ziglang.org/download/community-mirrors.txt || true)" |
| 105 | + if [ -z "$MIRRORS" ]; then |
| 106 | + MIRRORS='https://pkg.machengine.org/zig |
| 107 | + https://zigmirror.hryx.net/zig |
| 108 | + https://zig.linus.dev/zig |
| 109 | + https://zig.squirl.dev |
| 110 | + https://zig.florent.dev |
| 111 | + https://zig.mirror.mschae23.de/zig |
| 112 | + https://zigmirror.meox.dev |
| 113 | + https://ziglang.freetls.fastly.net |
| 114 | + https://zig.tilok.dev |
| 115 | + https://zig-mirror.tsimnet.eu/zig |
| 116 | + https://zig.karearl.com/zig |
| 117 | + https://pkg.earth/zig |
| 118 | + https://fs.liujiacai.net/zigbuilds' |
| 119 | + fi |
| 120 | +
|
| 121 | + case "$VERSION" in |
| 122 | + *-dev*) CANONICAL='https://ziglang.org/builds' ;; |
| 123 | + *) CANONICAL="https://ziglang.org/download/$VERSION" ;; |
| 124 | + esac |
| 125 | +
|
| 126 | + # Verify a downloaded tarball + sidecar against upstream's key and |
| 127 | + # confirm the signed filename matches what we requested. |
| 128 | + verify() { |
| 129 | + local tarball="$1" sig="$2" expected="$3" |
| 130 | + minisign -Vm "$tarball" -x "$sig" -P "$MINISIGN_KEY" >/dev/null 2>&1 || return 1 |
| 131 | + local signed |
| 132 | + signed="$(sed -n '3p' "$sig" | grep -oE 'file:[^[:space:]]+' | head -1 | cut -d: -f2-)" |
| 133 | + [ "$signed" = "$expected" ] || return 1 |
| 134 | + return 0 |
| 135 | + } |
| 136 | +
|
| 137 | + ASSETS=() |
| 138 | + # shellcheck disable=SC2086 |
| 139 | + for plat in $IN_PLATFORMS; do |
| 140 | + case "$plat" in |
| 141 | + *-*) : ;; |
| 142 | + *) die "invalid platform '$plat' (expected <arch>-<os>)" ;; |
| 143 | + esac |
| 144 | + arch="${plat%-*}" |
| 145 | + os="${plat##*-}" |
| 146 | + ext='.tar.xz' |
| 147 | + [ "$os" = 'windows' ] && ext='.zip' |
| 148 | + tarball="zig-${arch}-${os}-${VERSION}${ext}" |
| 149 | + sig="${tarball}.minisig" |
| 150 | + log "== $tarball ==" |
| 151 | +
|
| 152 | + ok=0 |
| 153 | + SOURCES=() |
| 154 | + [ -n "$IN_MIRROR" ] && SOURCES+=("$IN_MIRROR") |
| 155 | + while IFS= read -r m; do |
| 156 | + m="$(printf '%s' "$m" | tr -d '[:space:]')" |
| 157 | + [ -n "$m" ] && SOURCES+=("$m") |
| 158 | + done <<< "$MIRRORS" |
| 159 | + SOURCES+=("$CANONICAL") |
| 160 | +
|
| 161 | + for base in "${SOURCES[@]}"; do |
| 162 | + log " trying $base" |
| 163 | + curl -fsSL --max-time 300 "$base/$tarball?source=$SOURCE_TAG" -o "$tarball" || { rm -f "$tarball"; continue; } |
| 164 | + curl -fsSL --max-time 60 "$base/$sig?source=$SOURCE_TAG" -o "$sig" || { rm -f "$tarball" "$sig"; continue; } |
| 165 | + if verify "$tarball" "$sig" "$tarball"; then |
| 166 | + log " verified from $base" |
| 167 | + ok=1 |
| 168 | + break |
| 169 | + fi |
| 170 | + log " verification failed; discarding" |
| 171 | + rm -f "$tarball" "$sig" |
| 172 | + done |
| 173 | + [ "$ok" = 1 ] || die "could not obtain a verified $tarball from any source" |
| 174 | + ASSETS+=("$tarball" "$sig") |
| 175 | + done |
| 176 | +
|
| 177 | + [ "${#ASSETS[@]}" -gt 0 ] || die "no assets were produced" |
| 178 | +
|
| 179 | + # Checksums over every mirrored tarball (not the sidecars). |
| 180 | + sha256sum zig-*"$VERSION"*.tar.* > SHA256SUMS 2>/dev/null || sha256sum zig-* | grep -v '\.minisig' > SHA256SUMS |
| 181 | + ASSETS+=("SHA256SUMS") |
| 182 | +
|
| 183 | + NOTES="Minisign-verified copies of the Zig toolchain (\`$VERSION\`), for environments that cannot reach ziglang.org or the community mirrors. Every tarball was verified against upstream's minisign key before upload." |
| 184 | +
|
| 185 | + if ! gh release view "$IN_RELEASE_TAG" >/dev/null 2>&1; then |
| 186 | + log "Creating release $IN_RELEASE_TAG" |
| 187 | + gh release create "$IN_RELEASE_TAG" --title "$IN_RELEASE_TITLE" --notes "$NOTES" |
| 188 | + fi |
| 189 | + log "Uploading ${#ASSETS[@]} assets to $IN_RELEASE_TAG" |
| 190 | + gh release upload "$IN_RELEASE_TAG" "${ASSETS[@]}" --clobber |
| 191 | + log "Done." |
0 commit comments