1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
name: release
on:
push:
tags:
- 'v*.*.*'
# EVERY JOB READS, AND ONE JOB WRITES (#329).
#
# `contents: write` is held by the `cli` job alone, which calls
# rust-fs-core's release-cli workflow; there, only its `attach` job holds
# it, and that job checks nothing out. Every action here is pinned to
# a commit SHA, with the tag it stood for beside it: a tag is a pointer its
# owner can move, and a job holding a token should not run whatever it
# points at today. Every checkout sets `persist-credentials: false`, so no
# later step finds the token in .git/config.
# tests/scripts/test-write-jobs-pinned.sh fails a write job that runs an
# unpinned action or persists its token.
permissions:
contents: read
env:
CARGO_TERM_COLOR: always
CHORE_VERSION: "0.11.0"
jobs:
test:
name: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0
with:
persist-credentials: false
- name: verify tag matches Cargo.toml version
run: |
tag="${GITHUB_REF_NAME#v}"
crate_ver=$(grep -m1 '^version' Cargo.toml | sed -E 's/version *= *"(.*)"/\1/')
if [ "$tag" != "$crate_ver" ]; then
echo "tag $tag does not match Cargo.toml version $crate_ver" >&2
exit 1
fi
# The same gate as a pull request's CI, through the same chore
# tasks: the siblings at their pinned refs (rust-fs-core is the
# path dependency), the oracle tools, the kernel-made fixtures in
# the fs-linux-test-harness VM (ubuntu-latest is x86_64 with KVM),
# lint, and the whole suite -- which includes the e2fsck checks of
# the mkfs_ext4 binary (tests/mkfs_bin_fsck_oracle.rs) that used to
# be a job of their own here.
- name: install chore
run: scripts/ci-install-chore.sh
- name: chore siblings
run: chore siblings
- uses: dtolnay/rust-toolchain@6bed0761d98439e5a578e2877258200ad565ba87 # stable, 2026-09-27
with:
toolchain: 1.95.0
components: clippy, rustfmt
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
# The VM host first: `chore tools` fails a Linux host without
# Vagrant, QEMU and /dev/kvm, and this is the step that installs
# them (tests/scripts/test-vm-host-before-tools.sh).
- name: KVM, QEMU and Vagrant
run: ../fs-linux-test-harness/scripts/ci-setup-linux.sh
- name: chore tools
run: chore tools
- name: chore fixtures
run: chore fixtures
- name: chore lint
run: chore lint
- name: chore test
run: chore test
# THE RELEASE TARBALL is rust-fs-core's release-cli workflow, called here
# (#481). It builds the multi-call binary natively on darwin-arm64 and
# linux-x86_64, packages it with `scripts/core.sh package-cli` from what
# Cargo.toml's [package.metadata.package-cli] declares -- bin/rust-fs-ext4,
# mkfs.ext4, fsck.ext4 and fs.ext4 as relative symlinks to it, the man
# pages and completions the binary writes, share/rust-fs-ext4/CAVEATS and
# LICENSE -- and checks it; then its one write job attests both tarballs
# and attaches them to this tag's release. This repository once carried
# its own copy of all of that, and the family's copies drifted.
#
# Pinned to a commit SHA, with the tag it stood for beside it: the called
# workflow runs with the grants below, and a tag can be moved. core-ref
# is the tag chores.yml pins the sibling to, and toolchain the one
# rust-toolchain.toml pins; tests/ci_profile.rs holds all three to that.
#
# The attestation is signed by the called workflow, so a download is
# checked with `gh attestation verify <tarball> --repo
# antimatter-studios/rust-fs-ext4 --signer-workflow
# antimatter-studios/rust-fs-core/.github/workflows/release-cli.yml`.
cli:
needs:
permissions:
contents: write
id-token: write
attestations: write
uses: antimatter-studios/rust-fs-core/.github/workflows/release-cli.yml@9e900154a7dff2a6c8f136db31def33bed9f09cc # v0.3.0
with:
core-ref: v0.3.0
toolchain: 1.95.0
publish:
name: publish to crates.io
needs:
runs-on: ubuntu-latest
environment: release
permissions:
id-token: write
contents: read
steps:
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0
with:
persist-credentials: false
# The path dependency, at the ref chores.yml pins (read from there
# rather than restated; chore itself has no build for every runner
# this job uses).
- name: clone rust-fs-core sibling
shell: bash
run: |
set -euo pipefail
pin() { sed -n "s/^ $1: *//p" chores.yml | tr -d '\r'; }
git clone --depth 1 --branch "$(pin FS_CORE_REF)" "$(pin FS_CORE_URL)" ../rust-fs-core
- uses: dtolnay/rust-toolchain@6bed0761d98439e5a578e2877258200ad565ba87 # stable, 2026-09-27
with:
toolchain: 1.95.0
# A version already on crates.io cannot be published again, and a
# re-tag — or a release published by hand — would otherwise fail
# this job every time. Skip instead, so the workflow is idempotent
# and a red run means something actually went wrong.
#
# 200 is published and 404 is not; anything else -- a 429, a 5xx, a
# timeout -- is retried and then fails the step, rather than being
# read as "not published" and sent on to a `cargo publish` that
# fails with "already exists" (#329).
# tests/scripts/test-crates-io-check.sh runs this step against a
# stubbed curl.
- name: skip if this version is already on crates.io
id: already
shell: bash
run: scripts/crates-io-published.sh >> "$GITHUB_OUTPUT"
- uses: rust-lang/crates-io-auth-action@bbd81622f20ce9e2dd9622e3218b975523e45bbe # v1.0.4
if: steps.already.outputs.published == 'false'
id: auth
- name: cargo publish
if: steps.already.outputs.published == 'false'
env:
CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }}
# No `--locked`: publish must rewrite the `rust-fs-core` path
# dependency into its registry equivalent in the packaged
# lockfile, which `--locked` forbids ("cannot update the lock
# file"). The verify-build resolves rust-fs-core from crates.io
# via the `version` constraint, so the published artifact is
# pinned by that constraint, not by the local path checkout.
run: cargo publish