Skip to content

fix(rules): flag removed top-level iii sandbox with replacement; ad… #59

fix(rules): flag removed top-level iii sandbox with replacement; ad…

fix(rules): flag removed top-level iii sandbox with replacement; ad… #59

Workflow file for this run

name: release
on:
push:
# Patch tags only (X.Y.Z), not the X.Y / X floats. `on.push.tags`
# uses glob syntax (NOT regex), so `+` and `[0-9]` quantifier-style
# forms don't work — `v*.*.*` is the semver-shaped glob that
# requires two dots and excludes `v0.4` / `v0`.
#
# Force-pushing `v0.4` to roll the float forward previously fired
# this workflow with `v*` and created a duplicate release titled
# "v0.4", which broke `/releases/latest` resolution for consumers
# (ci-install.sh couldn't parse the two-component version).
tags: ["v*.*.*"]
workflow_dispatch:
inputs:
tag:
description: "Tag to release (e.g. v0.1.0)"
required: true
permissions:
contents: write
env:
CARGO_TERM_COLOR: always
jobs:
build:
name: build ${{ matrix.target }}
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: false
matrix:
include:
- target: aarch64-apple-darwin
# macos-14 was hitting a consistent setup-rust-toolchain
# failure where `cargo` on PATH resolved to `rustup-init`
# (the installer) instead of the rustup proxy — every
# `cargo metadata` / `cargo build` invocation errored with
# "unexpected argument 'metadata'/'build' found" against
# rustup-init's flag set. Image was broken across two
# back-to-back v0.4.2 release attempts on 2026-05-14.
# macos-15 is the current Apple-silicon runner and works.
runner: macos-15
use_cross: false
# x86_64-apple-darwin temporarily removed — GitHub's macos-13
# runners aren't reliably building this target. Re-add when the
# runner availability stabilises (Intel Mac users can use the
# linux-x86_64 binary via Rosetta in the meantime).
- target: x86_64-unknown-linux-gnu
runner: ubuntu-latest
use_cross: false
- target: aarch64-unknown-linux-gnu
runner: ubuntu-latest
use_cross: true
- target: x86_64-unknown-linux-musl
runner: ubuntu-latest
use_cross: false
apt: musl-tools
- target: aarch64-unknown-linux-musl
runner: ubuntu-latest
use_cross: true
steps:
- uses: actions/checkout@v5
- uses: actions-rust-lang/setup-rust-toolchain@v1
with:
target: ${{ matrix.target }}
# Cache ~/.cargo/{registry,git} and target/. Keyed on Cargo.lock +
# target triple so a `cargo update` invalidates correctly. Cross-rs
# targets benefit primarily from the registry cache (the build itself
# runs inside a Docker container, so target/ reuse there is partial).
- uses: Swatinem/rust-cache@v2
with:
key: ${{ matrix.target }}
shared-key: release
# The macos GitHub runner images (both macos-14 and macos-15)
# intermittently leave the cargo proxy in a broken state where
# `cargo build` fails with "rustup-init: unexpected argument
# 'build' found" — the proxy is resolving to the multi-call
# installer binary with no default toolchain set, so it can't
# dispatch the subcommand. setup-rust-toolchain reports success
# but the next cargo invocation fails.
#
# The recovery must run *after* Swatinem/rust-cache@v2 because
# rust-cache has `cache-bin: true` and will restore a stale
# `~/.cargo/bin` from a previous cache hit, overwriting any
# earlier reinstall. We run last so whatever rust-cache restored
# is the final state we're fixing up.
#
# Probe with a real subcommand. `cargo metadata --no-deps
# --format-version 1` requires a working toolchain to parse the
# manifest — rustup-init's multi-call binary can't fake it.
# `cargo --version` is fooled because rustup-init recognizes
# `--version` as one of its own flags and prints with exit 0.
#
# Linux runners haven't shown this flake; gating on apple-darwin
# keeps the workaround surgical and the linux builds free of the
# extra step. Healthy macos runs hit the probe-passes path and
# no-op past the curl.
- name: Recover from broken rustup state (macos)
if: contains(matrix.target, 'apple-darwin')
shell: bash
run: |
if ! cargo metadata --no-deps --format-version 1 >/dev/null 2>&1; then
echo "::warning::cargo proxy is broken (rustup-init shadowing with no default toolchain). Reinstalling stable."
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \
| sh -s -- -y --no-modify-path --default-toolchain stable --profile minimal
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
export PATH="$HOME/.cargo/bin:$PATH"
rustup default stable
rustup target add "${{ matrix.target }}"
fi
rustup show
cargo metadata --no-deps --format-version 1 >/dev/null
echo "cargo proxy verified"
which cargo
- name: Install apt packages
if: matrix.apt
run: |
sudo apt-get update
sudo apt-get install -y ${{ matrix.apt }}
- name: Install cross
if: matrix.use_cross
run: cargo install cross --locked
- name: Resolve version
id: ver
shell: bash
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
tag="${{ inputs.tag }}"
else
tag="${GITHUB_REF_NAME}"
fi
version="${tag#v}"
echo "tag=$tag" >> "$GITHUB_OUTPUT"
echo "version=$version" >> "$GITHUB_OUTPUT"
- name: Build
shell: bash
env:
# Bake the released tag's version (without the v prefix) into the
# binary via option_env!("RELEASE_VERSION"). The runtime update
# check uses this to decide whether the running binary is current.
RELEASE_VERSION: ${{ steps.ver.outputs.version }}
run: |
if [ "${{ matrix.use_cross }}" = "true" ]; then
cross build --release --workspace --target ${{ matrix.target }}
else
cargo build --release --workspace --target ${{ matrix.target }}
fi
- name: Package tarball
id: pkg
shell: bash
run: |
name="skills-and-validation-${{ steps.ver.outputs.version }}-${{ matrix.target }}"
mkdir -p "$name/bin" "$name/content" "$name/templates"
cp "target/${{ matrix.target }}/release/iii-skill-check" "$name/bin/"
cp "target/${{ matrix.target }}/release/iii-skill-render" "$name/bin/"
cp -r content/. "$name/content/"
cp -r templates/. "$name/templates/"
echo "${{ steps.ver.outputs.version }}" > "$name/VERSION"
tar -czf "$name.tar.gz" "$name"
if command -v sha256sum >/dev/null 2>&1; then
sha256sum "$name.tar.gz" > "$name.tar.gz.sha256"
else
shasum -a 256 "$name.tar.gz" > "$name.tar.gz.sha256"
fi
cat "$name.tar.gz.sha256"
echo "name=$name" >> "$GITHUB_OUTPUT"
- name: Upload artifact
uses: actions/upload-artifact@v5
with:
name: ${{ steps.pkg.outputs.name }}
path: |
${{ steps.pkg.outputs.name }}.tar.gz
${{ steps.pkg.outputs.name }}.tar.gz.sha256
retention-days: 7
if-no-files-found: error
release:
name: publish release
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- name: Download all artifacts
uses: actions/download-artifact@v5
with:
path: dist
merge-multiple: true
- name: Resolve tag
id: ver
shell: bash
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
tag="${{ inputs.tag }}"
else
tag="${GITHUB_REF_NAME}"
fi
echo "tag=$tag" >> "$GITHUB_OUTPUT"
- name: Create or update release
env:
GH_TOKEN: ${{ github.token }}
shell: bash
run: |
tag="${{ steps.ver.outputs.tag }}"
if ! gh release view "$tag" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then
gh release create "$tag" \
--repo "$GITHUB_REPOSITORY" \
--title "$tag" \
--generate-notes
fi
for f in dist/*.tar.gz dist/*.tar.gz.sha256; do
[ -f "$f" ] || continue
gh release upload "$tag" "$f" --repo "$GITHUB_REPOSITORY" --clobber
done
# Re-point floating tags so consumers can pin a moving target:
# `vMAJOR.MINOR` (e.g. v0.1) tracks every patch on its line —
# used by `uses: iii-hq/skills-and-validation@v0.1` and
# `version: 0.1` in .skill-check.yaml.
# `latest` tracks the most recent stable release across all
# versions — used by install.sh's default install path.
- name: Force-update floating tags
env:
GH_TOKEN: ${{ github.token }}
shell: bash
run: |
tag="${{ steps.ver.outputs.tag }}"
if [[ ! "$tag" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "tag '$tag' is not vMAJOR.MINOR.PATCH; skipping floating tags"
exit 0
fi
sha="${{ github.sha }}"
minor=$(echo "$tag" | sed -E 's/^(v[0-9]+\.[0-9]+)\.[0-9]+$/\1/')
repoint() {
local ref="$1"
if gh api "repos/${GITHUB_REPOSITORY}/git/ref/tags/${ref}" >/dev/null 2>&1; then
gh api -X PATCH "repos/${GITHUB_REPOSITORY}/git/refs/tags/${ref}" \
-f sha="$sha" -F force=true
echo "updated floating tag ${ref} -> $sha"
else
gh api -X POST "repos/${GITHUB_REPOSITORY}/git/refs" \
-f ref="refs/tags/${ref}" -f sha="$sha"
echo "created floating tag ${ref} -> $sha"
fi
}
repoint "$minor"
repoint latest