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
38 changes: 38 additions & 0 deletions .github/workflows/ci-post-merge.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: CI Post-merge Sentinel

on:
push:
branches:
- next
- main

permissions:
contents: read

concurrency:
group: post-merge-${{ github.ref }}
cancel-in-progress: true

jobs:
sentinel:
name: Post-merge sentinel (node v22)
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v6

- name: Setup node v22
uses: actions/setup-node@v6
with:
node-version: 22
cache: npm

- name: Install deps
run: npm ci

- name: Build project
run: npm run build

- name: Verify packed binaries
run: npm run verify:packed-binaries
12 changes: 6 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
name: CI

on:
push:
branches:
- main
pull_request:
types:
branches:
- next
- main
types:
- opened
- synchronize
- ready_for_review
Expand All @@ -25,7 +25,7 @@ jobs:

strategy:
matrix:
node-version: [22, 24]
node-version: [22]

steps:
- name: Checkout code
Expand All @@ -49,7 +49,7 @@ jobs:
lint:
strategy:
matrix:
node-version: [22, 24]
node-version: [22]

name: Code checks on node v${{ matrix.node-version }}
runs-on: ubuntu-latest
Expand Down
19 changes: 19 additions & 0 deletions .github/workflows/promote-next-to-main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,32 @@ jobs:
- name: Fetch main and next refs
run: git fetch origin main next

- name: No-op when next has no commits ahead of main
id: commits-check
shell: bash
run: |
set -euo pipefail

commit_ahead=$(git rev-list --max-count=1 origin/main..origin/next)

if [ -z "$commit_ahead" ]; then
echo "No commits to promote (origin/main..origin/next is empty)."
echo "has_commits=false" >> "$GITHUB_OUTPUT"
exit 0
fi

echo "has_commits=true" >> "$GITHUB_OUTPUT"

- name: Generate linearis-bot app token
if: ${{ steps.commits-check.outputs.has_commits == 'true' }}
id: app-token
uses: actions/create-github-app-token@v2
with:
app-id: ${{ secrets.RELEASE_APP_ID }}
private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}

- name: Build PR metadata
if: ${{ steps.commits-check.outputs.has_commits == 'true' }}
id: meta
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
Expand Down Expand Up @@ -112,6 +130,7 @@ jobs:
} >> "$GITHUB_OUTPUT"

- name: Upsert promotion PR
if: ${{ steps.commits-check.outputs.has_commits == 'true' }}
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
PROMOTION_PR_TITLE: ${{ steps.meta.outputs.title }}
Expand Down
20 changes: 3 additions & 17 deletions .github/workflows/release-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,15 @@ on:
push:
branches:
- next
schedule:
- cron: "0 9 * * 1"
- main
workflow_dispatch:

permissions:
contents: write
id-token: write

concurrency:
group: release-${{ github.event_name == 'schedule' && 'main' || github.ref_name }}
group: release-${{ github.ref_name }}
cancel-in-progress: false

jobs:
Expand Down Expand Up @@ -50,11 +49,7 @@ jobs:
id: target
shell: bash
run: |
if [ "${{ github.event_name }}" = "schedule" ]; then
branch="main"
else
branch="${{ github.ref_name }}"
fi
branch="${{ github.ref_name }}"

case "$branch" in
main|next) ;;
Expand Down Expand Up @@ -114,15 +109,6 @@ jobs:
- name: Build
run: npm run build

- name: Unit tests
run: npm test

- name: Lint and format check
run: npm run check:ci

- name: Type check
run: npx tsc --noEmit

- name: Verify npm auth
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
Expand Down
17 changes: 9 additions & 8 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,15 @@ Integration tests (`tests/integration/`) require `LINEAR_API_TOKEN` in your envi

## Publishing

Publishing is fully automated by the `Release` workflow (`.github/workflows/release-check.yml`).

- `next` (default branch): releases run on every push (prerelease channel)
- `main` (stable branch): releases run on weekly schedule
- Can be started manually with `workflow_dispatch` for either branch
- Executes build/test/lint/type-check gates before release
- Uses semantic-release to decide if a release is required
- Publishes npm package, creates tag, and creates GitHub release when releasable commits exist
Publishing is automated by GitHub Actions, primarily via the `Release` workflow (`.github/workflows/release-check.yml`).

At a high level:

