name: Release # Build binaries for a tag and attach them to a GitHub release. # # WHY BINARIES AT ALL, given `cargo install orgo` exists: installing from source needs # a Rust toolchain and about a minute of compiling syntect. Someone evaluating a site # generator should be able to download one file and point it at their notes. # # The tag is the source of truth for the version. The build checks it against Cargo.toml # rather than trusting them to match, because a release tagged v0.18.0 containing a binary # that reports 0.17.0 is the kind of thing nobody notices for months. on: push: tags: ["v*"] workflow_dispatch: inputs: tag: description: "Tag to build (for a re-run of a failed release)" required: true env: CARGO_TERM_COLOR: always jobs: build: name: ${{ matrix.target }} runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: include: # Apple Silicon and Intel Macs, built natively on their own runners so neither # is a cross-compile nobody has run. - { os: macos-latest, target: aarch64-apple-darwin } - { os: macos-13, target: x86_64-apple-darwin } # glibc for ordinary distributions, musl for containers and anything older than # the runner's glibc — a dynamically linked binary is the usual reason a # download does not run. - { os: ubuntu-latest, target: x86_64-unknown-linux-gnu } - { os: ubuntu-latest, target: x86_64-unknown-linux-musl } steps: - name: Checkout uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: ref: ${{ github.event.inputs.tag || github.ref }} - name: Install Rust uses: dtolnay/rust-toolchain@1ff72ee08e3cb84d84adba594e0a297990fc1ed3 # stable with: toolchain: stable targets: ${{ matrix.target }} - name: Install musl tools if: endsWith(matrix.target, '-musl') run: sudo apt-get update && sudo apt-get install -y --no-install-recommends musl-tools - name: Check the tag against Cargo.toml shell: bash run: | tag="${{ github.event.inputs.tag || github.ref_name }}" crate=$(cargo metadata --no-deps --format-version 1 \ | python3 -c 'import json,sys; print(json.load(sys.stdin)["packages"][0]["version"])') if [ "$tag" != "v$crate" ]; then echo "tag $tag does not match Cargo.toml version $crate" >&2 exit 1 fi - name: Build run: cargo build --release --locked --target ${{ matrix.target }} # A tarball rather than a bare binary: it keeps the executable bit through GitHub's # download path, and carries the licence with the thing it licenses. - name: Package shell: bash run: | staging="orgo-${{ github.event.inputs.tag || github.ref_name }}-${{ matrix.target }}" mkdir "$staging" cp "target/${{ matrix.target }}/release/orgo" "$staging/" cp README.md LICENSE "$staging/" tar czf "$staging.tar.gz" "$staging" shasum -a 256 "$staging.tar.gz" > "$staging.tar.gz.sha256" - name: Upload uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: ${{ matrix.target }} path: | *.tar.gz *.tar.gz.sha256 release: name: publish the release needs: build runs-on: ubuntu-latest permissions: contents: write steps: - name: Download every build uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: merge-multiple: true # A draft, deliberately. The release notes are written by a person, and a release # that publishes itself before anyone has read it cannot be edited quietly. - name: Create the draft release uses: softprops/action-gh-release@3d0d9888cb7fd7b750713d6e236d1fcb99157228 # v3.0.2 with: draft: true tag_name: ${{ github.event.inputs.tag || github.ref_name }} files: | *.tar.gz *.tar.gz.sha256 publish: name: publish to crates.io needs: build runs-on: ubuntu-latest # Named so it can be gated. Adding a required reviewer to this environment in the # repository settings turns a tag push into "waiting for a human", which is the right # shape for the one step in this workflow that cannot be undone: a published version # can be yanked but never replaced. environment: crates-io permissions: # The OIDC token this mints *is* the credential — there is no API token stored in # this repository, and nothing to leak from a compromised job beyond a token that # crates.io revokes when the job ends. id-token: write contents: read steps: - name: Checkout uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: ref: ${{ github.event.inputs.tag || github.ref }} - name: Install Rust uses: dtolnay/rust-toolchain@1ff72ee08e3cb84d84adba594e0a297990fc1ed3 # stable with: toolchain: stable # Exchanges GitHub's OIDC token for a short-lived crates.io token, and revokes it # when the job finishes. Configured on crates.io against this repository, this # workflow's filename, and the environment above. - name: Authenticate with crates.io id: auth uses: rust-lang/crates-io-auth-action@c6f97d42243bad5fab37ca0427f495c86d5b1a18 # v1.0.5 # `--locked` publishes exactly the dependency versions the tests ran against, rather # than whatever resolves at publish time. - name: Publish run: cargo publish --locked env: CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }}