| name | linear-ci-integration |
| description | Configure Linear CI/CD integration with GitHub Actions and testing.
Use when setting up automated testing, configuring CI pipelines,
or integrating Linear sync into your build process.
Trigger with phrases like "linear CI", "linear GitHub Actions",
"linear automated tests", "CI linear pipeline", "linear CI/CD".
|
| allowed-tools | Read, Write, Edit, Bash(gh:*) |
| version | 1.0.0 |
| license | MIT |
| author | Jeremy Longshore <jeremy@intentsolutions.io> |
Linear CI Integration
Overview
Integrate Linear into your CI/CD pipeline for automated testing and deployment tracking.
Prerequisites
- GitHub repository with Actions enabled
- Linear API key for CI
- npm/pnpm project configured
Instructions
Step 1: Store Secrets in GitHub
gh secret set LINEAR_API_KEY --body "lin_api_xxxxxxxxxxxx"
gh secret set LINEAR_WEBHOOK_SECRET --body "your_webhook_secret"
Step 2: Create Test Workflow
name: Linear Integration Tests
on:
push:
branches: [main]
pull_request:
branches: [main]
env:
LINEAR_API_KEY: ${{ secrets.LINEAR_API_KEY }}
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run Linear integration tests
run: npm run test:linear
env:
LINEAR_API_KEY: ${{
Step 3: Create Integration Test Suite
import { describe, it, expect, beforeAll, afterAll } from "vitest";
import { LinearClient } from "@linear/sdk";
describe("Linear Integration", () => {
let client: LinearClient;
let testTeamId: string;
const createdIssueIds: string[] = [];
beforeAll(async () => {
const apiKey = process.env.LINEAR_API_KEY;
if (!apiKey) {
throw new Error("LINEAR_API_KEY required for integration tests");
}
client = new LinearClient({ apiKey });
const teams = await client.teams();
testTeamId = teams.nodes[0].id;
});
afterAll(async () => {
for (const id of createdIssueIds) {
try {
await client.deleteIssue(id);
} catch {
}
}
});
(, () => {
viewer = client.;
(viewer.).();
(viewer.).();
});
(, () => {
result = client.({
: testTeamId,
: ,
: ,
});
(result.).();
issue = result.;
(issue?.).();
(issue) createdIssueIds.(issue.);
});
(, () => {
issues = client.({ : });
(issues..).();
});
});
Step 4: PR Status Updates
name: Update Linear Issues from PR
on:
pull_request:
types: [opened, closed, merged]
jobs:
update-linear:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Extract Linear Issue ID
id: extract
run: |
# Extract issue identifier from branch name (e.g., feature/ENG-123-description)
BRANCH_NAME="${{ github.head_ref }}"
ISSUE_ID=$(echo "$BRANCH_NAME" | grep -oE '[A-Z]+-[0-9]+' | head -1)
echo "issue_id=$ISSUE_ID" >> $GITHUB_OUTPUT
- name: Update Linear Issue
if: steps.extract.outputs.issue_id
run: |
npx ts-node scripts/update-linear-from-pr.ts \
--issue "${{ steps.extract.outputs.issue_id }}" \
--pr "${{ github.event.pull_request.number }}" \
--action "${{ github.event.action }}"
env:
LINEAR_API_KEY: ${{ secrets.LINEAR_API_KEY }}
Step 5: Update Script
import { LinearClient } from "@linear/sdk";
import { parseArgs } from "util";
const { values } = parseArgs({
options: {
issue: { type: "string" },
pr: { type: "string" },
action: { type: "string" },
},
});
async function main() {
const client = new LinearClient({
apiKey: process.env.LINEAR_API_KEY!,
});
const issue = await client.issue(values.issue!);
await client.createComment({
issueId: issue.id,
body: `PR #${values.pr} ${values.action}: https://github.com/${process.env.GITHUB_REPOSITORY}/pull/${values.pr}`,
});
if (values.action === "opened") {
const team = issue.;
states = team?.();
reviewState = states?..(
s..().()
);
(reviewState) {
client.(issue., { : reviewState. });
}
} (values. === || values. === ) {
team = issue.;
states = team?.();
doneState = states?..( s. === );
(doneState) {
client.(issue., { : doneState. });
}
}
}
().(.);
Step 6: Create Issues from CI Failures
name: Create Linear Issue on Test Failure
on:
workflow_run:
workflows: ["CI"]
types: [completed]
jobs:
create-issue:
if: ${{ github.event.workflow_run.conclusion == 'failure' }}
runs-on: ubuntu-latest
steps:
- name: Create Linear Issue
run: |
curl -X POST https://api.linear.app/graphql \
-H "Authorization: ${{ secrets.LINEAR_API_KEY }}" \
-H "Content-Type: application/json" \
-d '{
"query": "mutation { issueCreate(input: { teamId: \"${{ vars.LINEAR_TEAM_ID }}\", title: \"[CI] Build failure: ${{ github.event.workflow_run.head_branch }}\", description: \"Build failed: ${{ github.event.workflow_run.html_url }}\", priority: 1 }) { success } }"
}'
Output
- Automated test pipeline
- PR-to-issue linking
- Automatic state transitions
- Failure issue creation
- Test result artifacts
Error Handling
| Error | Cause | Solution |
|---|
Secret not found | Missing GitHub secret | Add LINEAR_API_KEY to repository secrets |
Issue not found | Invalid issue identifier | Verify branch naming convention |
Permission denied | Insufficient API key scope | Regenerate API key with write access |
Resources
Next Steps
Configure deployment integration with linear-deploy-integration.