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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
name: CI
'on':
pull_request:
types:
workflow_dispatch:
# A push to a pull request supersedes the run already in flight, and there is
# nothing left to learn from the superseded one. Cancellation is conditioned
# on the event so a push to `main`, a schedule, or a dispatch, none of which
# has a successor waiting, always runs to completion.
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
jobs:
build-test:
runs-on: ubuntu-latest
permissions:
contents: read
env:
CARGO_TERM_COLOR: always
BUILD_PROFILE: debug
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
- name: Setup Rust
uses: leynos/shared-actions/.github/actions/setup-rust@6dea5677a84fec60ca51b07202570e3af12ffdb4
- name: Install ripgrep
run: cargo binstall --no-confirm --locked ripgrep@15.2.0
- name: Install mdtablefix
# `make check-fmt` runs `mdtablefix --check --git`; 0.6.0 is the
# first release with `--check` and `--git`.
uses: leynos/shared-actions/.github/actions/install-mdtablefix@a5765019912a8ab6882b12db049c7cde635f3a85
with:
version: "0.6.0"
- name: Format
run: make check-fmt
- name: Markdown lint
# The comment said v20 and the SHA does not: `21c1be1b` is the commit
# the `v24.2.0` tag object points at, whose message is "Update to
# version 24.2.0." The pin is unchanged; only the label was wrong.
uses: DavidAnson/markdownlint-cli2-action@21c1be1b93ad9ed58fa840aacc3f279cde2a72ff # v24.2.0
with:
globs: '**/*.md'
- name: Lint
run: make lint
- name: Test and Measure Coverage
# Coverage here is for the local ratchet and nothing else. The report
# is neither published (`publish-artefact: 'false'` keeps the action
# from archiving it) nor sent to CodeScene: a pull request from a
# fork cannot read `secrets.CS_ACCESS_TOKEN`, so an upload step here is
# silently inert for exactly the changes that most need reviewing, and
# a lane that holds the token at all is a lane a fork's workflow edit
# could try to reach. `coverage-main.yml` owns both persistent outputs.
#
# The baseline paths are the action's defaults, which is what makes
# this ratchet comparable with the one the main publisher writes: both
# read and write `.coverage-baseline.rust`.
uses: leynos/shared-actions/.github/actions/generate-coverage@a5765019912a8ab6882b12db049c7cde635f3a85
with:
output-path: lcov.info
format: lcov
with-ratchet: 'true'
publish-artefact: 'false'
windows-atomic-contract:
# The atomic replacement contract is platform-sensitive. The swap depends
# on rename semantics that differ between POSIX and Windows, and on the
# destination's read-only state, which Windows records as a file attribute
# rather than as a mode bit. The suites that assert it drive the library
# helpers and the CLI against a read-only destination, so they run on the
# platform whose semantics they assert. The whole suite runs here too, so a
# change that is correct on Unix but not on Windows is caught by the same
# job rather than by the release that follows it.
name: atomic write contract (windows)
runs-on: windows-latest
permissions:
contents: read
defaults:
run:
shell: bash
env:
CARGO_TERM_COLOR: always
RUSTFLAGS: "-D warnings"
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
- name: Setup Rust
uses: leynos/shared-actions/.github/actions/setup-rust@6dea5677a84fec60ca51b07202570e3af12ffdb4
with:
install-binstall: false
use-sccache: false
- name: Test the atomic write contract
# `--lib` is required, not incidental: the rollback a failed swap
# performs after the destination was prepared is unreachable through a
# real rename, so it is covered by a private, test-only seam in
# `src/io.rs`, which only a unit-test build compiles.
run: cargo test --all-features --no-fail-fast --lib --test rewrite_atomic --test in_place_atomic
- name: Test the whole suite
# Every target reports, rather than stopping at the first one that
# fails. The checkout keeps the repository's LF line endings — see
# `.gitattributes`, without which Git for Windows hands the suites
# CRLF fixtures — so the byte-for-byte fixture and snapshot
# comparisons match the output this tool writes here too.
run: cargo test --all-features --no-fail-fast
binstall-packaging:
# Runner-backed dry run of the release packaging. It proves the crate
# builds on each release platform and that the staged assets match the
# cargo-binstall templates in Cargo.toml before a tag depends on it.
name: binstall packaging (${{ matrix.target }})
runs-on: ${{ matrix.runner }}
permissions:
contents: read
defaults:
run:
shell: bash
strategy:
fail-fast: false
matrix:
include:
- runner: ubuntu-latest
os: linux
arch: x86_64
target: x86_64-unknown-linux-gnu
- runner: macos-15
os: macos
arch: aarch64
target: aarch64-apple-darwin
# macOS runners are Apple silicon, so this row is the only check
# that the Intel cross-compile the release publishes still links.
- runner: macos-15
os: macos
arch: x86_64
target: x86_64-apple-darwin
is-foreign: true
- runner: windows-latest
os: windows
arch: x86_64
target: x86_64-pc-windows-msvc
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
- name: Setup Rust
uses: leynos/shared-actions/.github/actions/setup-rust@6dea5677a84fec60ca51b07202570e3af12ffdb4
with:
toolchain: 1.89.0
install-binstall: false
use-sccache: false
- name: Add release target
run: rustup target add --toolchain 1.89.0 ${{ matrix.target }}
- name: Build the release binary
run: cargo +1.89.0 build --release --bin mdtablefix --target ${{ matrix.target }}
- name: Stage the release assets
run: |
set -euo pipefail
binary_ext=""
if [[ "${{ matrix.target }}" == *windows* ]]; then
binary_ext=".exe"
fi
python_bin=python3
command -v python3 >/dev/null 2>&1 || python_bin=python
version="$("${python_bin}" -c \
'import pathlib, tomllib; print(tomllib.loads(pathlib.Path("Cargo.toml").read_text(encoding="utf-8"))["package"]["version"])')"
"${python_bin}" scripts/package_release_artifacts.py \
--binary "target/${{ matrix.target }}/release/mdtablefix${binary_ext}" \
--artifact-dir "dist/${{ matrix.os }}-${{ matrix.arch }}" \
--version "${version}" \
--target "${{ matrix.target }}" \
--os "${{ matrix.os }}" \
--arch "${{ matrix.arch }}"
"${python_bin}" scripts/verify_binstall_layout.py \
--artifact-dir "dist/${{ matrix.os }}-${{ matrix.arch }}" \
--target "${{ matrix.target }}"
- name: Run the packaged binary
# Skipped where the target is not the runner's own architecture.
if: ${{ !matrix.is-foreign }}
run: |
set -euo pipefail
binary_ext=""
if [[ "${{ matrix.target }}" == *windows* ]]; then
binary_ext=".exe"
fi
python_bin=python3
command -v python3 >/dev/null 2>&1 || python_bin=python
staged="dist/${{ matrix.os }}-${{ matrix.arch }}"
# Extract through Python: the three runner images ship different tar
# implementations, and only the archive contents are under test here.
"${python_bin}" -c 'import pathlib, sys, tarfile; archive = next(pathlib.Path(sys.argv[1]).glob("*.tar.gz")); tarfile.open(archive).extractall(sys.argv[2], filter="data")' \
"${staged}" "${RUNNER_TEMP}"
chmod +x "${RUNNER_TEMP}/mdtablefix${binary_ext}"
"${RUNNER_TEMP}/mdtablefix${binary_ext}" --version