- PR quality gates run in `ci.yml`
- Post-merge sentinel validation runs in `ci-post-merge.yml`
- Releases run in `release-check.yml` (semantic-release decides whether to publish)

For the authoritative workflow trigger matrix, required check names, and verification commands, see [`docs/ci-run-model.md`](docs/ci-run-model.md) (source of truth).

### Changelog ownership

Expand Down
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,14 +125,15 @@ Add this (or a version adapted to your workflow) to your `AGENTS.md` or `CLAUDE.

## Release Automation Policy

Releases are automated by GitHub Actions via `.github/workflows/release-check.yml`.
Linearis uses three CI/release workflows:

- `next` (default branch): release runs on every push (prerelease channel)
- `main` (stable branch): release runs on weekly schedule
- Manual `workflow_dispatch` supports releasing from either `main` or `next`
- `CHANGELOG.md` is automation-owned and must not be edited in pull requests
- `ci.yml` for required pull request checks
- `ci-post-merge.yml` for post-merge sentinel validation on `main`/`next` pushes
- `release-check.yml` for push-driven and manual releases

If a pull request branch contains `CHANGELOG.md` changes anywhere in `main...HEAD` history, CI fails and posts rebase instructions.
For the authoritative trigger matrix, required checks, and operational verification commands, see [`docs/ci-run-model.md`](docs/ci-run-model.md) (source of truth).

`CHANGELOG.md` is automation-owned and must not be edited in pull requests. If a pull request branch contains `CHANGELOG.md` changes anywhere in `main...HEAD` history, CI fails and posts rebase instructions.

## Contributing

Expand Down
76 changes: 76 additions & 0 deletions docs/ci-run-model.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# CI Run Model

This document defines how pull request checks and release runs are wired together.

## Workflow purpose

| Workflow | Purpose |
| --- | --- |
| `ci.yml` | Runs required pull request checks used by branch protection/rulesets. |
| `ci-post-merge.yml` | Runs post-merge sentinel validation on `main`/`next` pushes. |
| `release-check.yml` | Performs semantic-release on release branches after required checks have already passed in PR. |

## Trigger matrix

| Event | Branch | `ci.yml` | `ci-post-merge.yml` | `release-check.yml` |
| --- | --- | --- | --- | --- |
| `pull_request` | `main`, `next` | ✅ required checks | ❌ | ❌ |
| `push` | `main`, `next` | ❌ | ✅ post-merge sentinel | ✅ release run |
| `workflow_dispatch` | selected ref (only `main`/`next` accepted by job validation) | ❌ | ❌ | ✅ manual release run (main/next only) |

> **Constraint:** Although GitHub UI lets you choose any ref for `workflow_dispatch`, `release-check.yml` enforces `main` or `next` only and fails early for other refs.

## Required checks for repository ruleset

The repository ruleset must require exactly these check names:

- Unit tests on node v22
- Code checks on node v22

These checks are produced by `ci.yml` and must be green before merging to `main` or `next`.

## Release model (no schedule)

Release workflow is lean and intentionally does not run a weekly schedule:

- Push to `next` → prerelease channel
- Push to `main` → stable release channel
- Manual `workflow_dispatch` → ad-hoc release from `main` or `next` only (validated in the release job)

Because release runs happen only after merges, quality gates live in PR required checks, not duplicated release-time full matrices.

## Operational verification

Use GitHub CLI to confirm the run model:

```bash
# CI required checks from PRs

gh run list --workflow ci.yml --event pull_request --limit 20

# Post-merge sentinel runs on push (next)

gh run list --workflow ci-post-merge.yml --event push --branch next --limit 20

# Post-merge sentinel runs on push (main)

gh run list --workflow ci-post-merge.yml --event push --branch main --limit 20

# Release runs on push (main + next)

gh run list --workflow release-check.yml --event push --limit 20

# Optional manual release runs

gh run list --workflow release-check.yml --event workflow_dispatch --limit 20
```

## Rollback guidance

If the model needs to be reverted quickly:

1. Revert the workflow/ruleset refactor commit.
2. Confirm ruleset required checks point to existing check names.
3. Re-run a PR and confirm both required checks appear and pass.
4. Verify push-based release on `next` and `main` with `gh run list` filters above.
5. If release is blocked, run `workflow_dispatch` once as a temporary bridge while fixing ruleset/workflow drift.
Loading