Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 85 additions & 31 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,71 @@ name: Release

on:
push:
branches: [main]
tags: ["v*"]

permissions:
contents: write
id-token: write
pull-requests: write

env:
CARGO_TERM_COLOR: always
BINARY_NAME: cell

jobs:
release-plz-release:
name: Release-plz release
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
permissions:
contents: write
id-token: write
pull-requests: read
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
persist-credentials: false

- uses: dtolnay/rust-toolchain@stable

- name: Publish release
uses: release-plz/action@v0.5.128
with:
command: release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

release-plz-pr:
name: Release-plz PR
if: github.ref == 'refs/heads/main'
needs: release-plz-release
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
concurrency:
group: release-plz-${{ github.ref }}
cancel-in-progress: false
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
persist-credentials: false

- uses: dtolnay/rust-toolchain@stable

- name: Open or update release PR
uses: release-plz/action@v0.5.128
with:
command: release-pr
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

build:
name: Build (${{ matrix.target }})
if: startsWith(github.ref, 'refs/tags/v')
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
Expand Down Expand Up @@ -55,29 +107,41 @@ jobs:
key: ${{ matrix.target }}

- name: Build
run: cargo build --release --target ${{ matrix.target }} -p cell-sheet-tui
run: cargo build --locked --release --target ${{ matrix.target }} -p cell-sheet-tui

- name: Package (unix)
if: matrix.archive == 'tar.gz'
run: |
cd target/${{ matrix.target }}/release
tar czf ../../../${{ env.BINARY_NAME }}-${{ github.ref_name }}-${{ matrix.target }}.tar.gz ${{ env.BINARY_NAME }}
mkdir -p dist/${{ env.BINARY_NAME }}-${{ github.ref_name }}-${{ matrix.target }}
cp target/${{ matrix.target }}/release/${{ env.BINARY_NAME }} dist/${{ env.BINARY_NAME }}-${{ github.ref_name }}-${{ matrix.target }}/
cp README.md CHANGELOG.md LICENSE dist/${{ env.BINARY_NAME }}-${{ github.ref_name }}-${{ matrix.target }}/
tar czf ${{ env.BINARY_NAME }}-${{ github.ref_name }}-${{ matrix.target }}.tar.gz -C dist ${{ env.BINARY_NAME }}-${{ github.ref_name }}-${{ matrix.target }}
shasum -a 256 ${{ env.BINARY_NAME }}-${{ github.ref_name }}-${{ matrix.target }}.tar.gz > ${{ env.BINARY_NAME }}-${{ github.ref_name }}-${{ matrix.target }}.tar.gz.sha256

- name: Package (windows)
if: matrix.archive == 'zip'
shell: pwsh
run: |
Compress-Archive -Path "target/${{ matrix.target }}/release/${{ env.BINARY_NAME }}.exe" -DestinationPath "${{ env.BINARY_NAME }}-${{ github.ref_name }}-${{ matrix.target }}.zip"
$dist = "dist/${{ env.BINARY_NAME }}-${{ github.ref_name }}-${{ matrix.target }}"
New-Item -ItemType Directory -Force -Path $dist
Copy-Item "target/${{ matrix.target }}/release/${{ env.BINARY_NAME }}.exe" $dist
Copy-Item README.md,CHANGELOG.md,LICENSE $dist
Compress-Archive -Path $dist -DestinationPath "${{ env.BINARY_NAME }}-${{ github.ref_name }}-${{ matrix.target }}.zip"
$hash = (Get-FileHash "${{ env.BINARY_NAME }}-${{ github.ref_name }}-${{ matrix.target }}.zip" -Algorithm SHA256).Hash.ToLower()
"$hash ${{ env.BINARY_NAME }}-${{ github.ref_name }}-${{ matrix.target }}.zip" | Out-File -Encoding ascii "${{ env.BINARY_NAME }}-${{ github.ref_name }}-${{ matrix.target }}.zip.sha256"

- uses: actions/upload-artifact@v7
with:
name: ${{ env.BINARY_NAME }}-${{ matrix.target }}
path: ${{ env.BINARY_NAME }}-${{ github.ref_name }}-${{ matrix.target }}.*

release:
name: Create Release
upload-artifacts:
name: Upload release artifacts
if: startsWith(github.ref, 'refs/tags/v')
needs: build
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v6

Expand All @@ -86,32 +150,22 @@ jobs:
path: artifacts
merge-multiple: true

