name: Release
on:
push:
tags:
- "[0-9]+.[0-9]+.[0-9]+"
- "[0-9]+.[0-9]+.[0-9]+-*"
workflow_dispatch:
permissions:
contents: write
jobs:
check:
name: Check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
path: tw
- name: The tag and Cargo.toml have to agree
working-directory: tw
run: |
set -eu
case "${GITHUB_REF}" in
refs/tags/*) ;;
*) echo "::error::${GITHUB_REF} is not a tag." >&2; exit 1 ;;
esac
# `tw --version` is what a writer reports in a bug, so a release
# whose binary disagrees with its tag is one nobody can reason
# about afterwards.
tag="${GITHUB_REF_NAME%%-*}"
version="$(sed -n 's/^version = "\(.*\)"/\1/p' Cargo.toml | head -1)"
[ "$version" = "$tag" ] || {
echo "::error::Cargo.toml says ${version}, tag says ${tag}" >&2; exit 1; }
echo "tw ${tag}"
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
- uses: Swatinem/rust-cache@v2
with:
workspaces: tw
- name: check.sh
run: bash tw/.github/scripts/check.sh tw
build:
name: ${{ matrix.slug }}
needs: check
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- { slug: linux-x86_64, os: ubuntu-latest, target: x86_64-unknown-linux-musl, exe: "" }
- { slug: windows-x86_64, os: windows-latest, target: x86_64-pc-windows-msvc, exe: ".exe" }
- { slug: macos-aarch64, os: macos-latest, target: aarch64-apple-darwin, exe: "" }
- { slug: macos-x86_64, os: macos-latest, target: x86_64-apple-darwin, exe: "" }
steps:
- uses: actions/checkout@v4
with:
path: tw
- uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- uses: Swatinem/rust-cache@v2
with:
workspaces: tw
key: ${{ matrix.slug }}
- name: musl toolchain
if: matrix.target == 'x86_64-unknown-linux-musl'
run: sudo apt-get update && sudo apt-get install -y musl-tools
- name: Build
shell: bash
working-directory: tw
run: |
set -eu
if [ "${{ matrix.target }}" = "x86_64-unknown-linux-musl" ]; then
export CC_x86_64_unknown_linux_musl=musl-gcc
export RUSTFLAGS="-C target-feature=+crt-static"
fi
cargo build --release --target "${{ matrix.target }}"
- name: Name it for the platform
shell: bash
run: |
set -eu
asset="tw-${{ matrix.slug }}${{ matrix.exe }}"
cp "tw/target/${{ matrix.target }}/release/tw${{ matrix.exe }}" "$asset"
# No pipeline. `set -e` does not fire on a non-final command in
# one, so a hasher that failed would leave an EMPTY .sha256 and
# this step would report success -- and tw refuses a download it
# cannot verify, so every consumer would break on a release that
# looked fine here. The asset is already named for the platform,
# so neither hasher needs its output rewritten.
# sha256sum on Linux and Git Bash, shasum on macOS.
if command -v sha256sum >/dev/null 2>&1; then
sha256sum "$asset" > "$asset.sha256"
else
shasum -a 256 "$asset" > "$asset.sha256"
fi
# The step checks its own work: a published checksum that does
# not check out is worse than none, because a consumer that
# verifies refuses the release and one that does not never
# finds out.
if command -v sha256sum >/dev/null 2>&1; then
sha256sum -c "$asset.sha256"
else
shasum -a 256 -c "$asset.sha256"
fi
echo "--- $asset: $(wc -c < "$asset") bytes"
- name: It runs, and it finds things
if: matrix.slug != 'macos-x86_64'
shell: bash
run: |
set -eu
asset="./tw-${{ matrix.slug }}${{ matrix.exe }}"
chmod +x "$asset"
"$asset" --version
mkdir -p corpus
: > corpus/markdoc-lint.toml
printf '# Not fine\n\n{%% callout %%}\nbody\n' > corpus/bad.md
set +e
"$asset" check --no-prose --json --root corpus > report.json
code=$?
set -e
cat report.json
# Exit 1 is "found things", and it is what a hook and a CI step
# both read. A green run on a corpus with a broken tag in it is
# the failure this step exists to catch.
[ "$code" = 1 ] || { echo "::error::tw check exited ${code} on a corpus with an unclosed tag" >&2; exit 1; }
grep -q '"rule": "unclosed-tag"' report.json || {
echo "::error::tw check did not report the unclosed tag" >&2; exit 1; }
- name: Publish to the release
uses: softprops/action-gh-release@v2
with:
files: |
tw-${{ matrix.slug }}${{ matrix.exe }}
tw-${{ matrix.slug }}${{ matrix.exe }}.sha256
prerelease: ${{ contains(github.ref_name, '-') }}