11name : CI
2+ # cspell:ignore rhysd binstall mktemp sha256sum sha256sums shasum
23permissions :
34 contents : read
45concurrency :
2122env :
2223 CARGO_TERM_COLOR : always
2324 RUST_BACKTRACE : 1
25+ ACTIONLINT_VERSION : " 1.7.10"
26+ MARKDOWNLINT_VERSION : " 0.47.0"
27+ CSPELL_VERSION : " 9.4.0"
28+ SHFMT_VERSION : " 3.12.0"
29+ UV_VERSION : " 0.9.21"
2430
2531jobs :
2632 build :
2733 runs-on : ${{ matrix.os }}
2834 strategy :
29- fail-fast : false # Continue other jobs if one fails
35+ fail-fast : false # Continue other jobs if one fails
3036 matrix :
3137 os :
3238 - ubuntu-latest
@@ -41,47 +47,174 @@ jobs:
4147 target : x86_64-pc-windows-msvc
4248
4349 steps :
44- - uses : actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
50+ - uses : actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
4551
4652 - name : Install Rust toolchain
47- uses : actions-rust-lang/setup-rust-toolchain@1780873c7b576612439a134613cc4cc74ce5538c # v1.15.2
53+ uses : actions-rust-lang/setup-rust-toolchain@1780873c7b576612439a134613cc4cc74ce5538c # v1.15.2
4854 with :
4955 target : ${{ matrix.target }}
50- cache : true # Built-in caching; toolchain/components are specified in rust-toolchain.toml
56+ cache : true # Built-in caching
57+ # toolchain, components, etc. are specified in rust-toolchain.toml
5158
5259 - name : Install just
5360 if : matrix.os != 'windows-latest'
54- uses : taiki-e/install-action@bfc291e1e39400b67eda124e4a7b4380e93b3390 # v2.65.0
61+ uses : taiki-e/install-action@4c6723ec9c638cccae824b8957c5085b695c8085 # v2.65.7
5562 with :
5663 tool : just
5764
5865 - name : Install uv (for Python scripts and pytest)
5966 if : matrix.os != 'windows-latest'
60- uses : astral-sh/setup-uv@681c641aba71e4a1c380be3ab5e12ad51f415867 # v7.1.6
67+ uses : astral-sh/setup-uv@681c641aba71e4a1c380be3ab5e12ad51f415867 # v7.1.6
6168 with :
62- version : " 0.9.17 " # Pinned for reproducible CI
69+ version : ${{ env.UV_VERSION }}
6370
6471 - name : Install Node.js (for markdownlint and cspell)
6572 if : matrix.os != 'windows-latest'
6673 uses : actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
6774 with :
68- node-version : ' 20 '
75+ node-version : " 20 "
6976
7077 - name : Install Node.js packages
7178 if : matrix.os != 'windows-latest'
7279 run : |
73- npm install -g markdownlint-cli cspell
80+ npm install -g markdownlint-cli@${{ env.MARKDOWNLINT_VERSION }} cspell@${{ env.CSPELL_VERSION }}
7481
75- - name : Install jq (Linux)
82+ - name : Install taplo (for TOML formatting and linting)
83+ if : matrix.os != 'windows-latest'
84+ uses : taiki-e/install-action@4c6723ec9c638cccae824b8957c5085b695c8085 # v2.65.7
85+ with :
86+ tool : taplo-cli
87+
88+ - name : Install actionlint (Linux/macOS)
89+ if : matrix.os != 'windows-latest'
90+ run : |
91+ set -euo pipefail
92+
93+ # actionlint is published as prebuilt binaries (Go), not a Rust crate.
94+ # Install directly from rhysd/actionlint releases to avoid cargo-binstall fallback failures.
95+ # Verify SHA256 checksums from the upstream release for supply-chain hardening.
96+ OS="$(uname -s)"
97+ ARCH="$(uname -m)"
98+
99+ case "$OS" in
100+ Linux) ACTIONLINT_OS="linux" ;;
101+ Darwin) ACTIONLINT_OS="darwin" ;;
102+ *)
103+ echo "Unsupported OS for actionlint: $OS" >&2
104+ exit 1
105+ ;;
106+ esac
107+
108+ case "$ARCH" in
109+ x86_64|amd64) ACTIONLINT_ARCH="amd64" ;;
110+ arm64|aarch64) ACTIONLINT_ARCH="arm64" ;;
111+ *)
112+ echo "Unsupported architecture for actionlint: $ARCH" >&2
113+ exit 1
114+ ;;
115+ esac
116+
117+ verify_sha256() {
118+ local checksum_file="$1"
119+ if command -v sha256sum >/dev/null 2>&1; then
120+ sha256sum -c "$checksum_file"
121+ else
122+ shasum -a 256 -c "$checksum_file"
123+ fi
124+ }
125+
126+ VERSION="${ACTIONLINT_VERSION}"
127+ TARBALL="actionlint_${VERSION}_${ACTIONLINT_OS}_${ACTIONLINT_ARCH}.tar.gz"
128+ CHECKSUMS_FILE="actionlint_${VERSION}_checksums.txt"
129+ BASE_URL="https://github.com/rhysd/actionlint/releases/download/v${VERSION}"
130+
131+ tmpdir="$(mktemp -d)"
132+ trap 'rm -rf "$tmpdir"' EXIT
133+
134+ curl -fsSL "${BASE_URL}/${TARBALL}" -o "$tmpdir/$TARBALL"
135+ curl -fsSL "${BASE_URL}/${CHECKSUMS_FILE}" -o "$tmpdir/$CHECKSUMS_FILE"
136+
137+ orig_dir="$PWD"
138+ cd "$tmpdir"
139+ awk -v f="$TARBALL" '$NF==f {print; found=1} END {exit found?0:1}' "$CHECKSUMS_FILE" > checksum.txt
140+ verify_sha256 checksum.txt
141+ cd "$orig_dir"
142+
143+ tar -xzf "$tmpdir/$TARBALL" -C "$tmpdir"
144+
145+ actionlint_path="$(find "$tmpdir" -type f -name actionlint | head -n 1)"
146+ if [[ -z "$actionlint_path" ]]; then
147+ echo "actionlint binary not found in $TARBALL" >&2
148+ exit 1
149+ fi
150+
151+ sudo install -m 0755 "$actionlint_path" /usr/local/bin/actionlint
152+
153+ - name : actionlint -version
154+ if : matrix.os != 'windows-latest'
155+ run : actionlint -version
156+
157+ - name : Install additional tools (Linux)
76158 if : matrix.os == 'ubuntu-latest'
77159 run : |
160+ # Install shellcheck, jq, and yamllint
78161 sudo apt-get update
79- sudo apt-get install -y jq
162+ sudo apt-get install -y shellcheck jq yamllint
163+
164+ # Install shfmt (pinned for CI consistency)
165+ SHFMT_ASSET="shfmt_v${SHFMT_VERSION}_linux_amd64"
166+ SHFMT_BASE_URL="https://github.com/mvdan/sh/releases/download/v${SHFMT_VERSION}"
167+
168+ tmpdir="$(mktemp -d)"
169+ trap 'rm -rf "$tmpdir"' EXIT
170+
171+ curl -fsSL \
172+ "${SHFMT_BASE_URL}/${SHFMT_ASSET}" \
173+ -o "$tmpdir/${SHFMT_ASSET}"
174+ curl -fsSL \
175+ "${SHFMT_BASE_URL}/sha256sums.txt" \
176+ -o "$tmpdir/sha256sums.txt"
177+
178+ (
179+ cd "$tmpdir"
180+ awk -v f="${SHFMT_ASSET}" '$NF==f {print; found=1} END {exit found?0:1}' sha256sums.txt > checksum.txt
181+ sha256sum -c checksum.txt
182+ )
80183
81- - name : Install jq (macOS)
184+ sudo install -m 0755 "$tmpdir/${SHFMT_ASSET}" /usr/local/bin/shfmt
185+
186+ - name : Install additional tools (macOS)
82187 if : matrix.os == 'macos-latest'
83188 run : |
84- brew install jq
189+ # Install shellcheck, jq, and yamllint via Homebrew
190+ brew install shellcheck jq yamllint
191+
192+ # Install shfmt (pinned for CI consistency with Linux)
193+ SHFMT_ARCH="amd64"
194+ if [[ "$(uname -m)" == "arm64" ]]; then
195+ SHFMT_ARCH="arm64"
196+ fi
197+
198+ SHFMT_ASSET="shfmt_v${SHFMT_VERSION}_darwin_${SHFMT_ARCH}"
199+ SHFMT_BASE_URL="https://github.com/mvdan/sh/releases/download/v${SHFMT_VERSION}"
200+
201+ tmpdir="$(mktemp -d)"
202+ trap 'rm -rf "$tmpdir"' EXIT
203+
204+ curl -fsSL \
205+ "${SHFMT_BASE_URL}/${SHFMT_ASSET}" \
206+ -o "$tmpdir/${SHFMT_ASSET}"
207+ curl -fsSL \
208+ "${SHFMT_BASE_URL}/sha256sums.txt" \
209+ -o "$tmpdir/sha256sums.txt"
210+
211+ (
212+ cd "$tmpdir"
213+ awk -v f="${SHFMT_ASSET}" '$NF==f {print; found=1} END {exit found?0:1}' sha256sums.txt > checksum.txt
214+ shasum -a 256 -c checksum.txt
215+ )
216+
217+ sudo install -m 0755 "$tmpdir/${SHFMT_ASSET}" /usr/local/bin/shfmt
85218
86219 - name : Run CI checks (Linux/macOS)
87220 if : matrix.os != 'windows-latest'
0 commit comments