krz/orgo

Lightning fast org-mode static site generator.

clone: git clone https://gitbay.org/krz/orgo.git

v0.20.1: .github/workflows/release.yml · raw

  1name: Release
  2
  3# Build binaries for a tag and attach them to a GitHub release.
  4#
  5# WHY BINARIES AT ALL, given `cargo install orgo` exists: installing from source needs
  6# a Rust toolchain and about a minute of compiling syntect. Someone evaluating a site
  7# generator should be able to download one file and point it at their notes.
  8#
  9# The tag is the source of truth for the version. The build checks it against Cargo.toml
 10# rather than trusting them to match, because a release tagged v0.18.0 containing a binary
 11# that reports 0.17.0 is the kind of thing nobody notices for months.
 12
 13on:
 14  push:
 15    tags: ["v*"]
 16  workflow_dispatch:
 17    inputs:
 18      tag:
 19        description: "Tag to build (for a re-run of a failed release)"
 20        required: true
 21
 22env:
 23  CARGO_TERM_COLOR: always
 24
 25jobs:
 26  build:
 27    name: ${{ matrix.target }}
 28    runs-on: ${{ matrix.os }}
 29    strategy:
 30      fail-fast: false
 31      matrix:
 32        include:
 33          # Apple Silicon and Intel Macs, each built natively on its own runner. The
 34          # Intel label moves with the image: macos-13 was retired, and a job asking for
 35          # a retired label does not fail — it sits queued until someone notices, which
 36          # is how the v0.20.0 release spent fifteen minutes doing nothing.
 37          - { os: macos-latest, target: aarch64-apple-darwin }
 38          - { os: macos-15-intel, target: x86_64-apple-darwin }
 39          # glibc for ordinary distributions, musl for containers and anything older than
 40          # the runner's glibc — a dynamically linked binary is the usual reason a
 41          # download does not run.
 42          - { os: ubuntu-latest, target: x86_64-unknown-linux-gnu }
 43          - { os: ubuntu-latest, target: x86_64-unknown-linux-musl }
 44    steps:
 45      - name: Checkout
 46        uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
 47        with:
 48          ref: ${{ github.event.inputs.tag || github.ref }}
 49
 50      - name: Install Rust
 51        uses: dtolnay/rust-toolchain@1ff72ee08e3cb84d84adba594e0a297990fc1ed3 # stable
 52        with:
 53          toolchain: stable
 54          targets: ${{ matrix.target }}
 55
 56      - name: Install musl tools
 57        if: endsWith(matrix.target, '-musl')
 58        run: sudo apt-get update && sudo apt-get install -y --no-install-recommends musl-tools
 59
 60      - name: Check the tag against Cargo.toml
 61        shell: bash
 62        run: |
 63          tag="${{ github.event.inputs.tag || github.ref_name }}"
 64          crate=$(cargo metadata --no-deps --format-version 1 \
 65            | python3 -c 'import json,sys; print(json.load(sys.stdin)["packages"][0]["version"])')
 66          if [ "$tag" != "v$crate" ]; then
 67            echo "tag $tag does not match Cargo.toml version $crate" >&2
 68            exit 1
 69          fi
 70
 71      - name: Build
 72        run: cargo build --release --locked --target ${{ matrix.target }}
 73
 74      # A tarball rather than a bare binary: it keeps the executable bit through GitHub's
 75      # download path, and carries the licence with the thing it licenses.
 76      - name: Package
 77        shell: bash
 78        run: |
 79          staging="orgo-${{ github.event.inputs.tag || github.ref_name }}-${{ matrix.target }}"
 80          mkdir "$staging"
 81          cp "target/${{ matrix.target }}/release/orgo" "$staging/"
 82          cp README.md LICENSE "$staging/"
 83          tar czf "$staging.tar.gz" "$staging"
 84          shasum -a 256 "$staging.tar.gz" > "$staging.tar.gz.sha256"
 85
 86      - name: Upload
 87        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
 88        with:
 89          name: ${{ matrix.target }}
 90          path: |
 91            *.tar.gz
 92            *.tar.gz.sha256
 93
 94  release:
 95    name: publish the release
 96    needs: build
 97    runs-on: ubuntu-latest
 98    permissions:
 99      contents: write
100    steps:
101      - name: Download every build
102        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
103        with:
104          merge-multiple: true
105
106      # A draft, deliberately. The release notes are written by a person, and a release
107      # that publishes itself before anyone has read it cannot be edited quietly.
108      - name: Create the draft release
109        uses: softprops/action-gh-release@3d0d9888cb7fd7b750713d6e236d1fcb99157228 # v3.0.2
110        with:
111          draft: true
112          tag_name: ${{ github.event.inputs.tag || github.ref_name }}
113          files: |
114            *.tar.gz
115            *.tar.gz.sha256
116
117  publish:
118    name: publish to crates.io
119    needs: build
120    runs-on: ubuntu-latest
121    # Named so it can be gated. Adding a required reviewer to this environment in the
122    # repository settings turns a tag push into "waiting for a human", which is the right
123    # shape for the one step in this workflow that cannot be undone: a published version
124    # can be yanked but never replaced.
125    environment: crates-io
126    permissions:
127      # The OIDC token this mints *is* the credential — there is no API token stored in
128      # this repository, and nothing to leak from a compromised job beyond a token that
129      # crates.io revokes when the job ends.
130      id-token: write
131      contents: read
132    steps:
133      - name: Checkout
134        uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
135        with:
136          ref: ${{ github.event.inputs.tag || github.ref }}
137
138      - name: Install Rust
139        uses: dtolnay/rust-toolchain@1ff72ee08e3cb84d84adba594e0a297990fc1ed3 # stable
140        with:
141          toolchain: stable
142
143      # Exchanges GitHub's OIDC token for a short-lived crates.io token, and revokes it
144      # when the job finishes. Configured on crates.io against this repository, this
145      # workflow's filename, and the environment above.
146      - name: Authenticate with crates.io
147        id: auth
148        uses: rust-lang/crates-io-auth-action@c6f97d42243bad5fab37ca0427f495c86d5b1a18 # v1.0.5
149
150      # `--locked` publishes exactly the dependency versions the tests ran against, rather
151      # than whatever resolves at publish time.
152      - name: Publish
153        run: cargo publish --locked
154        env:
155          CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }}