Skip to content

feat(checkpoints): surface non-git workdir with a helpful hint #68

feat(checkpoints): surface non-git workdir with a helpful hint

feat(checkpoints): surface non-git workdir with a helpful hint #68

Workflow file for this run

name: Release
on:
push:
branches:
- main
workflow_dispatch:
inputs:
bump:
description: "Version bump level"
required: true
default: minor
type: choice
options:
- patch
- minor
- major
permissions:
contents: write
concurrency:
group: release
cancel-in-progress: false
jobs:
release:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Go
uses: actions/setup-go@v5
with:
# Pin to the major+minor declared in go.mod so `go mod vendor`
# and the release build succeed against the source tree.
go-version-file: go.mod
- name: Run tests
run: go test ./...
- name: Determine bump level
id: level
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
echo "level=${{ inputs.bump }}" >> "$GITHUB_OUTPUT"
else
echo "level=patch" >> "$GITHUB_OUTPUT"
fi
- name: Compute next version
id: version
run: |
git fetch --tags --force
LATEST=$(git tag -l 'v[0-9]*' --sort=-v:refname | head -n1)
if [ -z "$LATEST" ]; then
LATEST="v0.0.0"
fi
echo "latest=$LATEST" >> "$GITHUB_OUTPUT"
VERSION="${LATEST#v}"
MAJOR=$(echo "$VERSION" | cut -d. -f1)
MINOR=$(echo "$VERSION" | cut -d. -f2)
PATCH=$(echo "$VERSION" | cut -d. -f3)
case "${{ steps.level.outputs.level }}" in
major)
MAJOR=$((MAJOR + 1)); MINOR=0; PATCH=0 ;;
minor)
MINOR=$((MINOR + 1)); PATCH=0 ;;
patch)
PATCH=$((PATCH + 1)) ;;
esac
NEXT="v${MAJOR}.${MINOR}.${PATCH}"
echo "next=$NEXT" >> "$GITHUB_OUTPUT"
echo "Next version: $NEXT (from $LATEST, bump=${{ steps.level.outputs.level }})"
- name: Generate release notes
id: notes
env:
VERSION: ${{ steps.version.outputs.next }}
PREV: ${{ steps.version.outputs.latest }}
run: |
{
echo "## Install"
echo
echo '### Prebuilt binary (fastest)'
echo
echo 'Download the archive for your platform from the assets below, extract, and drop `ctm` into a directory on your `$PATH`. Archives include the binary plus `LICENSE` and `README.md`.'
echo
echo '| Platform | Asset |'
echo '|---|---|'
echo "| Linux x86_64 | \`ctm-${VERSION}-linux-amd64.tar.gz\` |"
echo "| Linux ARM64 | \`ctm-${VERSION}-linux-arm64.tar.gz\` |"
echo "| macOS (Intel) | \`ctm-${VERSION}-darwin-amd64.tar.gz\` |"
echo "| macOS (Apple Silicon) | \`ctm-${VERSION}-darwin-arm64.tar.gz\` |"
echo
echo 'Windows users: run the Linux binary under WSL — tmux has no native Windows support.'
echo
echo '### From source'
echo
echo '```bash'
echo "go install github.com/${{ github.repository }}@$VERSION"
echo '```'
echo
echo '### Air-gapped'
echo
echo "The \`ctm-${VERSION}-src.tar.gz\` archive is a vendored source tree (\`go mod vendor\` populated) that builds offline with \`go build .\`."
echo
echo "## Changes"
echo
if [ "$PREV" = "v0.0.0" ]; then
git log --pretty=format:'- %s (%h)' | head -n 200
else
git log --pretty=format:'- %s (%h)' "$PREV"..HEAD
fi
} > release-notes.md
echo "Generated notes:"
cat release-notes.md
- name: Configure git
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
- name: Create tag
env:
VERSION: ${{ steps.version.outputs.next }}
run: |
git tag -a "$VERSION" -m "Release $VERSION"
git push origin "$VERSION"
- name: Build release artifacts (cross-platform binaries + source + checksums)
env:
VERSION: ${{ steps.version.outputs.next }}
REPO: ${{ github.repository }}
run: |
set -euo pipefail
mkdir -p dist
# --- Source tarball (vendored, for air-gapped builds) ---
#
# `go mod vendor` populates vendor/ so downstream `go build`
# works without public internet (rules/build.md).
go mod vendor
tar \
--transform "s,^\\.,ctm-${VERSION}," \
--exclude='./.git' \
--exclude='./dist' \
--exclude='*.DS_Store' \
-czf "dist/ctm-${VERSION}-src.tar.gz" \
.
# --- Cross-compiled binaries ---
#
# ctm is pure Go (no cgo), so Go's native cross-compile is
# enough — no gcc-mingw tooling required. Each binary is
# built with:
# -trimpath : strip workspace paths for reproducibility
# -s -w : drop debug info + symbol table (~30% smaller)
# -X ...Version= : inject the release tag so `ctm version` reports it
LDFLAGS="-s -w -X github.com/${REPO}/cmd.Version=${VERSION}"
# Windows is not a target: ctm depends on tmux (not natively
# available on Windows) and uses POSIX syscalls like
# syscall.Flock in internal/logrotate / internal/session.
# Windows users run the Linux binary under WSL.
for target in linux-amd64 linux-arm64 darwin-amd64 darwin-arm64; do
goos="${target%-*}"
goarch="${target#*-}"
stage="ctm-${VERSION}-${target}"
mkdir -p "dist/${stage}"
echo "→ building ${target}"
CGO_ENABLED=0 GOOS="$goos" GOARCH="$goarch" \
go build -trimpath -ldflags "$LDFLAGS" \
-o "dist/${stage}/ctm" \
./
cp LICENSE README.md "dist/${stage}/"
(cd dist && tar -czf "${stage}.tar.gz" "${stage}" && rm -rf "${stage}")
done
# --- SHA256SUMS over every artifact ---
#
# GNU coreutils format (`sha256sum -c SHA256SUMS` verifies).
(cd dist && sha256sum ctm-*.tar.gz > SHA256SUMS)
echo "Artifacts:"
ls -lh dist
echo "Checksums:"
cat dist/SHA256SUMS
- name: Append checksums to release notes
run: |
{
echo
echo "## Verification"
echo
echo 'Tarball SHA256 sums (also attached as `SHA256SUMS`):'
echo
echo '```'
cat dist/SHA256SUMS
echo '```'
echo
echo 'Verify:'
echo
echo '```bash'
echo 'sha256sum -c SHA256SUMS # inside the dir where both files live'
echo '```'
} >> release-notes.md
- name: Create GitHub release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VERSION: ${{ steps.version.outputs.next }}
run: |
gh release create "$VERSION" \
--title "$VERSION" \
--notes-file release-notes.md \
dist/ctm-*.tar.gz \
dist/SHA256SUMS