semantic-release-linear-app 0.3.0-next.2 โ†’ 0.3.0-next.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,262 +1,61 @@
1
- # semantic-release-linear
1
+ # semantic-release-linear-app
2
2
 
3
- > A [semantic-release](https://github.com/semantic-release/semantic-release) plugin that updates Linear issues with version labels based on branch names.
4
-
5
- ## Features
6
-
7
- - ๐ŸŒฟ Extracts Linear issue IDs from branch names
8
- - ๐Ÿท๏ธ Automatically adds version labels to Linear issues
9
- - ๐ŸŽจ Color-coded labels based on release type
10
- - ๐Ÿงน Removes old version labels (configurable)
11
- - ๐Ÿ’ฌ Adds release comments to issues (optional)
12
- - โšก Batch operations for efficiency
13
- - ๐Ÿ“ Full TypeScript support
3
+ A [semantic-release](https://github.com/semantic-release/semantic-release) plugin that adds version labels to Linear issues based on branch names.
14
4
 
15
5
  ## Install
16
6
 
17
7
  ```bash
18
- npm install --save-dev semantic-release-linear
8
+ npm install --save-dev semantic-release-linear-app
19
9
  ```
20
10
 
21
- ## Quick Start
11
+ ## Setup
22
12
 
23
- 1. **Configure semantic-release** (`.releaserc.json`):
13
+ 1. Add to your `.releaserc.json`:
24
14
 
25
15
  ```json
26
16
  {
27
17
  "plugins": [
28
18
  "@semantic-release/commit-analyzer",
29
19
  "@semantic-release/release-notes-generator",
30
- ["semantic-release-linear", {
31
- "teamKeys": ["ENG", "FEAT", "BUG"]
20
+ ["semantic-release-linear-app", {
21
+ "teamKeys": ["ENG", "FEAT"]
32
22
  }],
33
23
  "@semantic-release/github"
34
24
  ]
35
25
  }
36
26
  ```
37
27
 
38
- 2. **Set your Linear API key**:
39
-
40
- ```bash
41
- export LINEAR_API_KEY=lin_api_xxx
42
- ```
28
+ 2. Set `LINEAR_API_KEY` environment variable (create at Linear Settings > API)
43
29
 
44
- 3. **Use proper branch names**:
30
+ 3. Use branch names with Linear issue IDs:
45
31
 
46
- ```bash
47
- git checkout -b feature/ENG-123-add-authentication
48
- git checkout -b bugfix/FEAT-456-fix-validation
49
- git checkout -b ENG-789-quick-fix
32
+ ```
33
+ feature/ENG-123-add-auth
34
+ bugfix/FEAT-456-fix-bug
35
+ ENG-789
50
36
  ```
51
37
 
52
- That's it! When semantic-release creates a release from these branches, the corresponding Linear issues will be labeled with the version number.
53
-
54
- ## Configuration
55
-
56
- ### Options
38
+ ## Options
57
39
 
58
40
  | Option | Default | Description |
59
41
  |--------|---------|-------------|
60
- | `apiKey` | - | Linear API key (or use `LINEAR_API_KEY` env var) |
61
- | `teamKeys` | `[]` | Team keys to filter (e.g., `["ENG", "FEAT"]`) |
42
+ | `teamKeys` | `[]` | Team keys to filter (e.g., `["ENG"]`) |
62
43
  | `labelPrefix` | `"v"` | Prefix for version labels |
63
44
  | `removeOldLabels` | `true` | Remove previous version labels |
64
- | `addComment` | `false` | Add a release comment to issues |
65
- | `skipBranches` | `["main", "master", "develop", "staging", "production"]` | Branches to skip unless they contain issues |
66
- | `requireIssueInBranch` | `true` | Only process branches with Linear issues |
45
+ | `addComment` | `false` | Add release comment to issues |
67
46
  | `dryRun` | `false` | Preview without making changes |
68
47
 
69
- ### TypeScript Configuration
70
-
71
- ```typescript
72
- // .releaserc.ts
73
- import type { PluginConfig } from 'semantic-release-linear';
74
-
75
- const config: PluginConfig = {
76
- teamKeys: ['ENG', 'FEAT'],
77
- labelPrefix: 'release-',
78
- addComment: true,
79
- skipBranches: ['main', 'develop']
80
- };
81
-
82
- export default {
83
- plugins: [
84
- '@semantic-release/commit-analyzer',
85
- '@semantic-release/release-notes-generator',
86
- ['semantic-release-linear', config],
87
- '@semantic-release/github'
88
- ]
89
- };
90
- ```
91
-
92
- ## Branch Naming Convention
93
-
94
- ### Recommended Patterns
95
-
96
- ```bash
97
- # Feature branches
98
- feature/ENG-123-user-authentication
99
- feature/FEAT-456-payment-integration
100
-
101
- # Bug fixes
102
- bugfix/BUG-789-login-error
103
- fix/ENG-101-memory-leak
104
-
105
- # Hotfixes
106
- hotfix/ENG-102-critical-security-patch
107
-
108
- # Simple format (still works)
109
- ENG-103-quick-update
110
- FEAT-104
111
- ```
112
-
113
- ### Multiple Issues in One Branch
114
-
115
- If you need to update multiple issues, include them all in the branch name:
116
-
117
- ```bash
118
- feature/ENG-123-FEAT-456-combined-update
119
- ```
120
-
121
- ### Enforce Branch Naming
122
-
123
- Use Git hooks or CI checks to enforce the naming convention:
124
-
125
- ```bash
126
- #!/bin/bash
127
- # .git/hooks/pre-push or CI script
128
-
129
- branch=$(git rev-parse --abbrev-ref HEAD)
130
- pattern="^(feature|bugfix|fix|hotfix)/[A-Z]+-[0-9]+|^[A-Z]+-[0-9]+"
131
-
132
- if ! [[ "$branch" =~ $pattern ]]; then
133
- echo "โŒ Branch name must include a Linear issue ID"
134
- echo " Example: feature/ENG-123-description"
135
- exit 1
136
- fi
137
- ```
138
-
139
- ## How It Works
140
-
141
- 1. **Branch Detection**: When semantic-release runs, it reads the current branch name
142
- 2. **Issue Extraction**: Extracts Linear issue IDs (e.g., `ENG-123`) from the branch
143
- 3. **Label Creation**: Creates a version label if needed (e.g., `v1.2.3`)
144
- 4. **Issue Update**: Applies the label to the Linear issue(s)
145
- 5. **Cleanup**: Optionally removes old version labels
146
-
147
- ### Label Colors
48
+ ## Labels
148
49
 
149
- Labels are color-coded by release type:
50
+ Labels are created based on version and channel:
150
51
 
151
- - ๐Ÿ”ด **Major** (breaking changes) - Red
152
- - ๐ŸŸ  **Minor** (new features) - Orange
153
- - ๐ŸŸข **Patch** (bug fixes) - Green
154
- - ๐ŸŸฃ **Prerelease** - Purple
52
+ - Default channel: `v1.2.3`
53
+ - Beta/next channels: `v1.2.3-beta`, `v1.2.3-next`
155
54
 
156
- ## Dry Run Mode
157
-
158
- Test what will happen without making changes:
159
-
160
- ```javascript
161
- {
162
- "plugins": [
163
- ["semantic-release-linear", {
164
- "dryRun": true
165
- }]
166
- ]
167
- }
168
- ```
169
-
170
- Output:
171
- ```
172
- [semantic-release] [semantic-release-linear] Found 1 Linear issue(s) in branch "feature/ENG-123-auth": ENG-123
173
- [semantic-release] [semantic-release-linear] [Dry run] Would update issues: ["ENG-123"]
174
- [semantic-release] [semantic-release-linear] [Dry run] Would apply label: v1.2.3
175
- ```
176
-
177
- ## Linear API Setup
178
-
179
- 1. Go to **Linear Settings** โ†’ **API** โ†’ **Personal API keys**
180
- 2. Create a new key with **write** access
181
- 3. Add to your CI environment as `LINEAR_API_KEY`
182
-
183
- ## CI Examples
184
-
185
- ### GitHub Actions
186
-
187
- ```yaml
188
- name: Release
189
-
190
- on:
191
- push:
192
- branches: [main, next]
193
-
194
- jobs:
195
- release:
196
- runs-on: ubuntu-latest
197
- steps:
198
- - uses: actions/checkout@v3
199
-
200
- - name: Setup Node.js
201
- uses: actions/setup-node@v3
202
- with:
203
- node-version: 18
204
-
205
- - name: Install dependencies
206
- run: npm ci
207
-
208
- - name: Release
209
- env:
210
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
211
- NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
212
- LINEAR_API_KEY: ${{ secrets.LINEAR_API_KEY }}
213
- run: npx semantic-release
214
- ```
215
-
216
- ## FAQ
217
-
218
- ### Why only branch names?
219
-
220
- Extracting from commits is unreliable - developers forget, commits get squashed, and there's no enforcement. Branch names are:
221
- - Required for every PR
222
- - Easily enforced via Git hooks
223
- - Visible in PR lists
224
- - A single source of truth
225
-
226
- ### What about releases from main/master?
227
-
228
- You have three options:
229
-
230
- 1. **Skip them** (default): Set `requireIssueInBranch: true`
231
- 2. **Tag main with an issue**: `git checkout main-ENG-123`
232
- 3. **Allow all branches**: Set `requireIssueInBranch: false`
233
-
234
- ### Can I update multiple issues?
235
-
236
- Yes, include multiple issue IDs in your branch name:
237
- ```bash
238
- feature/ENG-123-FEAT-456-big-feature
239
- ```
240
-
241
- ### What if my branch doesn't have an issue?
242
-
243
- The plugin will skip it (unless `requireIssueInBranch: false`). This is intentional - not every release needs to update Linear.
244
-
245
- ## Troubleshooting
246
-
247
- ### Issues not being detected
248
-
249
- 1. Check branch name: `git rev-parse --abbrev-ref HEAD`
250
- 2. Verify format matches: `TEAM-NUMBER`
251
- 3. Check team keys filter if configured
252
- 4. Run with `dryRun: true` to debug
253
-
254
- ### API errors
255
-
256
- - Verify API key has write access
257
- - Check Linear workspace permissions
258
- - Ensure network connectivity in CI
55
+ Colors indicate release type: red (major), orange (minor), green (patch), purple (prerelease).
259
56
 
260
57
  ## License
261
58
 
262
- MIT
59
+ MIT
60
+
61
+ <!-- SD-1135 e2e test v2 -->
@@ -1,4 +1,4 @@
1
- import { LinearIssue, LinearLabel, LinearViewer } from "../types";
1
+ import { LinearIssue, LinearLabel, LinearViewer } from '../types';
2
2
  /**
3
3
  * Linear API client for GraphQL operations
4
4
  */
@@ -10,7 +10,7 @@ const node_fetch_1 = __importDefault(require("node-fetch"));
10
10
  */
11
11
  class LinearClient {
12
12
  apiKey;
13
- apiUrl = "https://api.linear.app/graphql";
13
+ apiUrl = 'https://api.linear.app/graphql';
14
14
  constructor(apiKey) {
15
15
  this.apiKey = apiKey;
16
16
  }
@@ -19,10 +19,10 @@ class LinearClient {
19
19
  */
20
20
  async query(query, variables = {}) {
21
21
  const response = await (0, node_fetch_1.default)(this.apiUrl, {
22
- method: "POST",
22
+ method: 'POST',
23
23
  headers: {
24
24
  Authorization: this.apiKey,
25
- "Content-Type": "application/json",
25
+ 'Content-Type': 'application/json',
26
26
  },
27
27
  body: JSON.stringify({ query, variables }),
28
28
  });
@@ -31,7 +31,7 @@ class LinearClient {
31
31
  throw new Error(`Linear API error: ${data.errors[0].message}`);
32
32
  }
33
33
  if (!data.data) {
34
- throw new Error("No data returned from Linear API");
34
+ throw new Error('No data returned from Linear API');
35
35
  }
36
36
  return data.data;
37
37
  }
@@ -53,7 +53,7 @@ class LinearClient {
53
53
  /**
54
54
  * Find or create a label
55
55
  */
56
- async ensureLabel(name, color = "#4752C4") {
56
+ async ensureLabel(name, color = '#4752C4') {
57
57
  // First, try to find existing label
58
58
  const searchQuery = `
59
59
  query FindLabel($name: String!) {
@@ -9,10 +9,3 @@
9
9
  * @returns Array of unique issue identifiers
10
10
  */
11
11
  export declare function parseIssuesFromBranch(branchName: string, teamKeys?: string[] | null): string[];
12
- /**
13
- * Check if branch should be processed for Linear updates
14
- * @param branchName - The branch name to check
15
- * @param skipBranches - Branches to skip (default: main, master, develop without issue IDs)
16
- * @returns true if branch should be processed
17
- */
18
- export declare function shouldProcessBranch(branchName: string, skipBranches?: string[]): boolean;
@@ -5,7 +5,6 @@
5
5
  */
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
7
  exports.parseIssuesFromBranch = parseIssuesFromBranch;
8
- exports.shouldProcessBranch = shouldProcessBranch;
9
8
  /**
10
9
  * Extract Linear issue IDs from a branch name
11
10
  * @param branchName - The branch name to parse
@@ -15,34 +14,12 @@ exports.shouldProcessBranch = shouldProcessBranch;
15
14
  function parseIssuesFromBranch(branchName, teamKeys = null) {
16
15
  const issues = new Set();
17
16
  // Build regex pattern based on team keys
18
- const teamPattern = teamKeys ? `(?:${teamKeys.join("|")})` : "[A-Z]+";
17
+ const teamPattern = teamKeys ? `(?:${teamKeys.join('|')})` : '[A-Z]+';
19
18
  // Pattern matches: feature/ENG-123-description, ENG-123, bugfix/FEAT-45, etc.
20
- const issuePattern = new RegExp(`\\b(${teamPattern}-\\d+)\\b`, "gi");
19
+ const issuePattern = new RegExp(`\\b(${teamPattern}-\\d+)\\b`, 'gi');
21
20
  const matches = Array.from(branchName.matchAll(issuePattern));
22
21
  for (const match of matches) {
23
22
  issues.add(match[1].toUpperCase());
24
23
  }
25
24
  return Array.from(issues);
26
25
  }
27
- /**
28
- * Check if branch should be processed for Linear updates
29
- * @param branchName - The branch name to check
30
- * @param skipBranches - Branches to skip (default: main, master, develop without issue IDs)
31
- * @returns true if branch should be processed
32
- */
33
- function shouldProcessBranch(branchName, skipBranches = [
34
- "main",
35
- "master",
36
- "develop",
37
- "staging",
38
- "production",
39
- ]) {
40
- // Skip certain branches UNLESS they contain an issue ID
41
- if (skipBranches.includes(branchName)) {
42
- // These branches are OK if they contain an issue ID
43
- const hasIssue = /[A-Z]+-\d+/.test(branchName);
44
- return hasIssue;
45
- }
46
- // Process any other branch that contains an issue ID
47
- return /[A-Z]+-\d+/.test(branchName);
48
- }
@@ -1,39 +1,26 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const parse_issues_1 = require("./parse-issues");
4
- describe("parse-issues", () => {
5
- test("extracts Linear issue IDs from branch name", () => {
6
- const branchName = "feature/ENG-123-add-new-feature";
4
+ describe('parse-issues', () => {
5
+ test('extracts Linear issue IDs from branch name', () => {
6
+ const branchName = 'feature/ENG-123-add-new-feature';
7
7
  const result = (0, parse_issues_1.parseIssuesFromBranch)(branchName);
8
- expect(result).toEqual(["ENG-123"]);
8
+ expect(result).toEqual(['ENG-123']);
9
9
  });
10
- test("extracts multiple issue IDs from branch name", () => {
11
- const branchName = "fix/ENG-123-FEAT-456-bug-fix";
10
+ test('extracts multiple issue IDs from branch name', () => {
11
+ const branchName = 'fix/ENG-123-FEAT-456-bug-fix';
12
12
  const result = (0, parse_issues_1.parseIssuesFromBranch)(branchName);
13
- expect(result).toEqual(expect.arrayContaining(["ENG-123", "FEAT-456"]));
13
+ expect(result).toEqual(expect.arrayContaining(['ENG-123', 'FEAT-456']));
14
14
  expect(result).toHaveLength(2);
15
15
  });
16
- test("filters by team keys when provided", () => {
17
- const branchName = "feature/ENG-123-OTHER-456";
18
- const result = (0, parse_issues_1.parseIssuesFromBranch)(branchName, ["ENG"]);
19
- expect(result).toEqual(["ENG-123"]);
16
+ test('filters by team keys when provided', () => {
17
+ const branchName = 'feature/ENG-123-OTHER-456';
18
+ const result = (0, parse_issues_1.parseIssuesFromBranch)(branchName, ['ENG']);
19
+ expect(result).toEqual(['ENG-123']);
20
20
  });
21
- test("returns empty array for branch without issues", () => {
22
- const branchName = "feature/no-issues-here";
21
+ test('returns empty array for branch without issues', () => {
22
+ const branchName = 'feature/no-issues-here';
23
23
  const result = (0, parse_issues_1.parseIssuesFromBranch)(branchName);
24
24
  expect(result).toEqual([]);
25
25
  });
26
- test("shouldProcessBranch returns false for skip branches without issues", () => {
27
- expect((0, parse_issues_1.shouldProcessBranch)("main", ["main", "master"])).toBe(false);
28
- expect((0, parse_issues_1.shouldProcessBranch)("master", ["main", "master"])).toBe(false);
29
- });
30
- test("shouldProcessBranch returns true for skip branches with issues", () => {
31
- expect((0, parse_issues_1.shouldProcessBranch)("main-ENG-123", ["main", "master"])).toBe(true);
32
- });
33
- test("shouldProcessBranch returns true for feature branches with issues", () => {
34
- expect((0, parse_issues_1.shouldProcessBranch)("feature/ENG-123", ["main", "master"])).toBe(true);
35
- });
36
- test("shouldProcessBranch returns false for feature branches without issues", () => {
37
- expect((0, parse_issues_1.shouldProcessBranch)("feature/no-issues", ["main", "master"])).toBe(false);
38
- });
39
26
  });
@@ -1,5 +1,5 @@
1
- import { SuccessContext } from "semantic-release";
2
- import { PluginConfig, LinearContext } from "../types";
1
+ import { SuccessContext } from 'semantic-release';
2
+ import { PluginConfig, LinearContext } from '../types';
3
3
  interface ExtendedContext extends SuccessContext {
4
4
  linear?: LinearContext;
5
5
  }
@@ -9,31 +9,33 @@ const parse_issues_1 = require("./parse-issues");
9
9
  */
10
10
  async function findSourceBranches(commits, logger) {
11
11
  const branches = new Set();
12
+ const skipBranches = ['main', 'master', 'develop', 'stable', 'HEAD'];
12
13
  if (commits.length === 0)
13
14
  return branches;
14
- try {
15
- // Get all branches that contain these commits
16
- const { stdout } = await (0, execa_1.execa)("git", [
17
- "branch",
18
- "-r",
19
- "--contains",
20
- commits[0].hash,
21
- "--merged",
22
- ]);
23
- // Parse remote branch names
24
- const branchLines = stdout.split("\n").map((b) => b.trim());
25
- for (const line of branchLines) {
26
- // Extract branch name from origin/feature/ENG-123-description
27
- const match = line.match(/origin\/(.+)/);
28
- if (match &&
29
- !["main", "master", "develop", "stable", "HEAD"].includes(match[1])) {
30
- branches.add(match[1]);
31
- logger.log(`Found source branch: ${match[1]}`);
15
+ // Check all commits to find all source branches
16
+ for (const commit of commits) {
17
+ try {
18
+ const { stdout } = await (0, execa_1.execa)('git', [
19
+ 'branch',
20
+ '-r',
21
+ '--contains',
22
+ commit.hash,
23
+ '--merged',
24
+ ]);
25
+ const branchLines = stdout.split('\n').map((b) => b.trim());
26
+ for (const line of branchLines) {
27
+ const match = line.match(/origin\/(.+)/);
28
+ if (match && !skipBranches.includes(match[1])) {
29
+ branches.add(match[1]);
30
+ }
32
31
  }
33
32
  }
33
+ catch {
34
+ // Commit might not exist or other git error, continue with next commit
35
+ }
34
36
  }
35
- catch (error) {
36
- logger.debug(`Git branch lookup failed: ${error.message}`);
37
+ if (branches.size > 0) {
38
+ logger.log(`Found source branches: ${Array.from(branches).join(', ')}`);
37
39
  }
38
40
  return branches;
39
41
  }
@@ -43,18 +45,18 @@ async function findSourceBranches(commits, logger) {
43
45
  async function success(pluginConfig, context) {
44
46
  const { logger, nextRelease, linear, commits } = context;
45
47
  if (!linear) {
46
- logger.log("Linear context not found, skipping issue updates");
48
+ logger.log('Linear context not found, skipping issue updates');
47
49
  return;
48
50
  }
49
51
  if (!commits || commits.length === 0) {
50
- logger.log("No commits found in release, skipping Linear updates");
52
+ logger.log('No commits found in release, skipping Linear updates');
51
53
  return;
52
54
  }
53
- const { removeOldLabels = true, addComment = false, dryRun = false, } = pluginConfig;
55
+ const { removeOldLabels = true, addComment = false, dryRun = false } = pluginConfig;
54
56
  // Find all branches that contributed to this release
55
57
  const sourceBranches = await findSourceBranches(commits, logger);
56
58
  if (sourceBranches.size === 0) {
57
- logger.log("No source branches found, skipping Linear updates");
59
+ logger.log('No source branches found, skipping Linear updates');
58
60
  return;
59
61
  }
60
62
  // Extract Linear issue IDs from all found branches
@@ -64,21 +66,24 @@ async function success(pluginConfig, context) {
64
66
  branchIssues.forEach((id) => issueIds.add(id));
65
67
  }
66
68
  if (issueIds.size === 0) {
67
- logger.log(`No Linear issues found in branches: ${Array.from(sourceBranches).join(", ")}`);
69
+ logger.log(`No Linear issues found in branches: ${Array.from(sourceBranches).join(', ')}`);
68
70
  return;
69
71
  }
70
- logger.log(`Found ${issueIds.size} Linear issue(s): ${Array.from(issueIds).join(", ")} ` +
72
+ logger.log(`Found ${issueIds.size} Linear issue(s): ${Array.from(issueIds).join(', ')} ` +
71
73
  `from ${sourceBranches.size} branch(es)`);
74
+ // Build label name with optional channel suffix
75
+ const version = nextRelease.version;
76
+ const channel = nextRelease.channel;
77
+ const labelName = channel
78
+ ? `${linear.labelPrefix}${version}-${channel}`
79
+ : `${linear.labelPrefix}${version}`;
72
80
  if (dryRun) {
73
- logger.log("[Dry run] Would update issues:", Array.from(issueIds));
74
- logger.log(`[Dry run] Would apply label: ${linear.labelPrefix}${nextRelease.version}`);
81
+ logger.log('[Dry run] Would update issues:', Array.from(issueIds));
82
+ logger.log(`[Dry run] Would apply label: ${labelName}`);
75
83
  return;
76
84
  }
77
85
  // Initialize Linear client and prepare label
78
86
  const client = new linear_client_1.LinearClient(linear.apiKey);
79
- const version = nextRelease.version;
80
- const channel = nextRelease.channel || "latest";
81
- const labelName = `${linear.labelPrefix}${version}`;
82
87
  const labelColor = getLabelColor(nextRelease.type);
83
88
  // Ensure the version label exists
84
89
  const label = await client.ensureLabel(labelName, labelColor);
@@ -89,7 +94,7 @@ async function success(pluginConfig, context) {
89
94
  const issue = await client.getIssue(issueId);
90
95
  if (!issue) {
91
96
  logger.warn(`Issue ${issueId} not found in Linear`);
92
- return { issueId, status: "not_found" };
97
+ return { issueId, status: 'not_found' };
93
98
  }
94
99
  // Remove old version labels if configured
95
100
  if (removeOldLabels) {
@@ -99,24 +104,24 @@ async function success(pluginConfig, context) {
99
104
  await client.addLabelToIssue(issue.id, label.id);
100
105
  // Add comment if configured
101
106
  if (addComment) {
102
- const emoji = channel === "latest" ? "๐Ÿš€" : "๐Ÿ”ฌ";
103
- const channelText = channel === "latest" ? "" : ` (${channel} channel)`;
107
+ const emoji = channel ? '๐Ÿ”ฌ' : '๐Ÿš€';
108
+ const channelText = channel ? ` (${channel} channel)` : '';
104
109
  const comment = `${emoji} Released in version ${version}${channelText}`;
105
110
  await client.addComment(issue.id, comment);
106
111
  }
107
112
  logger.log(`โœ“ Updated issue ${issueId}`);
108
- return { issueId, status: "updated" };
113
+ return { issueId, status: 'updated' };
109
114
  }
110
115
  catch (error) {
111
116
  const message = error instanceof Error ? error.message : String(error);
112
117
  logger.error(`Failed to update issue ${issueId}: ${message}`);
113
- return { issueId, status: "failed", error: message };
118
+ return { issueId, status: 'failed', error: message };
114
119
  }
115
120
  }));
116
121
  // Log summary
117
- const updated = results.filter((r) => r.status === "fulfilled" && r.value?.status === "updated").length;
118
- const failed = results.filter((r) => r.status === "rejected" || r.value?.status === "failed").length;
119
- const notFound = results.filter((r) => r.status === "fulfilled" && r.value?.status === "not_found").length;
122
+ const updated = results.filter((r) => r.status === 'fulfilled' && r.value?.status === 'updated').length;
123
+ const failed = results.filter((r) => r.status === 'rejected' || r.value?.status === 'failed').length;
124
+ const notFound = results.filter((r) => r.status === 'fulfilled' && r.value?.status === 'not_found').length;
120
125
  logger.log(`Linear update complete: ${updated} updated, ${failed} failed, ${notFound} not found`);
121
126
  }
122
127
  /**
@@ -124,13 +129,13 @@ async function success(pluginConfig, context) {
124
129
  */
125
130
  function getLabelColor(releaseType) {
126
131
  const colors = {
127
- major: "#F44336", // Red for breaking changes
128
- premajor: "#E91E63", // Pink for pre-major
129
- minor: "#FF9800", // Orange for new features
130
- preminor: "#FFC107", // Amber for pre-minor
131
- patch: "#4CAF50", // Green for fixes
132
- prepatch: "#8BC34A", // Light green for pre-patch
133
- prerelease: "#9C27B0", // Purple for prereleases
132
+ major: '#F44336', // Red for breaking changes
133
+ premajor: '#E91E63', // Pink for pre-major
134
+ minor: '#FF9800', // Orange for new features
135
+ preminor: '#FFC107', // Amber for pre-minor
136
+ patch: '#4CAF50', // Green for fixes
137
+ prepatch: '#8BC34A', // Light green for pre-patch
138
+ prerelease: '#9C27B0', // Purple for prereleases
134
139
  };
135
- return colors[releaseType] || "#4752C4"; // Default blue
140
+ return colors[releaseType] || '#4752C4'; // Default blue
136
141
  }
@@ -1,5 +1,5 @@
1
- import { VerifyConditionsContext } from "semantic-release";
2
- import { PluginConfig, LinearContext } from "../types";
1
+ import { VerifyConditionsContext } from 'semantic-release';
2
+ import { PluginConfig, LinearContext } from '../types';
3
3
  /**
4
4
  * Verify the plugin configuration and Linear API access
5
5
  */