Skip to content

Commit f66aa09

Browse files
authored
Add release automation script and auto-tag workflow (#25)
Mirrors the `pnpm release` flow from linear-release. Adds: - VERSION file as the source of truth for the action's version - scripts/release.sh — preflights, creates a release/X.Y.Z branch, bumps VERSION + the cli_version default in action.yml + the README inputs table, and opens a PR - .github/workflows/auto-tag-release.yml — on merged release/* PRs, validates the branch matches the VERSION file, pushes vX.Y.Z, and triggers the existing Release workflow - workflow_dispatch trigger on release.yml so the auto-tag workflow can fire it via gh workflow run - RELEASING.md rewritten around the new flow
1 parent 20b42c3 commit f66aa09

5 files changed

Lines changed: 219 additions & 13 deletions

File tree

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
name: Auto-tag release
2+
3+
on:
4+
pull_request:
5+
types: [closed]
6+
branches: [main]
7+
8+
jobs:
9+
tag:
10+
name: Create release tag
11+
runs-on: ubuntu-latest
12+
if: >
13+
github.event.pull_request.merged == true &&
14+
github.event.pull_request.head.repo.full_name == github.repository &&
15+
startsWith(github.event.pull_request.head.ref, 'release/')
16+
permissions:
17+
contents: write
18+
actions: write
19+
steps:
20+
- uses: actions/checkout@v4
21+
with:
22+
ref: ${{ github.event.pull_request.merge_commit_sha }}
23+
fetch-depth: 0
24+
fetch-tags: true
25+
26+
- name: Extract and validate version
27+
id: version
28+
run: |
29+
BRANCH="${{ github.event.pull_request.head.ref }}"
30+
VERSION="${BRANCH#release/}"
31+
32+
if ! echo "$VERSION" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+$'; then
33+
echo "::error::Invalid version format: $VERSION"
34+
exit 1
35+
fi
36+
37+
FILE_VERSION=$(tr -d '[:space:]' < VERSION)
38+
if [ "$VERSION" != "$FILE_VERSION" ]; then
39+
echo "::error::Branch version ($VERSION) does not match VERSION file ($FILE_VERSION)"
40+
exit 1
41+
fi
42+
43+
if git tag -l "v$VERSION" | grep -q .; then
44+
echo "::error::Tag v$VERSION already exists"
45+
exit 1
46+
fi
47+
48+
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
49+
50+
- name: Create and push tag
51+
run: |
52+
git tag "v${{ steps.version.outputs.version }}"
53+
git push origin "v${{ steps.version.outputs.version }}"
54+
55+
- name: Trigger release workflow
56+
env:
57+
GH_TOKEN: ${{ github.token }}
58+
run: |
59+
gh workflow run release.yml --ref "v${{ steps.version.outputs.version }}"

.github/workflows/release.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ on:
44
push:
55
tags:
66
- "v*"
7+
workflow_dispatch:
78

89
jobs:
910
release:

RELEASING.md

Lines changed: 55 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,68 @@ This document describes how to cut a new release of `linear-release-action`.
44

55
## When to release
66

7-
Cut a new release whenever `main` has changes that should be picked up by consumers — most commonly after bumping the default `cli_version` in [`action.yml`](./action.yml) to track a new [`linear-release` CLI](https://github.com/linear/linear-release) release.
7+
Cut a new release whenever `main` has changes that should be picked up by consumers — most commonly after the [`linear-release` CLI](https://github.com/linear/linear-release) ships a new version that the action should default to.
88

9-
## How to release
9+
## Prerequisites
1010

11-
From a clean `main` checkout that's up to date with `origin/main`, push a `vMAJOR.MINOR.PATCH` tag:
11+
- You must be on the `main` branch with a clean working tree, up to date with `origin/main`
12+
- The [GitHub CLI](https://cli.github.com) (`gh`) must be installed and authenticated
13+
14+
## Creating a release
15+
16+
Run the release script with the target version:
1217

1318
```bash
14-
git checkout main && git pull
15-
git tag v0.7.2
16-
git push origin v0.7.2
19+
./scripts/release.sh <version>
1720
```
1821

19-
That triggers the [Release workflow](./.github/workflows/release.yml), which:
22+
For example:
23+
24+
```bash
25+
./scripts/release.sh 0.10.0
26+
```
27+
28+
The version must follow `MAJOR.MINOR.PATCH` format (e.g., `0.10.0`, `1.0.0`).
29+
30+
## What happens
31+
32+
The release script (`scripts/release.sh`) and CI workflows handle the full process:
33+
34+
### 1. `./scripts/release.sh <version>` (local)
35+
36+
The script runs preflight checks and then:
37+
38+
1. Validates the version format
39+
2. Checks that `gh` is installed and authenticated
40+
3. Verifies the working tree is clean, you're on `main`, and it's up to date with `origin/main`
41+
4. Ensures the `v<version>` tag and `release/<version>` branch don't already exist
42+
5. Creates a `release/<version>` branch
43+
6. Bumps the version in [`VERSION`](./VERSION), the `cli_version` default in [`action.yml`](./action.yml), and the inputs table in [`README.md`](./README.md)
44+
7. Commits the change and pushes the branch
45+
8. Opens a PR against `main` via `gh pr create`
46+
47+
### 2. PR review and merge
48+
49+
Review and merge the PR as usual. The PR contains the version bumps only.
50+
51+
### 3. Auto-tagging (CI)
52+
53+
When a PR from a `release/*` branch is merged into `main`, the [Auto-tag release workflow](./.github/workflows/auto-tag-release.yml) runs automatically:
54+
55+
1. Validates that the branch version matches the `VERSION` file on `main`
56+
2. Creates and pushes the `v<version>` tag
57+
3. Triggers the [Release workflow](./.github/workflows/release.yml)
58+
59+
### 4. Release workflow (CI)
60+
61+
The Release workflow then:
2062

21-
1. Validates the tag format.
22-
2. Force-updates the floating `v<major>` tag (e.g. `v0`) to the same commit so consumers using `linear/linear-release-action@v0` pick up the change automatically.
23-
3. Creates a GitHub Release with auto-generated notes from the merged PRs since the previous tag.
63+
1. Validates the tag format
64+
2. Force-updates the floating `v<major>` tag (e.g. `v0`) to the same commit so consumers using `linear/linear-release-action@v0` pick up the change automatically
65+
3. Creates a GitHub Release with auto-generated notes from the merged PRs since the previous tag
2466

2567
## Notes
2668

27-
- Consumers reference this action as `linear/linear-release-action@v0` (the floating major tag), so the major-tag move in step 2 is the load-bearing step. Without it, consumers stay on whichever commit the major tag previously pointed to.
28-
- The action has no version-bearing file in the repo — the source of truth for the action's version is the git tag itself.
29-
- The CLI version that the action installs at runtime is controlled by [`action.yml`'s `cli_version` default](./action.yml). To bump it, open a regular PR updating `action.yml` and `README.md`, merge, then cut a new action release with the steps above.
69+
- Consumers reference this action as `linear/linear-release-action@v0` (the floating major tag), so the major-tag move is the load-bearing step. Without it, consumers stay on whichever commit the major tag previously pointed to.
70+
- The source of truth for the action's version is [`VERSION`](./VERSION); the auto-tag workflow fails if the branch name and `VERSION` file disagree.
71+
- The script bumps the `cli_version` default to match the action version. If you need a release where these diverge, edit the release branch by hand before merging the PR — the auto-tag workflow validates the `VERSION` file, not `cli_version`.

VERSION

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
0.9.0

scripts/release.sh

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
VERSION="${1:-}"
5+
6+
# --- Usage ---
7+
if [ -z "$VERSION" ]; then
8+
echo "Usage: $0 <version>"
9+
echo "Example: $0 0.10.0"
10+
exit 1
11+
fi
12+
13+
# --- Validate version format ---
14+
if ! echo "$VERSION" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+$'; then
15+
echo "Error: Invalid version format '$VERSION'. Expected MAJOR.MINOR.PATCH (e.g., 0.10.0)"
16+
exit 1
17+
fi
18+
19+
# --- Preflight checks ---
20+
21+
# gh CLI
22+
if ! command -v gh &>/dev/null; then
23+
echo "Error: gh CLI is not installed. Install it from https://cli.github.com"
24+
exit 1
25+
fi
26+
if ! gh auth status &>/dev/null; then
27+
echo "Error: gh CLI is not authenticated. Run 'gh auth login' first."
28+
exit 1
29+
fi
30+
31+
# Clean working tree
32+
if [ -n "$(git status --porcelain)" ]; then
33+
echo "Error: Working tree is not clean. Commit or stash your changes first."
34+
exit 1
35+
fi
36+
37+
# Must be on main
38+
CURRENT_BRANCH=$(git branch --show-current)
39+
if [ "$CURRENT_BRANCH" != "main" ]; then
40+
echo "Error: Must be on 'main' branch (currently on '$CURRENT_BRANCH')."
41+
exit 1
42+
fi
43+
44+
# Up to date with origin/main
45+
git fetch origin main --tags --quiet
46+
LOCAL_SHA=$(git rev-parse main)
47+
REMOTE_SHA=$(git rev-parse origin/main)
48+
if [ "$LOCAL_SHA" != "$REMOTE_SHA" ]; then
49+
echo "Error: Local 'main' is not up to date with 'origin/main'. Run 'git pull' first."
50+
exit 1
51+
fi
52+
53+
# Tag must not exist
54+
if git tag -l "v$VERSION" | grep -q .; then
55+
echo "Error: Tag 'v$VERSION' already exists."
56+
exit 1
57+
fi
58+
59+
# Branch must not exist (local or remote)
60+
BRANCH="release/$VERSION"
61+
if git show-ref --verify --quiet "refs/heads/$BRANCH" 2>/dev/null; then
62+
echo "Error: Local branch '$BRANCH' already exists."
63+
exit 1
64+
fi
65+
if git ls-remote --exit-code --heads origin "$BRANCH" &>/dev/null; then
66+
echo "Error: Remote branch '$BRANCH' already exists."
67+
exit 1
68+
fi
69+
70+
# --- Create release branch and bump versions ---
71+
echo "Creating branch '$BRANCH'..."
72+
git checkout -b "$BRANCH"
73+
74+
echo "Updating VERSION to $VERSION..."
75+
echo "$VERSION" > VERSION
76+
77+
echo "Updating cli_version default in action.yml..."
78+
sed -i.bak -E "s/^( default: v)[0-9]+\.[0-9]+\.[0-9]+\$/\1$VERSION/" action.yml
79+
sed -i.bak -E "s/(Linear Release CLI version to install \(e\.g\., \"v)[0-9]+\.[0-9]+\.[0-9]+(\"\))/\1$VERSION\2/" action.yml
80+
rm action.yml.bak
81+
82+
echo "Updating cli_version reference in README.md..."
83+
sed -i.bak -E "s/(\`v)[0-9]+\.[0-9]+\.[0-9]+(\` \| Linear Release CLI)/\1$VERSION\2/" README.md
84+
rm README.md.bak
85+
86+
git add VERSION action.yml README.md
87+
git commit -m "Release v$VERSION"
88+
89+
# --- Push and create PR ---
90+
echo "Pushing branch..."
91+
git push -u origin "$BRANCH"
92+
93+
echo "Creating pull request..."
94+
PR_URL=$(gh pr create \
95+
--title "Release v$VERSION" \
96+
--body "Bumps the action and default CLI version to v$VERSION.
97+
98+
After this PR is merged, the \`v$VERSION\` tag will be created automatically, triggering the [Release workflow](./.github/workflows/release.yml)." \
99+
--base main)
100+
101+
echo ""
102+
echo "PR created: $PR_URL"
103+
echo "Once merged, the tag 'v$VERSION' will be created automatically and the release workflow will run."

0 commit comments

Comments
 (0)