From f8d2fb67f89c91288a041a84258e5f7b55eb1800 Mon Sep 17 00:00:00 2001 From: Romain Cascino Date: Tue, 24 Feb 2026 09:38:42 +0000 Subject: [PATCH 1/2] Add skill to generate configuration --- skills/linear-release-setup/SKILL.md | 435 +++++++++++++++++++++++++++ 1 file changed, 435 insertions(+) create mode 100644 skills/linear-release-setup/SKILL.md diff --git a/skills/linear-release-setup/SKILL.md b/skills/linear-release-setup/SKILL.md new file mode 100644 index 0000000..3c73035 --- /dev/null +++ b/skills/linear-release-setup/SKILL.md @@ -0,0 +1,435 @@ +--- +name: linear-release-setup +description: Generate CI/CD configuration for Linear Release. Use when setting up + release tracking, configuring CI pipelines for Linear, or integrating deployments + with Linear releases. Supports GitHub Actions, GitLab CI, CircleCI, and other platforms. +--- + +# Linear Release Setup + +[Linear Release](https://github.com/linear/linear-release) is a CLI tool that integrates CI/CD pipelines with [Linear's release management](https://linear.app/docs/releases). It scans commits for issue identifiers, creates/updates releases, and tracks deployment stages. + +This skill generates CI/CD configuration files for Linear Release based on the user's project setup. + +## Interactive Workflow + +Follow these steps when the user asks to set up Linear Release: + +### Step 1: Detect existing CI setup + +Look for existing CI configuration files: + +- `.github/workflows/*.yml` — GitHub Actions +- `.gitlab-ci.yml` — GitLab CI +- `.circleci/config.yml` — CircleCI +- `Jenkinsfile`, `bitbucket-pipelines.yml`, etc. — other platforms + +### Step 2: Ask the user + +Gather the following information (skip questions you can infer from the codebase): + +1. **CI platform** — if not auto-detected, ask which platform they use +2. **Pipeline type** — continuous (every merge creates a completed release) or scheduled (releases go through stages like staging → production) +3. **Monorepo?** — if the repo has multiple apps/services, ask which paths to track (e.g. `apps/web/**`) +4. **Deployment stages** — for scheduled pipelines, ask what stages they use (e.g. staging, production) +5. **Release naming** — whether they want custom names/versions (e.g. `v1.2.0`) or the default (short commit SHA) + +### Step 3: Generate the CI configuration + +Use the patterns in this skill to generate the appropriate configuration. Add the config to an existing workflow file or create a new one, depending on the user's preference. + +### Step 4: Remind about secrets + +Tell the user to add the `LINEAR_ACCESS_KEY` secret to their CI environment: + +- **GitHub Actions**: Repository Settings → Secrets and variables → Actions → New repository secret +- **GitLab CI**: Settings → CI/CD → Variables +- **CircleCI**: Project Settings → Environment Variables + +The access key is created in Linear under Settings → Releases → Pipelines. + +## Key Concepts + +### Pipeline Types + +**Continuous pipelines**: Every merge to the main branch creates a completed release. Use a single `sync` command — releases are created in the completed stage automatically. + +**Scheduled pipelines**: Releases go through deployment stages (e.g. staging → production). Use multiple commands: + +- `sync` — creates a release or adds issues to the current release +- `update --stage=` — moves the release to a deployment stage +- `complete` — marks the release as done + +### Commands Reference + +| Command | Purpose | Key flags | +| ---------- | ---------------------------------- | ------------------------------------------------ | +| `sync` | Create/update release from commits | `--name`, `--release-version`, `--include-paths` | +| `update` | Move release to a deployment stage | `--stage` (required), `--release-version` | +| `complete` | Mark release as complete | `--release-version` | + +All commands support `--json` for structured output. + +### Requirements + +- **Full git history**: The checkout step must use `fetch-depth: 0` (or equivalent full clone) so Linear Release can scan commits between releases. +- **`LINEAR_ACCESS_KEY`**: Pipeline access key from Linear, stored as a CI secret. + +### Path Filtering (Monorepos) + +Use `--include-paths` to only include commits touching specific paths: + +```bash +linear-release sync --include-paths="apps/mobile/**" +linear-release sync --include-paths="apps/mobile/**,packages/shared/**" +``` + +Patterns use Git pathspec glob syntax, relative to the repository root. + +## GitHub Actions Patterns + +### Using the Official Action + +The simplest setup for GitHub Actions uses `linear/linear-release-action@v0`. + +#### Continuous Pipeline + +```yaml +name: Linear Release +on: + push: + branches: [main] + +jobs: + linear-release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - uses: linear/linear-release-action@v0 + with: + access_key: ${{ secrets.LINEAR_ACCESS_KEY }} +``` + +#### Scheduled Pipeline + +```yaml +name: Linear Release +on: + push: + branches: [main] + workflow_dispatch: + inputs: + action: + description: "Release action" + required: true + type: choice + options: + - sync + - update + - complete + stage: + description: "Deployment stage (for update)" + required: false + type: string + release_version: + description: "Release version (optional)" + required: false + type: string + +jobs: + linear-release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + # On push: sync issues to the current release + - uses: linear/linear-release-action@v0 + if: github.event_name == 'push' + with: + access_key: ${{ secrets.LINEAR_ACCESS_KEY }} + + # Manual: run the specified action + - uses: linear/linear-release-action@v0 + if: github.event_name == 'workflow_dispatch' + with: + access_key: ${{ secrets.LINEAR_ACCESS_KEY }} + action: ${{ inputs.action }} + stage: ${{ inputs.stage }} + release_version: ${{ inputs.release_version }} +``` + +#### With Path Filtering (Monorepo) + +```yaml +- uses: linear/linear-release-action@v0 + with: + access_key: ${{ secrets.LINEAR_ACCESS_KEY }} + include_paths: "apps/web/**,packages/shared/**" +``` + +### Using the CLI Binary Directly + +For advanced use cases or when you need more control: + +```yaml +name: Linear Release +on: + push: + branches: [main] + +jobs: + linear-release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Download Linear Release + run: | + curl -sL https://github.com/linear/linear-release/releases/latest/download/linear-release-linux-x64 -o linear-release + chmod +x linear-release + + - name: Sync release + run: ./linear-release sync + env: + LINEAR_ACCESS_KEY: ${{ secrets.LINEAR_ACCESS_KEY }} +``` + +## GitLab CI Patterns + +### Setup + +All GitLab CI patterns use this base setup to download the binary: + +```yaml +.linear-release-setup: &linear-release-setup + before_script: + - curl -sL https://github.com/linear/linear-release/releases/latest/download/linear-release-linux-x64 -o linear-release + - chmod +x linear-release +``` + +### Continuous Pipeline + +```yaml +.linear-release-setup: &linear-release-setup + before_script: + - curl -sL https://github.com/linear/linear-release/releases/latest/download/linear-release-linux-x64 -o linear-release + - chmod +x linear-release + +linear-release-sync: + <<: *linear-release-setup + stage: deploy + script: + - ./linear-release sync + variables: + LINEAR_ACCESS_KEY: $LINEAR_ACCESS_KEY + GIT_DEPTH: 0 + rules: + - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH +``` + +### Scheduled Pipeline + +```yaml +.linear-release-setup: &linear-release-setup + before_script: + - curl -sL https://github.com/linear/linear-release/releases/latest/download/linear-release-linux-x64 -o linear-release + - chmod +x linear-release + +# Runs on every merge to add issues to the current release +linear-release-sync: + <<: *linear-release-setup + stage: deploy + script: + - ./linear-release sync + variables: + LINEAR_ACCESS_KEY: $LINEAR_ACCESS_KEY + GIT_DEPTH: 0 + rules: + - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH + +# Trigger manually to move the release to a deployment stage +linear-release-update: + <<: *linear-release-setup + stage: deploy + script: + - ./linear-release update --stage="$STAGE" + variables: + LINEAR_ACCESS_KEY: $LINEAR_ACCESS_KEY + STAGE: "" + GIT_DEPTH: 0 + rules: + - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH + when: manual + +# Trigger manually to complete the release +linear-release-complete: + <<: *linear-release-setup + stage: deploy + script: + - ./linear-release complete + variables: + LINEAR_ACCESS_KEY: $LINEAR_ACCESS_KEY + GIT_DEPTH: 0 + rules: + - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH + when: manual +``` + +### With Path Filtering (Monorepo) + +Add `--include-paths` to the `sync` script in any of the patterns above: + +```yaml +script: + - ./linear-release sync --include-paths="apps/web/**,packages/shared/**" +``` + +## CircleCI Patterns + +### Continuous Pipeline + +```yaml +version: 2.1 + +jobs: + linear-release-sync: + docker: + - image: cimg/base:current + steps: + - checkout + - run: + name: Download Linear Release + command: | + curl -sL https://github.com/linear/linear-release/releases/latest/download/linear-release-linux-x64 -o linear-release + chmod +x linear-release + - run: + name: Sync release + command: ./linear-release sync + +workflows: + release: + jobs: + - linear-release-sync: + filters: + branches: + only: main +``` + +The `LINEAR_ACCESS_KEY` environment variable must be set in CircleCI project settings. + +### Scheduled Pipeline + +```yaml +version: 2.1 + +jobs: + linear-release-sync: + docker: + - image: cimg/base:current + steps: + - checkout + - run: + name: Download Linear Release + command: | + curl -sL https://github.com/linear/linear-release/releases/latest/download/linear-release-linux-x64 -o linear-release + chmod +x linear-release + - run: + name: Sync release + command: ./linear-release sync + + linear-release-update: + docker: + - image: cimg/base:current + parameters: + stage: + type: string + steps: + - checkout + - run: + name: Download Linear Release + command: | + curl -sL https://github.com/linear/linear-release/releases/latest/download/linear-release-linux-x64 -o linear-release + chmod +x linear-release + - run: + name: Update release stage + command: ./linear-release update --stage="<< parameters.stage >>" + + linear-release-complete: + docker: + - image: cimg/base:current + steps: + - checkout + - run: + name: Download Linear Release + command: | + curl -sL https://github.com/linear/linear-release/releases/latest/download/linear-release-linux-x64 -o linear-release + chmod +x linear-release + - run: + name: Complete release + command: ./linear-release complete + +workflows: + # Sync on every push to main + release-sync: + jobs: + - linear-release-sync: + filters: + branches: + only: main + + # Trigger stage updates and completion via CircleCI API + release-update: + jobs: + - linear-release-update: + stage: staging + - hold-production: + type: approval + requires: + - linear-release-update + - linear-release-update: + stage: production + requires: + - hold-production + - hold-complete: + type: approval + requires: + - linear-release-update + - linear-release-complete: + requires: + - hold-complete +``` + +## General CI Pattern + +For any CI platform not listed above, use this generic bash pattern: + +```bash +# 1. Ensure full git history is available (no shallow clones) +# 2. Download the binary for your platform (linux-x64, darwin-arm64, or darwin-x64) +curl -sL https://github.com/linear/linear-release/releases/latest/download/linear-release-linux-x64 -o linear-release +chmod +x linear-release + +# 3. Set the access key +export LINEAR_ACCESS_KEY="" + +# 4. Run the appropriate command +./linear-release sync # Continuous: on every merge +./linear-release sync --name="v1.2.0" # With custom name +./linear-release update --stage="staging" # Scheduled: move to stage +./linear-release complete # Scheduled: finalize release +./linear-release sync --include-paths="apps/web/**" # Monorepo: filter by path +``` + +### Checklist + +- [ ] Full clone / `fetch-depth: 0` — shallow clones will miss commits between releases +- [ ] `LINEAR_ACCESS_KEY` set as a secret environment variable +- [ ] Correct binary for the runner platform (`linux-x64`, `darwin-arm64`, or `darwin-x64`) +- [ ] Runs on merges to the main/default branch From 31d67ae9448b011316aeb25584d03848366fbc46 Mon Sep 17 00:00:00 2001 From: Romain Cascino Date: Tue, 24 Feb 2026 10:00:17 +0000 Subject: [PATCH 2/2] More improvements to skill + adjust README --- README.md | 14 +- examples/circleci-continuous.yml | 31 ++ examples/circleci-scheduled.yml | 129 +++++++ examples/github-actions-continuous.yml | 22 ++ examples/github-actions-scheduled.yml | 83 +++++ examples/gitlab-ci-continuous.yml | 21 ++ examples/gitlab-ci-scheduled.yml | 79 +++++ skills/linear-release-setup/SKILL.md | 443 ++++--------------------- 8 files changed, 440 insertions(+), 382 deletions(-) create mode 100644 examples/circleci-continuous.yml create mode 100644 examples/circleci-scheduled.yml create mode 100644 examples/github-actions-continuous.yml create mode 100644 examples/github-actions-scheduled.yml create mode 100644 examples/gitlab-ci-continuous.yml create mode 100644 examples/gitlab-ci-scheduled.yml diff --git a/README.md b/README.md index e5ea53f..9084064 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,15 @@ Linear Release is a CLI tool that integrates your CI/CD pipeline with [Linear's - Scans commits for Linear issue identifiers (e.g., `ENG-123`) - Detects pull request references in commit messages - Creates and updates releases in Linear -- Tracks deployment stages (staging, production, etc.) +- Tracks deployment stages for scheduled releases + +## Pipeline Types + +Linear Release supports two pipeline styles, configured in Linear: + +**Continuous**: Every deployment creates a completed release. Use `sync` after each deploy — the release is created already completed. + +**Scheduled**: An ongoing release collects changes over time, then moves through stages (e.g. "code freeze", "qa") before completion. Useful for release trains. Use `sync` to add issues, `update` to move between stages, and `complete` to finalize. ## Installation @@ -77,6 +85,10 @@ chmod +x linear-release LINEAR_ACCESS_KEY= ./linear-release sync ``` +### AI-assisted setup + +Use the [Linear Release setup skill](./skills/linear-release-setup/SKILL.md) to generate CI configuration tailored to your project. It supports GitHub Actions, GitLab CI, CircleCI, and other platforms, and walks you through continuous vs. scheduled pipelines, monorepo path filtering, and more. + ## Commands ### `sync` diff --git a/examples/circleci-continuous.yml b/examples/circleci-continuous.yml new file mode 100644 index 0000000..36179e3 --- /dev/null +++ b/examples/circleci-continuous.yml @@ -0,0 +1,31 @@ +# Linear Release — CircleCI (Continuous Pipeline) +# +# Use when: Every deployment creates a completed release automatically. +# Trigger: On every push to main. +# Customize: Branch name, path filters (--include-paths). +# Note: Set LINEAR_ACCESS_KEY in CircleCI project settings. + +version: 2.1 + +jobs: + linear-release-sync: + docker: + - image: cimg/base:current + steps: + - checkout + - run: + name: Download Linear Release + command: | + curl -sL https://github.com/linear/linear-release/releases/latest/download/linear-release-linux-x64 -o linear-release + chmod +x linear-release + - run: + name: Sync release + command: ./linear-release sync + +workflows: + release: + jobs: + - linear-release-sync: + filters: + branches: + only: main diff --git a/examples/circleci-scheduled.yml b/examples/circleci-scheduled.yml new file mode 100644 index 0000000..cfd6c8f --- /dev/null +++ b/examples/circleci-scheduled.yml @@ -0,0 +1,129 @@ +# Linear Release — CircleCI (Scheduled) +# +# Use when: Releases follow a branch cut model. Main syncs without a version, +# release branches derive version from CIRCLE_BRANCH. +# +# Trigger: Auto on push, API trigger for later stage transitions and completion. +# Customize: Branch patterns, stage names, auto-promotion stage. +# Note: Set LINEAR_ACCESS_KEY and CIRCLE_TOKEN in CircleCI project settings. +# +# Branch creation detection: CircleCI has no built-in signal, so auto-promotion +# checks the API for previous pipelines on the branch. +# +# Monorepo: CircleCI doesn't support path filtering natively. Use the `path-filtering` +# orb or split into separate workflows and use the API to conditionally trigger. + +version: 2.1 + +# Pipeline parameters — passed via CircleCI API for manual stage transitions. +# On normal pushes, defaults apply and only the sync workflows run. +parameters: + run-release-action: + type: boolean + default: false + action: + type: enum + enum: ["update", "complete"] + default: "update" + stage: + type: string + default: "" + release_version: + type: string + default: "" + +jobs: + linear-release-sync-main: + docker: + - image: cimg/base:current + steps: + - checkout + - run: + name: Download Linear Release + command: | + curl -sL https://github.com/linear/linear-release/releases/latest/download/linear-release-linux-x64 -o linear-release + chmod +x linear-release + - run: + name: Sync release + command: ./linear-release sync + + linear-release-sync-release: + docker: + - image: cimg/base:current + steps: + - checkout + - run: + name: Download Linear Release + command: | + curl -sL https://github.com/linear/linear-release/releases/latest/download/linear-release-linux-x64 -o linear-release + chmod +x linear-release + - run: + name: Derive version and sync + command: | + RELEASE_VERSION="${CIRCLE_BRANCH#release/}" + ./linear-release sync --release-version="$RELEASE_VERSION" + - run: + name: Auto-promote on branch creation + command: | + RELEASE_VERSION="${CIRCLE_BRANCH#release/}" + # Check if this is the first pipeline on this branch (branch creation) + PIPELINE_COUNT=$(curl -s -H "Circle-Token: $CIRCLE_TOKEN" \ + "https://circleci.com/api/v2/project/gh/$CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME/pipeline?branch=$CIRCLE_BRANCH" \ + | jq '.items | length') + if [ "$PIPELINE_COUNT" -le 1 ]; then + ./linear-release update --release-version="$RELEASE_VERSION" --stage="code freeze" + fi + + linear-release-action: + docker: + - image: cimg/base:current + steps: + - checkout + - run: + name: Download Linear Release + command: | + curl -sL https://github.com/linear/linear-release/releases/latest/download/linear-release-linux-x64 -o linear-release + chmod +x linear-release + - run: + name: Run release action + command: | + case "<< pipeline.parameters.action >>" in + update) + ./linear-release update \ + --release-version="<< pipeline.parameters.release_version >>" \ + --stage="<< pipeline.parameters.stage >>" + ;; + complete) + ./linear-release complete \ + --release-version="<< pipeline.parameters.release_version >>" + ;; + esac + +workflows: + # On normal pushes — sync issues to the release + release-sync-main: + when: + not: << pipeline.parameters.run-release-action >> + jobs: + - linear-release-sync-main: + filters: + branches: + only: main + + release-sync-release: + when: + not: << pipeline.parameters.run-release-action >> + jobs: + - linear-release-sync-release: + filters: + branches: + only: /^release\/.*/ + + # Trigger via CircleCI API: + # curl -X POST https://circleci.com/api/v2/project/gh/ORG/REPO/pipeline \ + # -H "Circle-Token: $CIRCLE_TOKEN" -H "Content-Type: application/json" \ + # -d '{"parameters": {"run-release-action": true, "action": "update", "stage": "qa", "release_version": "1.2.0"}}' + release-action: + when: << pipeline.parameters.run-release-action >> + jobs: + - linear-release-action diff --git a/examples/github-actions-continuous.yml b/examples/github-actions-continuous.yml new file mode 100644 index 0000000..2c5416e --- /dev/null +++ b/examples/github-actions-continuous.yml @@ -0,0 +1,22 @@ +# Linear Release — GitHub Actions (Continuous Pipeline) +# +# Use when: Every deployment creates a completed release automatically. +# Trigger: On every push to main. +# Customize: Branch name, path filters (include_paths input). + +name: Linear Release +on: + push: + branches: [main] + +jobs: + linear-release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - uses: linear/linear-release-action@v0 + with: + access_key: ${{ secrets.LINEAR_ACCESS_KEY }} diff --git a/examples/github-actions-scheduled.yml b/examples/github-actions-scheduled.yml new file mode 100644 index 0000000..946d63a --- /dev/null +++ b/examples/github-actions-scheduled.yml @@ -0,0 +1,83 @@ +# Linear Release — GitHub Actions (Scheduled) +# +# Use when: Releases follow a branch cut model. Main collects changes into the +# current release, a release branch is cut for stabilization, and branch creation +# auto-promotes to "code freeze". +# +# Trigger: push to main (sync), push to release/* (sync + auto code freeze on +# creation), or manual workflow_dispatch for later stages and completion. +# Customize: Branch patterns, stage names, version derivation. +# +# Monorepo: GitHub Actions `paths` applies to all branches in a push trigger. +# To path-filter main without filtering release branches, split into two files: +# File 1 (main): add `paths: [...]` to the push trigger, keep only the main sync step. +# File 2 (release): keep the release branch + workflow_dispatch logic as-is. +# Add `include_paths` to the action in both files. + +name: Linear Release +on: + push: + branches: + - main + - "release/**" + workflow_dispatch: + inputs: + action: + description: "Release action" + required: true + type: choice + options: + - update + - complete + stage: + description: "Release stage (for update, e.g. qa)" + required: false + type: string + release_version: + description: "Release version" + required: true + type: string + +jobs: + linear-release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + # Main branch: sync without --release-version (targets current started release) + - uses: linear/linear-release-action@v0 + if: github.event_name == 'push' && !startsWith(github.ref_name, 'release/') + with: + access_key: ${{ secrets.LINEAR_ACCESS_KEY }} + + # Release branch: derive version from branch name + - name: Set release version + if: github.event_name == 'push' && startsWith(github.ref_name, 'release/') + run: echo "RELEASE_VERSION=${GITHUB_REF_NAME#release/}" >> "$GITHUB_ENV" + + # Release branch: sync with explicit version + - uses: linear/linear-release-action@v0 + if: github.event_name == 'push' && startsWith(github.ref_name, 'release/') + with: + access_key: ${{ secrets.LINEAR_ACCESS_KEY }} + release_version: ${{ env.RELEASE_VERSION }} + + # Branch creation: auto-promote to code freeze + - uses: linear/linear-release-action@v0 + if: github.event_name == 'push' && startsWith(github.ref_name, 'release/') && github.event.created + with: + access_key: ${{ secrets.LINEAR_ACCESS_KEY }} + action: update + stage: code freeze + release_version: ${{ env.RELEASE_VERSION }} + + # Manual: run the specified action (later stages, completion) + - uses: linear/linear-release-action@v0 + if: github.event_name == 'workflow_dispatch' + with: + access_key: ${{ secrets.LINEAR_ACCESS_KEY }} + action: ${{ inputs.action }} + stage: ${{ inputs.stage }} + release_version: ${{ inputs.release_version }} diff --git a/examples/gitlab-ci-continuous.yml b/examples/gitlab-ci-continuous.yml new file mode 100644 index 0000000..fac0062 --- /dev/null +++ b/examples/gitlab-ci-continuous.yml @@ -0,0 +1,21 @@ +# Linear Release — GitLab CI (Continuous Pipeline) +# +# Use when: Every deployment creates a completed release automatically. +# Trigger: On every push to the default branch. +# Customize: Branch rules, path filters (--include-paths). + +.linear-release-setup: &linear-release-setup + before_script: + - curl -sL https://github.com/linear/linear-release/releases/latest/download/linear-release-linux-x64 -o linear-release + - chmod +x linear-release + +linear-release-sync: + <<: *linear-release-setup + stage: deploy + script: + - ./linear-release sync + variables: + LINEAR_ACCESS_KEY: $LINEAR_ACCESS_KEY + GIT_DEPTH: 0 + rules: + - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH diff --git a/examples/gitlab-ci-scheduled.yml b/examples/gitlab-ci-scheduled.yml new file mode 100644 index 0000000..440740a --- /dev/null +++ b/examples/gitlab-ci-scheduled.yml @@ -0,0 +1,79 @@ +# Linear Release — GitLab CI (Scheduled) +# +# Use when: Releases follow a branch cut model. Main syncs without a version, +# release branches derive version from branch name. Branch creation auto-promotes +# to "code freeze" (detected via CI_COMMIT_BEFORE_SHA being all zeros). +# +# Trigger: Auto on push, manual for later stage transitions and completion. +# Customize: Branch patterns, stage names, version derivation. +# +# Monorepo: Add `changes` filter to the main job's rules only (not release branch jobs): +# rules: +# - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH +# changes: +# - "apps/mobile/**" +# - "packages/shared/**" +# Also add --include-paths to all sync commands. + +.linear-release-setup: &linear-release-setup + before_script: + - curl -sL https://github.com/linear/linear-release/releases/latest/download/linear-release-linux-x64 -o linear-release + - chmod +x linear-release + +# Main branch: sync without --release-version (targets current started release) +linear-release-sync-main: + <<: *linear-release-setup + stage: deploy + script: + - ./linear-release sync + variables: + LINEAR_ACCESS_KEY: $LINEAR_ACCESS_KEY + GIT_DEPTH: 0 + rules: + - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH + +# Release branch: derive version from branch name and sync +linear-release-sync-release: + <<: *linear-release-setup + stage: deploy + script: + - export RELEASE_VERSION="${CI_COMMIT_BRANCH#release/}" + - ./linear-release sync --release-version="$RELEASE_VERSION" + # Auto-promote on branch creation (first push to a new branch) + - | + if [ "$CI_COMMIT_BEFORE_SHA" = "0000000000000000000000000000000000000000" ]; then + ./linear-release update --release-version="$RELEASE_VERSION" --stage="code freeze" + fi + variables: + LINEAR_ACCESS_KEY: $LINEAR_ACCESS_KEY + GIT_DEPTH: 0 + rules: + - if: $CI_COMMIT_BRANCH =~ /^release\// + +# Trigger manually for later stage transitions and completion +linear-release-update: + <<: *linear-release-setup + stage: deploy + script: + - export RELEASE_VERSION="${CI_COMMIT_BRANCH#release/}" + - ./linear-release update --release-version="$RELEASE_VERSION" --stage="$STAGE" + variables: + LINEAR_ACCESS_KEY: $LINEAR_ACCESS_KEY + STAGE: "" + GIT_DEPTH: 0 + rules: + - if: $CI_COMMIT_BRANCH =~ /^release\// + when: manual + +linear-release-complete: + <<: *linear-release-setup + stage: deploy + script: + - export RELEASE_VERSION="${CI_COMMIT_BRANCH#release/}" + - ./linear-release complete --release-version="$RELEASE_VERSION" + variables: + LINEAR_ACCESS_KEY: $LINEAR_ACCESS_KEY + GIT_DEPTH: 0 + rules: + - if: $CI_COMMIT_BRANCH =~ /^release\// + when: manual diff --git a/skills/linear-release-setup/SKILL.md b/skills/linear-release-setup/SKILL.md index 3c73035..904e14b 100644 --- a/skills/linear-release-setup/SKILL.md +++ b/skills/linear-release-setup/SKILL.md @@ -7,429 +7,110 @@ description: Generate CI/CD configuration for Linear Release. Use when setting u # Linear Release Setup -[Linear Release](https://github.com/linear/linear-release) is a CLI tool that integrates CI/CD pipelines with [Linear's release management](https://linear.app/docs/releases). It scans commits for issue identifiers, creates/updates releases, and tracks deployment stages. - -This skill generates CI/CD configuration files for Linear Release based on the user's project setup. - ## Interactive Workflow -Follow these steps when the user asks to set up Linear Release: +### Step 1: Preflight -### Step 1: Detect existing CI setup +Before generating config, confirm: -Look for existing CI configuration files: - -- `.github/workflows/*.yml` — GitHub Actions -- `.gitlab-ci.yml` — GitLab CI -- `.circleci/config.yml` — CircleCI -- `Jenkinsfile`, `bitbucket-pipelines.yml`, etc. — other platforms +1. **Pipeline exists in Linear** — the user must have created a release pipeline in Linear first (Settings → Releases). Each pipeline has its own access key. +2. **Detect CI platform** — look for `.github/workflows/*.yml` (GitHub Actions), `.gitlab-ci.yml` (GitLab CI), `.circleci/config.yml` (CircleCI), or other CI config. +3. **Detect default branch** — check `git symbolic-ref refs/remotes/origin/HEAD` or the CI config. Don't assume `main`. ### Step 2: Ask the user Gather the following information (skip questions you can infer from the codebase): -1. **CI platform** — if not auto-detected, ask which platform they use -2. **Pipeline type** — continuous (every merge creates a completed release) or scheduled (releases go through stages like staging → production) +1. **CI platform** — if not auto-detected +2. **Pipeline type** — continuous (every deploy = a completed release) or scheduled (releases collect changes over time, then move through stages) 3. **Monorepo?** — if the repo has multiple apps/services, ask which paths to track (e.g. `apps/web/**`) -4. **Deployment stages** — for scheduled pipelines, ask what stages they use (e.g. staging, production) -5. **Release naming** — whether they want custom names/versions (e.g. `v1.2.0`) or the default (short commit SHA) - -### Step 3: Generate the CI configuration - -Use the patterns in this skill to generate the appropriate configuration. Add the config to an existing workflow file or create a new one, depending on the user's preference. - -### Step 4: Remind about secrets - -Tell the user to add the `LINEAR_ACCESS_KEY` secret to their CI environment: - -- **GitHub Actions**: Repository Settings → Secrets and variables → Actions → New repository secret -- **GitLab CI**: Settings → CI/CD → Variables -- **CircleCI**: Project Settings → Environment Variables - -The access key is created in Linear under Settings → Releases → Pipelines. - -## Key Concepts - -### Pipeline Types - -**Continuous pipelines**: Every merge to the main branch creates a completed release. Use a single `sync` command — releases are created in the completed stage automatically. -**Scheduled pipelines**: Releases go through deployment stages (e.g. staging → production). Use multiple commands: - -- `sync` — creates a release or adds issues to the current release -- `update --stage=` — moves the release to a deployment stage -- `complete` — marks the release as done - -### Commands Reference - -| Command | Purpose | Key flags | -| ---------- | ---------------------------------- | ------------------------------------------------ | -| `sync` | Create/update release from commits | `--name`, `--release-version`, `--include-paths` | -| `update` | Move release to a deployment stage | `--stage` (required), `--release-version` | -| `complete` | Mark release as complete | `--release-version` | +For scheduled pipelines, always ask these explicitly (don't infer — they significantly affect the generated config): -All commands support `--json` for structured output. +4. **Branch model** — just `main`, or `main` + release branches (e.g. `release/*`)? +5. **Release versioning** — calendar-based (e.g. `2026.05`), semver (e.g. `1.2.0`), or default (commit SHA)? Where does the version come from — branch name, CI variable, file, git tag? +6. **Release stages** — what stages before completion (e.g. "code freeze", "qa")? +7. **Automation level** — all manual (via workflow_dispatch), or some automated (e.g. branch creation → code freeze)? -### Requirements +### Step 3: Generate the CI configuration -- **Full git history**: The checkout step must use `fetch-depth: 0` (or equivalent full clone) so Linear Release can scan commits between releases. -- **`LINEAR_ACCESS_KEY`**: Pipeline access key from Linear, stored as a CI secret. +Select the right example template, read it, adapt it (branch patterns, stage names, paths, version format), and add it to an existing workflow or create a new one. -### Path Filtering (Monorepos) +| Platform | Pipeline Type | Example | +| -------------- | ------------- | ---------------------------------------------------------------------------------------------------------------------------- | +| GitHub Actions | Continuous | [`github-actions-continuous.yml`](https://github.com/linear/linear-release/blob/main/examples/github-actions-continuous.yml) | +| GitHub Actions | Scheduled | [`github-actions-scheduled.yml`](https://github.com/linear/linear-release/blob/main/examples/github-actions-scheduled.yml) | +| GitLab CI | Continuous | [`gitlab-ci-continuous.yml`](https://github.com/linear/linear-release/blob/main/examples/gitlab-ci-continuous.yml) | +| GitLab CI | Scheduled | [`gitlab-ci-scheduled.yml`](https://github.com/linear/linear-release/blob/main/examples/gitlab-ci-scheduled.yml) | +| CircleCI | Continuous | [`circleci-continuous.yml`](https://github.com/linear/linear-release/blob/main/examples/circleci-continuous.yml) | +| CircleCI | Scheduled | [`circleci-scheduled.yml`](https://github.com/linear/linear-release/blob/main/examples/circleci-scheduled.yml) | -Use `--include-paths` to only include commits touching specific paths: +For GitHub Actions, prefer the official action (`linear/linear-release-action@v0`). For other platforms, download the CLI binary and refer to the [README](https://github.com/linear/linear-release#commands) for the full command reference: ```bash -linear-release sync --include-paths="apps/mobile/**" -linear-release sync --include-paths="apps/mobile/**,packages/shared/**" +curl -sL https://github.com/linear/linear-release/releases/latest/download/linear-release-linux-x64 -o linear-release +chmod +x linear-release ``` -Patterns use Git pathspec glob syntax, relative to the repository root. +Each scheduled example includes a **monorepo** note in the header explaining how to split workflows for path filtering per platform. -## GitHub Actions Patterns - -### Using the Official Action - -The simplest setup for GitHub Actions uses `linear/linear-release-action@v0`. - -#### Continuous Pipeline - -```yaml -name: Linear Release -on: - push: - branches: [main] - -jobs: - linear-release: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 +### Step 4: Remind about secrets - - uses: linear/linear-release-action@v0 - with: - access_key: ${{ secrets.LINEAR_ACCESS_KEY }} -``` +Tell the user to add the `LINEAR_ACCESS_KEY` secret to their CI environment: -#### Scheduled Pipeline - -```yaml -name: Linear Release -on: - push: - branches: [main] - workflow_dispatch: - inputs: - action: - description: "Release action" - required: true - type: choice - options: - - sync - - update - - complete - stage: - description: "Deployment stage (for update)" - required: false - type: string - release_version: - description: "Release version (optional)" - required: false - type: string - -jobs: - linear-release: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - # On push: sync issues to the current release - - uses: linear/linear-release-action@v0 - if: github.event_name == 'push' - with: - access_key: ${{ secrets.LINEAR_ACCESS_KEY }} - - # Manual: run the specified action - - uses: linear/linear-release-action@v0 - if: github.event_name == 'workflow_dispatch' - with: - access_key: ${{ secrets.LINEAR_ACCESS_KEY }} - action: ${{ inputs.action }} - stage: ${{ inputs.stage }} - release_version: ${{ inputs.release_version }} -``` +- **GitHub Actions**: Repository Settings → Secrets and variables → Actions → New repository secret +- **GitLab CI**: Settings → CI/CD → Variables +- **CircleCI**: Project Settings → Environment Variables -#### With Path Filtering (Monorepo) +The access key is created in Linear from the pipeline's settings page. Each pipeline has its own access key. -```yaml -- uses: linear/linear-release-action@v0 - with: - access_key: ${{ secrets.LINEAR_ACCESS_KEY }} - include_paths: "apps/web/**,packages/shared/**" -``` +## Key Concepts -### Using the CLI Binary Directly - -For advanced use cases or when you need more control: - -```yaml -name: Linear Release -on: - push: - branches: [main] - -jobs: - linear-release: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Download Linear Release - run: | - curl -sL https://github.com/linear/linear-release/releases/latest/download/linear-release-linux-x64 -o linear-release - chmod +x linear-release - - - name: Sync release - run: ./linear-release sync - env: - LINEAR_ACCESS_KEY: ${{ secrets.LINEAR_ACCESS_KEY }} -``` +### Pipelines -## GitLab CI Patterns +A **release pipeline** in Linear represents a deployment lane — e.g. "iOS", "Android", "Web". Each product or environment you ship independently should be its own pipeline. Don't confuse this with CI pipelines — a Linear pipeline is the release tracking unit, and your CI config calls the CLI to update it. -### Setup +### Pipeline Types -All GitLab CI patterns use this base setup to download the binary: +**Continuous**: Every deploy creates a completed release. One `sync` call on push. -```yaml -.linear-release-setup: &linear-release-setup - before_script: - - curl -sL https://github.com/linear/linear-release/releases/latest/download/linear-release-linux-x64 -o linear-release - - chmod +x linear-release -``` +**Scheduled**: An ongoing release collects changes, then moves through stages before completion. Three commands: -### Continuous Pipeline - -```yaml -.linear-release-setup: &linear-release-setup - before_script: - - curl -sL https://github.com/linear/linear-release/releases/latest/download/linear-release-linux-x64 -o linear-release - - chmod +x linear-release - -linear-release-sync: - <<: *linear-release-setup - stage: deploy - script: - - ./linear-release sync - variables: - LINEAR_ACCESS_KEY: $LINEAR_ACCESS_KEY - GIT_DEPTH: 0 - rules: - - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH -``` +- `sync` — adds issues from new commits to the current release +- `update --stage=` — moves the release to a stage (e.g. "code freeze") +- `complete` — marks the release as shipped -### Scheduled Pipeline - -```yaml -.linear-release-setup: &linear-release-setup - before_script: - - curl -sL https://github.com/linear/linear-release/releases/latest/download/linear-release-linux-x64 -o linear-release - - chmod +x linear-release - -# Runs on every merge to add issues to the current release -linear-release-sync: - <<: *linear-release-setup - stage: deploy - script: - - ./linear-release sync - variables: - LINEAR_ACCESS_KEY: $LINEAR_ACCESS_KEY - GIT_DEPTH: 0 - rules: - - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH - -# Trigger manually to move the release to a deployment stage -linear-release-update: - <<: *linear-release-setup - stage: deploy - script: - - ./linear-release update --stage="$STAGE" - variables: - LINEAR_ACCESS_KEY: $LINEAR_ACCESS_KEY - STAGE: "" - GIT_DEPTH: 0 - rules: - - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH - when: manual - -# Trigger manually to complete the release -linear-release-complete: - <<: *linear-release-setup - stage: deploy - script: - - ./linear-release complete - variables: - LINEAR_ACCESS_KEY: $LINEAR_ACCESS_KEY - GIT_DEPTH: 0 - rules: - - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH - when: manual -``` +The typical scheduled flow uses **release branches**: `main` collects changes, a `release/*` branch is cut for stabilization, and branch creation auto-promotes to a stage. Version is derived from the branch name (e.g. `release/1.2.0` → `1.2.0`). On `main`, `sync` runs without `--release-version` so issues land on the current started release. On release branches, `sync` runs with `--release-version` to target the specific release. -### With Path Filtering (Monorepo) +### Stages -Add `--include-paths` to the `sync` script in any of the patterns above: +Stages are phases a scheduled release moves through — e.g. "code freeze", "in review", "qa". They represent process steps, not environments. Different environments (staging, production) should be separate pipelines. -```yaml -script: - - ./linear-release sync --include-paths="apps/web/**,packages/shared/**" -``` +Stages can be **frozen** in Linear. A frozen stage makes `sync` (without `--release-version`) skip that release and target the next one — a safety net for code freezes. -## CircleCI Patterns - -### Continuous Pipeline - -```yaml -version: 2.1 - -jobs: - linear-release-sync: - docker: - - image: cimg/base:current - steps: - - checkout - - run: - name: Download Linear Release - command: | - curl -sL https://github.com/linear/linear-release/releases/latest/download/linear-release-linux-x64 -o linear-release - chmod +x linear-release - - run: - name: Sync release - command: ./linear-release sync - -workflows: - release: - jobs: - - linear-release-sync: - filters: - branches: - only: main -``` +### Commands -The `LINEAR_ACCESS_KEY` environment variable must be set in CircleCI project settings. - -### Scheduled Pipeline - -```yaml -version: 2.1 - -jobs: - linear-release-sync: - docker: - - image: cimg/base:current - steps: - - checkout - - run: - name: Download Linear Release - command: | - curl -sL https://github.com/linear/linear-release/releases/latest/download/linear-release-linux-x64 -o linear-release - chmod +x linear-release - - run: - name: Sync release - command: ./linear-release sync - - linear-release-update: - docker: - - image: cimg/base:current - parameters: - stage: - type: string - steps: - - checkout - - run: - name: Download Linear Release - command: | - curl -sL https://github.com/linear/linear-release/releases/latest/download/linear-release-linux-x64 -o linear-release - chmod +x linear-release - - run: - name: Update release stage - command: ./linear-release update --stage="<< parameters.stage >>" - - linear-release-complete: - docker: - - image: cimg/base:current - steps: - - checkout - - run: - name: Download Linear Release - command: | - curl -sL https://github.com/linear/linear-release/releases/latest/download/linear-release-linux-x64 -o linear-release - chmod +x linear-release - - run: - name: Complete release - command: ./linear-release complete - -workflows: - # Sync on every push to main - release-sync: - jobs: - - linear-release-sync: - filters: - branches: - only: main - - # Trigger stage updates and completion via CircleCI API - release-update: - jobs: - - linear-release-update: - stage: staging - - hold-production: - type: approval - requires: - - linear-release-update - - linear-release-update: - stage: production - requires: - - hold-production - - hold-complete: - type: approval - requires: - - linear-release-update - - linear-release-complete: - requires: - - hold-complete -``` +| Command | Purpose | Key flags | +| ---------- | ---------------------------------- | ------------------------------------------------ | +| `sync` | Create/update release from commits | `--name`, `--release-version`, `--include-paths` | +| `update` | Move release to a stage | `--stage` (required), `--release-version` | +| `complete` | Mark release as complete | `--release-version` | -## General CI Pattern +### Path Filtering (Monorepos) -For any CI platform not listed above, use this generic bash pattern: +Path filters can be configured in Linear's pipeline settings or via the CLI's `--include-paths` flag (CLI takes precedence if both are set). If the user has already configured paths in Linear, the CLI flag is optional. -```bash -# 1. Ensure full git history is available (no shallow clones) -# 2. Download the binary for your platform (linux-x64, darwin-arm64, or darwin-x64) -curl -sL https://github.com/linear/linear-release/releases/latest/download/linear-release-linux-x64 -o linear-release -chmod +x linear-release +For **monorepos with release branches**, CI platforms often can't path-filter differently per branch. The solution is two workflow/job definitions: `main` with path filtering, release branches without. Each scheduled example includes platform-specific instructions. -# 3. Set the access key -export LINEAR_ACCESS_KEY="" +### Requirements -# 4. Run the appropriate command -./linear-release sync # Continuous: on every merge -./linear-release sync --name="v1.2.0" # With custom name -./linear-release update --stage="staging" # Scheduled: move to stage -./linear-release complete # Scheduled: finalize release -./linear-release sync --include-paths="apps/web/**" # Monorepo: filter by path -``` +- **Full git history**: `fetch-depth: 0` or equivalent — shallow clones miss commits between releases. +- **`LINEAR_ACCESS_KEY`**: Per-pipeline access key from Linear, stored as a CI secret. ### Checklist -- [ ] Full clone / `fetch-depth: 0` — shallow clones will miss commits between releases -- [ ] `LINEAR_ACCESS_KEY` set as a secret environment variable -- [ ] Correct binary for the runner platform (`linux-x64`, `darwin-arm64`, or `darwin-x64`) -- [ ] Runs on merges to the main/default branch +- [ ] Full clone / `fetch-depth: 0` +- [ ] `LINEAR_ACCESS_KEY` set as a secret (one per pipeline) +- [ ] Correct binary platform (`linux-x64`, `darwin-arm64`, or `darwin-x64`) +- [ ] Triggers on the correct branches (`main` for continuous; `main` + `release/*` for scheduled) +- [ ] Monorepo: path filters set (in Linear config or via `--include-paths`), and separate workflows if using release branches