- name: Create GitHub Release
run: gh release create ${{ github.ref_name }} artifacts/* --generate-notes
- name: Wait for release-plz draft
run: |
for attempt in {1..30}; do
if gh release view "${{ github.ref_name }}" >/dev/null 2>&1; then
exit 0
fi
sleep 2
done
echo "release ${{ github.ref_name }} was not created by release-plz" >&2
exit 1
env:
GH_TOKEN: ${{ github.token }}

publish:
name: Publish to crates.io
needs: release
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6

- uses: dtolnay/rust-toolchain@stable

- uses: rust-lang/crates-io-auth-action@v1.0.4
id: auth

- name: Publish cell-sheet-core
run: cargo publish -p cell-sheet-core
env:
CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }}

- name: Wait for crates.io index
run: sleep 30

- name: Publish cell-sheet-tui
run: cargo publish -p cell-sheet-tui
- name: Upload release artifacts
run: |
gh release upload "${{ github.ref_name }}" artifacts/* --clobber
gh release edit "${{ github.ref_name }}" --draft=false
env:
CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }}
GH_TOKEN: ${{ github.token }}
10 changes: 6 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -190,10 +190,12 @@ for broader proposals.
## Releasing

Releases are cut by maintainers. The process is documented in the
[Releasing section of the README](README.md#releasing): bump the workspace
version in `Cargo.toml`, move `Unreleased` entries into the new version
section in `CHANGELOG.md`, commit, tag `vX.Y.Z`, and push the tag. The
release workflow handles binaries and `crates.io` publication.
[Releasing section of the README](README.md#releasing) and
[`RELEASE.md`](RELEASE.md): `release-plz` opens a release PR with the next
workspace version and changelog updates. Merging that PR publishes the crates
via trusted publishing, creates the `vX.Y.Z` tag, and lets the release workflow
attach binaries and checksums to the GitHub Release. Maintainers should not
push release tags manually during the normal flow.

## License

Expand Down
27 changes: 13 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -276,20 +276,19 @@ The core library is independent of the terminal UI and can be tested without a t

## Releasing

1. Update the version in `[Cargo.toml](Cargo.toml)` (workspace version)
2. Update `[CHANGELOG.md](CHANGELOG.md)` with the new version's changes
3. Commit: `git commit -am "release: bump to vX.Y.Z"`
4. Tag and push:
```sh
git tag vX.Y.Z
git push origin main --tags
```

Pushing a `v*` tag triggers the [release workflow](.github/workflows/release.yml), which:

- Builds binaries for Linux (x86_64, aarch64), macOS (x86_64, aarch64), and Windows (x86_64)
- Creates a GitHub Release with the binaries attached
- Publishes `cell-sheet-core` and `cell-sheet-tui` to [crates.io](https://crates.io) via trusted publishing
Releases are automated with [release-plz](https://release-plz.dev/) from the
[release workflow](.github/workflows/release.yml):

- Pushes to `main` open or update a release PR with the next version and
changelog updates.
- Merging the release PR publishes `cell-sheet-core` and `cell-sheet-tui` to
[crates.io](https://crates.io) via trusted publishing, creates the `vX.Y.Z`
tag, and creates a draft GitHub Release.
- The `vX.Y.Z` tag then builds binaries for Linux (x86_64, aarch64), macOS
(x86_64, aarch64), and Windows (x86_64), uploads archives and SHA256
checksums, and publishes the GitHub Release.

See [RELEASE.md](RELEASE.md) for maintainer instructions and failure handling.

## Contributing

Expand Down
59 changes: 59 additions & 0 deletions RELEASE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Releasing

Releases are automated with
[release-plz](https://release-plz.dev/) from
[`.github/workflows/release.yml`](.github/workflows/release.yml).

The `release.yml` workflow is configured as the crates.io trusted-publishing
provenance source for both `cell-sheet-core` and `cell-sheet-tui`. Do not
rename or replace it without updating the trusted-publishing configuration on
crates.io for both crates.

## Normal Release Flow

1. Land user-visible changes on `main` with conventional commit messages.
2. The release workflow opens or updates a `release-plz` pull request with:
- the next workspace version in `Cargo.toml`
- the matching `CHANGELOG.md` release section
- any lockfile changes needed for the release
3. Review the release PR and merge it when the changelog and version are right.
4. After the merge, `release-plz` publishes unpublished crates to crates.io via
trusted publishing, creates a single `vX.Y.Z` tag, and creates a draft
GitHub Release.
5. The same workflow runs again for the new `vX.Y.Z` tag, builds release
binaries, uploads archives plus `.sha256` checksum files, and publishes the
draft GitHub Release.

No manual tag push is needed.

## Workflow Ownership

`release-plz` owns:

- release PR creation
- version and changelog updates
- crates.io publishing
- the `vX.Y.Z` tag
- the draft GitHub Release

The tag-triggered artifact jobs in `release.yml` own:

- Linux, macOS, and Windows binary builds
- release archives
- SHA256 checksum files
- publishing the draft GitHub Release after artifacts are attached

The workspace has two public crates but one product release, so
[`release-plz.toml`](release-plz.toml) is only configuration for the
`release-plz` jobs in `release.yml`: it disables per-crate tags by default and
enables a single `v{{ version }}` tag/release for `cell-sheet-tui`.

## Failure Handling

- If crates.io publishing fails, fix the issue and rerun the failed workflow.
Do not create a manual tag for the same version.
- If artifact building fails after crates.io publishing succeeds, the GitHub
Release remains a draft. Fix the workflow or code, rerun the tag workflow,
and let it upload artifacts and publish the draft.
- If the release PR has the wrong changelog or version, edit the source commits
or `release-plz.toml` configuration and let the release PR update.
16 changes: 16 additions & 0 deletions release-plz.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[workspace]
release_always = false
changelog_update = false
git_release_enable = false
git_tag_enable = false

[[package]]
name = "cell-sheet-tui"
changelog_update = true
changelog_path = "./CHANGELOG.md"
changelog_include = ["cell-sheet-core"]
git_tag_name = "v{{ version }}"
git_tag_enable = true
git_release_enable = true
git_release_name = "v{{ version }}"
git_release_draft = true
Loading