v1.10.0 #14
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Publish the `deckfile` Rust crate to crates.io when a GitHub release is promoted from prerelease | |
| # to full release — the Rust half of the same release that publishes @spacedevin/deck to npm. | |
| # | |
| # The crate is GENERATED from src/index.tish (see scripts/build-rust.mjs), so it is emitted here | |
| # rather than committed: one source, and the published crate cannot drift from the published npm | |
| # package because both come out of the same tag. | |
| # | |
| # AUTH: crates.io Trusted Publishing (OIDC, no long-lived token), matching npm-release.yml — with a | |
| # CARGO_REGISTRY_TOKEN fallback used only to bootstrap. | |
| # | |
| # Trusted Publishing config is attached to a crate you already own, so it cannot publish a crate that | |
| # does not exist yet. Bootstrapping, once: | |
| # 1. Add CARGO_REGISTRY_TOKEN as a repo secret and run this workflow — that claims `deckfile`. | |
| # 2. Configure Trusted Publishing on crates.io: | |
| # deckfile > Settings > Trusted Publishing > GitHub Actions | |
| # Repository owner: spacedevin | |
| # Repository name: deck | |
| # Workflow filename: crates-release.yml | |
| # Environment: (leave blank) | |
| # 3. DELETE the secret. The step below falls back to OIDC automatically, so the long-lived token | |
| # exists only for as long as the bootstrap takes. | |
| name: Crates.io release | |
| on: | |
| release: | |
| types: [published, edited] | |
| # Re-dispatch to recover a failed/throttled run. Publishing is idempotent below: a version already | |
| # on crates.io is skipped rather than failing the job. | |
| workflow_dispatch: | |
| inputs: | |
| tag: | |
| description: "Release tag to (re)publish, e.g. v1.0.0" | |
| required: true | |
| type: string | |
| permissions: | |
| contents: read | |
| id-token: write # required for crates.io Trusted Publishing | |
| jobs: | |
| publish: | |
| name: Publish deckfile to crates.io | |
| if: ${{ github.event_name == 'workflow_dispatch' || github.event.release.prerelease == false }} | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: refs/tags/${{ github.event.release.tag_name || inputs.tag }} | |
| fetch-depth: 0 | |
| - name: Setup Node | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: "24" | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Install Rust | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: Resolve version from the tag | |
| id: version | |
| run: | | |
| TAG="${{ github.event.release.tag_name || inputs.tag }}" | |
| echo "version=${TAG#v}" >> "$GITHUB_OUTPUT" | |
| - name: Skip if this version is already on crates.io | |
| id: check | |
| run: | | |
| V="${{ steps.version.outputs.version }}" | |
| if curl -sf "https://crates.io/api/v1/crates/deckfile/$V" -H 'User-Agent: deck-release' >/dev/null 2>&1; then | |
| echo "already=true" >> "$GITHUB_OUTPUT" | |
| echo "::notice::deckfile@$V is already published — skipping." | |
| else | |
| echo "already=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| # Uses the tish from `npm ci` above — the package's own dev dependency pins >= 3.2, which is | |
| # where the rust-lib emit landed (tishlang/tish#588). One source of truth for the compiler | |
| # version, so a released crate is always emitted by the tish the package declares. | |
| - name: Emit the crate | |
| if: steps.check.outputs.already == 'false' | |
| env: | |
| DECKFILE_VERSION: ${{ steps.version.outputs.version }} | |
| run: | | |
| export PATH="$PWD/node_modules/.bin:$PATH" | |
| tish -V | |
| npm run build:rust | |
| - name: Test the emitted crate | |
| if: steps.check.outputs.already == 'false' | |
| working-directory: crate | |
| run: cargo test | |
| # Two auth paths, because Trusted Publishing alone cannot bootstrap a crate that does not exist | |
| # yet: a config is attached to a crate you already own, so there is nothing to attach to before | |
| # the first publish. That is exactly how this failed on v1.2.1 — | |
| # Status: 400. No Trusted Publishing config found for repository `spacedevin/deck`. | |
| # | |
| # So: if a CARGO_REGISTRY_TOKEN secret is set, use it. Otherwise use Trusted Publishing. | |
| # To claim the name, add the secret and release once; then configure Trusted Publishing on | |
| # crates.io (deckfile > Settings > Trusted Publishing > owner `spacedevin`, repo `deck`, | |
| # workflow `crates-release.yml`) and DELETE the secret — this falls back to OIDC on its own, | |
| # so the long-lived token exists only for as long as it takes to bootstrap. | |
| # `secrets` is not available in a step-level `if`, only in `env` — so the presence of the token | |
| # has to be turned into a step output before it can gate anything. | |
| - name: Detect a crates.io token | |
| id: cred | |
| if: steps.check.outputs.already == 'false' | |
| env: | |
| TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} | |
| run: | | |
| if [ -n "${TOKEN:-}" ]; then | |
| echo "have_token=true" >> "$GITHUB_OUTPUT" | |
| echo "::notice::Publishing with CARGO_REGISTRY_TOKEN. Once deckfile exists, configure Trusted Publishing and delete this secret." | |
| else | |
| echo "have_token=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Authenticate to crates.io (Trusted Publishing) | |
| if: ${{ steps.check.outputs.already == 'false' && steps.cred.outputs.have_token == 'false' }} | |
| uses: rust-lang/crates-io-auth-action@v1 | |
| id: auth | |
| # --allow-dirty: `crate/` is generated and gitignored, so cargo sees it as untracked. | |
| - name: Publish | |
| if: steps.check.outputs.already == 'false' | |
| working-directory: crate | |
| env: | |
| CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN || steps.auth.outputs.token }} | |
| run: | | |
| if [ -z "${CARGO_REGISTRY_TOKEN:-}" ]; then | |
| echo "No crates.io credential." | |
| echo "Either set the CARGO_REGISTRY_TOKEN repo secret (needed for the FIRST publish of a" | |
| echo "crate, which Trusted Publishing cannot bootstrap), or configure Trusted Publishing" | |
| echo "for \`deckfile\` at https://crates.io/crates/deckfile/settings." | |
| exit 1 | |
| fi | |
| cargo publish --allow-dirty |