linear-sdk-cli 0.8.3 → 0.9.0
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/CHANGELOG.md +7 -0
- package/COVERAGE.md +26 -25
- package/README.md +163 -2
- package/package.json +1 -1
- package/skills/linear-sdk-cli/SKILL.md +98 -0
- package/skills/linear-sdk-cli/references/auth.md +31 -0
- package/skills/linear-sdk-cli/references/config.md +13 -9
- package/skills/linear-sdk-cli/references/issue.md +3 -2
- package/skills/linear-sdk-cli/references/release-note.md +148 -0
- package/skills/linear-sdk-cli/references/release-pipeline.md +137 -0
- package/skills/linear-sdk-cli/references/release-stage.md +121 -0
- package/skills/linear-sdk-cli/references/release.md +348 -0
- package/src/cli.ts +8 -0
- package/src/commands/issue.ts +9 -3
- package/src/commands/meta.ts +82 -2
- package/src/commands/release-note.ts +90 -0
- package/src/commands/release-pipeline.ts +122 -0
- package/src/commands/release-stage.ts +100 -0
- package/src/commands/release.ts +254 -0
- package/src/config.ts +254 -6
- package/src/lib/keyring.ts +28 -0
- package/src/lib/options.ts +18 -0
- package/src/lib/output-shapes.ts +68 -1
- package/src/lib/release-input.ts +138 -0
- package/src/services/issue.ts +12 -3
- package/src/services/release.ts +691 -0
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
# linear release
|
|
2
|
+
|
|
3
|
+
> Track releases and issue membership (Business/Enterprise)
|
|
4
|
+
|
|
5
|
+
_Generated from `linear commands --json`. `linear release --help` (or `<subcommand> --help`) is authoritative._
|
|
6
|
+
|
|
7
|
+
Every command also accepts the global flags `-j/--json`, `--no-input`, `-y/--yes`, `-q/--quiet`, `--workspace <slug>`, `--api-key <key>`, `--access-token <token>`, `-t/--team <key>`, `-n/--limit <n>`, `--all`, `-f/--fields <a,b,c>`, `--no-ansi` (alias `--no-color`), and `--debug`. Only command-specific options are listed below.
|
|
8
|
+
|
|
9
|
+
### `linear release`
|
|
10
|
+
|
|
11
|
+
Track releases and issue membership (Business/Enterprise)
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
linear release [options]
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### `linear release add-issue`
|
|
18
|
+
|
|
19
|
+
Add an issue (KEY-123 or UUID) to an exact release
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
linear release add-issue [options] <id> <issue>
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
**Output (`--json`)**: a receipt object
|
|
26
|
+
|
|
27
|
+
```text
|
|
28
|
+
id: string
|
|
29
|
+
releaseId: string
|
|
30
|
+
issueId: string
|
|
31
|
+
identifier: string
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### `linear release complete`
|
|
35
|
+
|
|
36
|
+
Complete an explicit scheduled release; may roll open issues forward
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
linear release complete [options]
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
| Option | Description |
|
|
43
|
+
| ----------------------------- | -------------------------------------------------------------------- |
|
|
44
|
+
| `--description <text>` | release description (Markdown) |
|
|
45
|
+
| `--description-file <path>` | read description from file ('-' for stdin) |
|
|
46
|
+
| `--clear-description` | clear the description |
|
|
47
|
+
| `--pipeline <id\|slug>` | target pipeline UUID or slug |
|
|
48
|
+
| `--release-version <version>` | explicit release version within the pipeline (no latest fallback) |
|
|
49
|
+
| `--name <name>` | release name |
|
|
50
|
+
| `--link <label=url\|url>` | attach an external link (repeatable) |
|
|
51
|
+
| `--commit-sha <sha>` | full SHA (Linear preserves an existing SHA when targeting a version) |
|
|
52
|
+
|
|
53
|
+
**Output (`--json`)**: a receipt object
|
|
54
|
+
|
|
55
|
+
```text
|
|
56
|
+
id: string
|
|
57
|
+
name: string
|
|
58
|
+
version: string | null
|
|
59
|
+
commitSha: string | null
|
|
60
|
+
url: string
|
|
61
|
+
pipeline: {id: string, name: string, type: string}
|
|
62
|
+
stage: {id: string, name: string, type: string, frozen: boolean}
|
|
63
|
+
startDate: string | null
|
|
64
|
+
targetDate: string | null
|
|
65
|
+
startedAt: string | null
|
|
66
|
+
completedAt: string | null
|
|
67
|
+
canceledAt: string | null
|
|
68
|
+
archivedAt: string | null
|
|
69
|
+
description: string | null
|
|
70
|
+
issueCount: number
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### `linear release create`
|
|
74
|
+
|
|
75
|
+
Create a release (stage defaults depend on pipeline type)
|
|
76
|
+
|
|
77
|
+
Aliases: `new`
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
linear release create [options]
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
| Option | Description |
|
|
84
|
+
| ----------------------------- | -------------------------------------------- |
|
|
85
|
+
| `--description <text>` | release description (Markdown) |
|
|
86
|
+
| `--description-file <path>` | read description from file ('-' for stdin) |
|
|
87
|
+
| `--clear-description` | clear the description |
|
|
88
|
+
| `--name <name>` | release name |
|
|
89
|
+
| `--release-version <version>` | release version (unique within the pipeline) |
|
|
90
|
+
| `--commit-sha <sha>` | full Git commit SHA |
|
|
91
|
+
| `--stage <id>` | stage UUID from the same pipeline |
|
|
92
|
+
| `--start-date <date>` | estimated start date (YYYY-MM-DD) |
|
|
93
|
+
| `--target-date <date>` | estimated completion date (YYYY-MM-DD) |
|
|
94
|
+
| `--pipeline <id\|slug>` | pipeline UUID or slug |
|
|
95
|
+
|
|
96
|
+
**Output (`--json`)**: a receipt object
|
|
97
|
+
|
|
98
|
+
```text
|
|
99
|
+
id: string
|
|
100
|
+
name: string
|
|
101
|
+
version: string | null
|
|
102
|
+
commitSha: string | null
|
|
103
|
+
url: string
|
|
104
|
+
pipeline: {id: string, name: string, type: string}
|
|
105
|
+
stage: {id: string, name: string, type: string, frozen: boolean}
|
|
106
|
+
startDate: string | null
|
|
107
|
+
targetDate: string | null
|
|
108
|
+
startedAt: string | null
|
|
109
|
+
completedAt: string | null
|
|
110
|
+
canceledAt: string | null
|
|
111
|
+
archivedAt: string | null
|
|
112
|
+
description: string | null
|
|
113
|
+
issueCount: number
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### `linear release issues`
|
|
117
|
+
|
|
118
|
+
List issues in a release (UUID or slug)
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
linear release issues [options] <id>
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
**Output (`--json`)**: a bare array of objects
|
|
125
|
+
|
|
126
|
+
```text
|
|
127
|
+
id: string
|
|
128
|
+
identifier: string
|
|
129
|
+
title: string
|
|
130
|
+
url: string
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### `linear release links`
|
|
134
|
+
|
|
135
|
+
List external links on a release (UUID or slug)
|
|
136
|
+
|
|
137
|
+
```
|
|
138
|
+
linear release links [options] <id>
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
**Output (`--json`)**: a bare array of objects
|
|
142
|
+
|
|
143
|
+
```text
|
|
144
|
+
id: string
|
|
145
|
+
label: string | null
|
|
146
|
+
url: string
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### `linear release list`
|
|
150
|
+
|
|
151
|
+
List releases, optionally scoped to a pipeline
|
|
152
|
+
|
|
153
|
+
Aliases: `ls`
|
|
154
|
+
|
|
155
|
+
```
|
|
156
|
+
linear release list [options]
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
| Option | Description |
|
|
160
|
+
| ----------------------------- | ------------------------------- |
|
|
161
|
+
| `--pipeline <id\|slug>` | filter by pipeline UUID or slug |
|
|
162
|
+
| `--release-version <version>` | filter by exact version |
|
|
163
|
+
| `--stage-type <type>` | filter by lifecycle stage type |
|
|
164
|
+
| `--include-archived` | include archived releases |
|
|
165
|
+
|
|
166
|
+
**Output (`--json`)**: a bare array of objects
|
|
167
|
+
|
|
168
|
+
```text
|
|
169
|
+
id: string
|
|
170
|
+
name: string
|
|
171
|
+
version: string | null
|
|
172
|
+
commitSha: string | null
|
|
173
|
+
url: string
|
|
174
|
+
pipeline: {id: string, name: string, type: string}
|
|
175
|
+
stage: {id: string, name: string, type: string, frozen: boolean}
|
|
176
|
+
startDate: string | null
|
|
177
|
+
targetDate: string | null
|
|
178
|
+
startedAt: string | null
|
|
179
|
+
completedAt: string | null
|
|
180
|
+
canceledAt: string | null
|
|
181
|
+
archivedAt: string | null
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### `linear release remove-issue`
|
|
185
|
+
|
|
186
|
+
Remove an issue from an exact release
|
|
187
|
+
|
|
188
|
+
```
|
|
189
|
+
linear release remove-issue [options] <id> <issue>
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
**Output (`--json`)**: a receipt object
|
|
193
|
+
|
|
194
|
+
```text
|
|
195
|
+
releaseId: string
|
|
196
|
+
issue: string
|
|
197
|
+
removed: boolean
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### `linear release sync`
|
|
201
|
+
|
|
202
|
+
Sync explicit issue references; continuous pipelines complete immediately
|
|
203
|
+
|
|
204
|
+
```
|
|
205
|
+
linear release sync [options]
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
| Option | Description |
|
|
209
|
+
| ------------------------------ | ----------------------------------------------------------------- |
|
|
210
|
+
| `--description <text>` | release description (Markdown) |
|
|
211
|
+
| `--description-file <path>` | read description from file ('-' for stdin) |
|
|
212
|
+
| `--clear-description` | clear the description |
|
|
213
|
+
| `--pipeline <id\|slug>` | target pipeline UUID or slug |
|
|
214
|
+
| `--release-version <version>` | explicit release version within the pipeline (no latest fallback) |
|
|
215
|
+
| `--name <name>` | release name |
|
|
216
|
+
| `--link <label=url\|url>` | attach an external link (repeatable) |
|
|
217
|
+
| `--commit-sha <sha>` | full Git commit SHA (no implicit checkout scanning) |
|
|
218
|
+
| `--issue <key[=sha]>` | issue reference, defaulting to --commit-sha (repeatable) |
|
|
219
|
+
| `--reverted-issue <key[=sha]>` | reverted issue reference to remove (repeatable) |
|
|
220
|
+
|
|
221
|
+
**Output (`--json`)**: a receipt object
|
|
222
|
+
|
|
223
|
+
```text
|
|
224
|
+
id: string
|
|
225
|
+
name: string
|
|
226
|
+
version: string | null
|
|
227
|
+
commitSha: string | null
|
|
228
|
+
url: string
|
|
229
|
+
pipeline: {id: string, name: string, type: string}
|
|
230
|
+
stage: {id: string, name: string, type: string, frozen: boolean}
|
|
231
|
+
startDate: string | null
|
|
232
|
+
targetDate: string | null
|
|
233
|
+
startedAt: string | null
|
|
234
|
+
completedAt: string | null
|
|
235
|
+
canceledAt: string | null
|
|
236
|
+
archivedAt: string | null
|
|
237
|
+
description: string | null
|
|
238
|
+
issueCount: number
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### `linear release update`
|
|
242
|
+
|
|
243
|
+
Update exact release metadata or stage by UUID/slug
|
|
244
|
+
|
|
245
|
+
Aliases: `edit`
|
|
246
|
+
|
|
247
|
+
```
|
|
248
|
+
linear release update [options] <id>
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
| Option | Description |
|
|
252
|
+
| ----------------------------- | -------------------------------------------- |
|
|
253
|
+
| `--description <text>` | release description (Markdown) |
|
|
254
|
+
| `--description-file <path>` | read description from file ('-' for stdin) |
|
|
255
|
+
| `--clear-description` | clear the description |
|
|
256
|
+
| `--name <name>` | release name |
|
|
257
|
+
| `--release-version <version>` | release version (unique within the pipeline) |
|
|
258
|
+
| `--commit-sha <sha>` | full Git commit SHA |
|
|
259
|
+
| `--stage <id>` | stage UUID from the same pipeline |
|
|
260
|
+
| `--start-date <date>` | estimated start date (YYYY-MM-DD) |
|
|
261
|
+
| `--target-date <date>` | estimated completion date (YYYY-MM-DD) |
|
|
262
|
+
|
|
263
|
+
**Output (`--json`)**: a receipt object
|
|
264
|
+
|
|
265
|
+
```text
|
|
266
|
+
id: string
|
|
267
|
+
name: string
|
|
268
|
+
version: string | null
|
|
269
|
+
commitSha: string | null
|
|
270
|
+
url: string
|
|
271
|
+
pipeline: {id: string, name: string, type: string}
|
|
272
|
+
stage: {id: string, name: string, type: string, frozen: boolean}
|
|
273
|
+
startDate: string | null
|
|
274
|
+
targetDate: string | null
|
|
275
|
+
startedAt: string | null
|
|
276
|
+
completedAt: string | null
|
|
277
|
+
canceledAt: string | null
|
|
278
|
+
archivedAt: string | null
|
|
279
|
+
description: string | null
|
|
280
|
+
issueCount: number
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
### `linear release update-version`
|
|
284
|
+
|
|
285
|
+
Update stage/name/links for an explicit pipeline and release version
|
|
286
|
+
|
|
287
|
+
```
|
|
288
|
+
linear release update-version [options]
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
| Option | Description |
|
|
292
|
+
| ----------------------------- | ----------------------------------------------------------------- |
|
|
293
|
+
| `--description <text>` | release description (Markdown) |
|
|
294
|
+
| `--description-file <path>` | read description from file ('-' for stdin) |
|
|
295
|
+
| `--clear-description` | clear the description |
|
|
296
|
+
| `--pipeline <id\|slug>` | target pipeline UUID or slug |
|
|
297
|
+
| `--release-version <version>` | explicit release version within the pipeline (no latest fallback) |
|
|
298
|
+
| `--name <name>` | release name |
|
|
299
|
+
| `--link <label=url\|url>` | attach an external link (repeatable) |
|
|
300
|
+
| `--stage <name>` | stage name (Linear matches exact name, then normalized name) |
|
|
301
|
+
|
|
302
|
+
**Output (`--json`)**: a receipt object
|
|
303
|
+
|
|
304
|
+
```text
|
|
305
|
+
id: string
|
|
306
|
+
name: string
|
|
307
|
+
version: string | null
|
|
308
|
+
commitSha: string | null
|
|
309
|
+
url: string
|
|
310
|
+
pipeline: {id: string, name: string, type: string}
|
|
311
|
+
stage: {id: string, name: string, type: string, frozen: boolean}
|
|
312
|
+
startDate: string | null
|
|
313
|
+
targetDate: string | null
|
|
314
|
+
startedAt: string | null
|
|
315
|
+
completedAt: string | null
|
|
316
|
+
canceledAt: string | null
|
|
317
|
+
archivedAt: string | null
|
|
318
|
+
description: string | null
|
|
319
|
+
issueCount: number
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
### `linear release view`
|
|
323
|
+
|
|
324
|
+
Show a release by UUID or slug
|
|
325
|
+
|
|
326
|
+
```
|
|
327
|
+
linear release view [options] <id>
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
**Output (`--json`)**: a bare object
|
|
331
|
+
|
|
332
|
+
```text
|
|
333
|
+
id: string
|
|
334
|
+
name: string
|
|
335
|
+
version: string | null
|
|
336
|
+
commitSha: string | null
|
|
337
|
+
url: string
|
|
338
|
+
pipeline: {id: string, name: string, type: string}
|
|
339
|
+
stage: {id: string, name: string, type: string, frozen: boolean}
|
|
340
|
+
startDate: string | null
|
|
341
|
+
targetDate: string | null
|
|
342
|
+
startedAt: string | null
|
|
343
|
+
completedAt: string | null
|
|
344
|
+
canceledAt: string | null
|
|
345
|
+
archivedAt: string | null
|
|
346
|
+
description: string | null
|
|
347
|
+
issueCount: number
|
|
348
|
+
```
|
package/src/cli.ts
CHANGED
|
@@ -19,6 +19,10 @@ import { registerProjectLabel } from "./commands/project-label.js";
|
|
|
19
19
|
import { registerProjectUpdate } from "./commands/project-update.js";
|
|
20
20
|
import { registerMilestone } from "./commands/milestone.js";
|
|
21
21
|
import { registerCycle } from "./commands/cycle.js";
|
|
22
|
+
import { registerRelease } from "./commands/release.js";
|
|
23
|
+
import { registerReleasePipeline } from "./commands/release-pipeline.js";
|
|
24
|
+
import { registerReleaseStage } from "./commands/release-stage.js";
|
|
25
|
+
import { registerReleaseNote } from "./commands/release-note.js";
|
|
22
26
|
import { registerUser } from "./commands/user.js";
|
|
23
27
|
import { registerLabel } from "./commands/label.js";
|
|
24
28
|
import { registerState } from "./commands/state.js";
|
|
@@ -94,6 +98,10 @@ export function createProgram(): Command {
|
|
|
94
98
|
registerProjectUpdate(program);
|
|
95
99
|
registerMilestone(program);
|
|
96
100
|
registerCycle(program);
|
|
101
|
+
registerRelease(program);
|
|
102
|
+
registerReleasePipeline(program);
|
|
103
|
+
registerReleaseStage(program);
|
|
104
|
+
registerReleaseNote(program);
|
|
97
105
|
// Phase 3: users, labels, workflow states, comments, documents, attachments, favorites.
|
|
98
106
|
registerUser(program);
|
|
99
107
|
registerLabel(program);
|
package/src/commands/issue.ts
CHANGED
|
@@ -371,8 +371,12 @@ export function registerIssue(program: Command): void {
|
|
|
371
371
|
.option("-s, --state <name>", "workflow state name or type")
|
|
372
372
|
.option("-P, --priority <0-4>", "priority", parsePriority)
|
|
373
373
|
.option("-l, --label <name>", "label (repeatable / comma-separated)", parseList)
|
|
374
|
-
.option("-p, --project <name>", "project name or id")
|
|
375
|
-
.option("--
|
|
374
|
+
.option("-p, --project <name>", "project name or id (overrides the configured default)")
|
|
375
|
+
.option("--no-project", "create outside any project, ignoring configured and parent defaults")
|
|
376
|
+
.option(
|
|
377
|
+
"--milestone <name>",
|
|
378
|
+
"project milestone (requires an explicit, configured, or parent project)",
|
|
379
|
+
)
|
|
376
380
|
.option(CYCLE_FLAG, CYCLE_DESC)
|
|
377
381
|
.option("--estimate <n>", "estimate points", parseIntOption)
|
|
378
382
|
.option(
|
|
@@ -391,6 +395,7 @@ export function registerIssue(program: Command): void {
|
|
|
391
395
|
[
|
|
392
396
|
"",
|
|
393
397
|
"Examples:",
|
|
398
|
+
" linear config set project 'My Project' # default for this folder/repo",
|
|
394
399
|
" linear issue create --title 'Fix login' --team TES --assignee me",
|
|
395
400
|
" linear issue create --title 'Fix login' --assignee me --delegate Codex",
|
|
396
401
|
" linear issue create --title 'Fix login' --delegate Codex --dry-run --json",
|
|
@@ -438,7 +443,8 @@ export function registerIssue(program: Command): void {
|
|
|
438
443
|
state: opts.state,
|
|
439
444
|
priority: opts.priority,
|
|
440
445
|
label: opts.label,
|
|
441
|
-
project: opts.project,
|
|
446
|
+
project: opts.project === false ? null : opts.project,
|
|
447
|
+
defaultProject: ctx.config.project,
|
|
442
448
|
milestone: opts.milestone,
|
|
443
449
|
cycle: opts.cycle,
|
|
444
450
|
estimate: opts.estimate,
|
package/src/commands/meta.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Top-level meta commands: whoami, auth (login/list/default/token/status/
|
|
3
|
-
* logout/migrate), config.
|
|
3
|
+
* logout/rename/migrate), config.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { Command } from "commander";
|
|
@@ -20,6 +20,8 @@ import {
|
|
|
20
20
|
readOAuthCredential,
|
|
21
21
|
writeOAuthCredential,
|
|
22
22
|
migrateCredentials,
|
|
23
|
+
renameApiKeyProfile,
|
|
24
|
+
profileRenameProjectUpdates,
|
|
23
25
|
redactKey,
|
|
24
26
|
resolveConfig,
|
|
25
27
|
defaultProjectConfigPath,
|
|
@@ -631,6 +633,80 @@ export function registerMeta(program: Command): void {
|
|
|
631
633
|
}),
|
|
632
634
|
);
|
|
633
635
|
|
|
636
|
+
// rename ------------------------------------------------------------------
|
|
637
|
+
auth
|
|
638
|
+
.command("rename <old-slug> <new-slug>")
|
|
639
|
+
.description("Rename a stored API-key profile after its Linear workspace URL slug changes")
|
|
640
|
+
.option(
|
|
641
|
+
"--project-config <paths...>",
|
|
642
|
+
"also inspect these project configs for old workspace selectors (read-only)",
|
|
643
|
+
)
|
|
644
|
+
.addHelpText(
|
|
645
|
+
"after",
|
|
646
|
+
`
|
|
647
|
+
Validates the exact stored API key against the current Linear workspace slug.
|
|
648
|
+
Preserves the key, profile team, user workspace/default, and shared keyring list.
|
|
649
|
+
Refuses configured or unlisted keyring destinations; never overwrites or revokes.
|
|
650
|
+
Keyring moves require atomic create-only support (macOS Keychain). Linux Secret
|
|
651
|
+
Service entries are refused; file-only API-key profiles are supported.
|
|
652
|
+
An interrupted rename can be resumed by repeating the same command.
|
|
653
|
+
OAuth user/app profiles are not supported; their credentials and grants stay unchanged.
|
|
654
|
+
Project files are never edited. Reports the discovered project config and any
|
|
655
|
+
--project-config paths needing updates; other repositories must be checked separately.
|
|
656
|
+
Example: linear auth rename old-workspace new-workspace --json`,
|
|
657
|
+
)
|
|
658
|
+
.action(
|
|
659
|
+
action(async (ctx: Context, opts, oldSlug: string, newSlug: string) => {
|
|
660
|
+
const from = explicitWorkspaceSlug(oldSlug);
|
|
661
|
+
const to = explicitWorkspaceSlug(newSlug);
|
|
662
|
+
if (ctx.options.workspace && ctx.options.workspace !== from)
|
|
663
|
+
throw usageError("--workspace must match <old-slug> when renaming a profile.");
|
|
664
|
+
if (ctx.options.apiKey || ctx.options.accessToken)
|
|
665
|
+
throw usageError(
|
|
666
|
+
"`auth rename` uses only the named stored profile; remove credential flags.",
|
|
667
|
+
);
|
|
668
|
+
const checkedProjectConfigs = [
|
|
669
|
+
...new Set([
|
|
670
|
+
...(ctx.config.projectConfigPath ? [ctx.config.projectConfigPath] : []),
|
|
671
|
+
...(opts.projectConfig ?? []).map((path: string) => resolve(path)),
|
|
672
|
+
]),
|
|
673
|
+
] as string[];
|
|
674
|
+
const projectConfigUpdates = profileRenameProjectUpdates(from, to, checkedProjectConfigs);
|
|
675
|
+
const globalConfigUpdates = profileRenameProjectUpdates(
|
|
676
|
+
from,
|
|
677
|
+
to,
|
|
678
|
+
ctx.config.globalConfigPath ? [ctx.config.globalConfigPath] : [],
|
|
679
|
+
);
|
|
680
|
+
let organization: AuthIdentity["organization"] | undefined;
|
|
681
|
+
const saved = await renameApiKeyProfile(from, to, async (key) => {
|
|
682
|
+
organization = (await validateAuthCredential(key, to)).organization;
|
|
683
|
+
});
|
|
684
|
+
const otherProjects = `Update workspace = "${from}" to "${to}" in other project configs and LINEAR_WORKSPACE overrides. Only the listed project paths were checked.`;
|
|
685
|
+
ctx.output.emit(
|
|
686
|
+
{
|
|
687
|
+
success: true,
|
|
688
|
+
from,
|
|
689
|
+
workspace: to,
|
|
690
|
+
credentialType: "api-key",
|
|
691
|
+
organization: organization!,
|
|
692
|
+
...saved,
|
|
693
|
+
checkedProjectConfigs,
|
|
694
|
+
projectConfigUpdates,
|
|
695
|
+
globalConfigUpdates,
|
|
696
|
+
otherProjects,
|
|
697
|
+
},
|
|
698
|
+
() => {
|
|
699
|
+
ctx.output.success(
|
|
700
|
+
`Renamed API-key profile '${from}' to '${to}'. Credential preserved; no grant revoked.`,
|
|
701
|
+
);
|
|
702
|
+
for (const update of [...projectConfigUpdates, ...globalConfigUpdates])
|
|
703
|
+
ctx.output.info(`Update ${update.path}: workspace = "${to}" (currently "${from}").`);
|
|
704
|
+
ctx.output.info(otherProjects);
|
|
705
|
+
},
|
|
706
|
+
);
|
|
707
|
+
}),
|
|
708
|
+
);
|
|
709
|
+
|
|
634
710
|
// migrate -----------------------------------------------------------------
|
|
635
711
|
auth
|
|
636
712
|
.command("migrate")
|
|
@@ -753,6 +829,7 @@ export function registerMeta(program: Command): void {
|
|
|
753
829
|
workspaceProfile: c.workspaceProfile ?? null,
|
|
754
830
|
team: c.team ?? null,
|
|
755
831
|
workspace: c.workspace ?? null,
|
|
832
|
+
project: c.project ?? null,
|
|
756
833
|
sort: c.sort,
|
|
757
834
|
origins: c.origins,
|
|
758
835
|
userConfigPath: c.userConfigPath,
|
|
@@ -766,6 +843,7 @@ export function registerMeta(program: Command): void {
|
|
|
766
843
|
["Workspace profile", c.workspaceProfile ?? "(none)"],
|
|
767
844
|
["Team", show("team", c.team)],
|
|
768
845
|
["Workspace", show("workspace", c.workspace)],
|
|
846
|
+
["Default issue project", show("project", c.project)],
|
|
769
847
|
["Sort", show("sort", c.sort)],
|
|
770
848
|
["User config", c.userConfigPath],
|
|
771
849
|
["Project config", c.projectConfigPath],
|
|
@@ -780,6 +858,7 @@ export function registerMeta(program: Command): void {
|
|
|
780
858
|
.command("init")
|
|
781
859
|
.description("Write a project .linear.toml (at the git root, or here outside a repository)")
|
|
782
860
|
.option("--team <key>", "default team key (otherwise chosen from a list)")
|
|
861
|
+
.option("--project <name|id>", "default Linear project for new issues")
|
|
783
862
|
.option("--sort <order>", `default issue-list sort (${ISSUE_SORTS.join(" | ")})`)
|
|
784
863
|
.option("--path <file>", "write this file instead of <git root>/.linear.toml")
|
|
785
864
|
.option("--force", "overwrite an existing file")
|
|
@@ -808,10 +887,11 @@ export function registerMeta(program: Command): void {
|
|
|
808
887
|
team: validateSetting("team", team),
|
|
809
888
|
};
|
|
810
889
|
if (opts.sort !== undefined) settings.sort = validateSetting("sort", opts.sort);
|
|
890
|
+
if (opts.project !== undefined) settings.project = validateSetting("project", opts.project);
|
|
811
891
|
initProjectConfig(path, settings, { force: opts.force === true });
|
|
812
892
|
ctx.output.emit({ success: true, path, ...settings }, () =>
|
|
813
893
|
ctx.output.success(
|
|
814
|
-
`Wrote ${path} (team ${settings.team}${settings.sort ? `, sort ${settings.sort}` : ""}).`,
|
|
894
|
+
`Wrote ${path} (team ${settings.team}${settings.project ? `, project ${settings.project}` : ""}${settings.sort ? `, sort ${settings.sort}` : ""}).`,
|
|
815
895
|
),
|
|
816
896
|
);
|
|
817
897
|
}),
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { action } from "../lib/action.js";
|
|
3
|
+
import { noteOptions, noteInput } from "../lib/release-input.js";
|
|
4
|
+
import { confirmDestructive } from "../lib/prompt.js";
|
|
5
|
+
import * as svc from "../services/release.js";
|
|
6
|
+
|
|
7
|
+
export function registerReleaseNote(program: Command): void {
|
|
8
|
+
const group = program
|
|
9
|
+
.command("release-note")
|
|
10
|
+
.description("Manage Markdown notes covering one or more releases");
|
|
11
|
+
group
|
|
12
|
+
.command("list")
|
|
13
|
+
.alias("ls")
|
|
14
|
+
.description("List release notes with optional pipeline/release filters")
|
|
15
|
+
.option("--pipeline <id|slug>", "filter by pipeline UUID or slug")
|
|
16
|
+
.option("--release <id>", "filter by covered release UUID or slug")
|
|
17
|
+
.option("--include-archived", "include archived notes")
|
|
18
|
+
.action(
|
|
19
|
+
action(async (ctx, opts) => {
|
|
20
|
+
const rows = await svc.listNotes(ctx.client, ctx.limit, opts);
|
|
21
|
+
ctx.output.list(
|
|
22
|
+
rows,
|
|
23
|
+
[
|
|
24
|
+
{ key: "id", header: "ID", value: (r) => r.id },
|
|
25
|
+
{ key: "title", header: "Title", value: (r) => r.title ?? "—" },
|
|
26
|
+
{ key: "pipeline", header: "Pipeline", value: (r) => r.pipeline.name },
|
|
27
|
+
{ key: "releaseCount", header: "Releases", value: (r) => r.releaseCount },
|
|
28
|
+
],
|
|
29
|
+
rows,
|
|
30
|
+
);
|
|
31
|
+
}),
|
|
32
|
+
);
|
|
33
|
+
group
|
|
34
|
+
.command("view <id>")
|
|
35
|
+
.description("Show a release note and its Markdown body by UUID or slug")
|
|
36
|
+
.action(
|
|
37
|
+
action(async (ctx, _opts, id: string) => {
|
|
38
|
+
const n = await svc.getNote(ctx.client, id);
|
|
39
|
+
ctx.output.detail(n, [
|
|
40
|
+
["ID", n.id],
|
|
41
|
+
["Title", n.title],
|
|
42
|
+
["Pipeline", n.pipeline.name],
|
|
43
|
+
["Releases", n.releaseCount],
|
|
44
|
+
["First release", n.firstRelease?.name],
|
|
45
|
+
["Last release", n.lastRelease?.name],
|
|
46
|
+
["Generation status", n.generationStatus],
|
|
47
|
+
["Archived", n.archivedAt],
|
|
48
|
+
["URL", n.url],
|
|
49
|
+
["Content", n.content],
|
|
50
|
+
]);
|
|
51
|
+
}),
|
|
52
|
+
);
|
|
53
|
+
noteOptions(
|
|
54
|
+
group
|
|
55
|
+
.command("create")
|
|
56
|
+
.description("Create a note for explicit releases or an inclusive createdAt range"),
|
|
57
|
+
)
|
|
58
|
+
.requiredOption("--pipeline <id|slug>", "pipeline UUID or slug")
|
|
59
|
+
.action(
|
|
60
|
+
action(async (ctx, opts) => {
|
|
61
|
+
const input = noteInput(opts);
|
|
62
|
+
svc.validateNoteScope(input, true);
|
|
63
|
+
const n = await svc.createNote(ctx.client, opts.pipeline, input);
|
|
64
|
+
ctx.output.emit(n, () => ctx.output.success(`Created release note ${n.id}`));
|
|
65
|
+
}),
|
|
66
|
+
);
|
|
67
|
+
noteOptions(
|
|
68
|
+
group
|
|
69
|
+
.command("update <id>")
|
|
70
|
+
.description("Update a note's title, Markdown, or covered releases"),
|
|
71
|
+
).action(
|
|
72
|
+
action(async (ctx, opts, id: string) => {
|
|
73
|
+
const input = noteInput(opts);
|
|
74
|
+
svc.validateNoteScope(input, false);
|
|
75
|
+
const n = await svc.updateNote(ctx.client, id, input);
|
|
76
|
+
ctx.output.emit(n, () => ctx.output.success(`Updated release note ${n.id}`));
|
|
77
|
+
}),
|
|
78
|
+
);
|
|
79
|
+
group
|
|
80
|
+
.command("delete <id>")
|
|
81
|
+
.description("Delete a release note by UUID or slug")
|
|
82
|
+
.action(
|
|
83
|
+
action(async (ctx, _opts, id: string) => {
|
|
84
|
+
const n = await svc.getNote(ctx.client, id);
|
|
85
|
+
if (!(await confirmDestructive(ctx, `Delete release note ${n.title ?? n.id}?`))) return;
|
|
86
|
+
const receipt = await svc.deleteNote(ctx.client, n.id);
|
|
87
|
+
ctx.output.emit(receipt, () => ctx.output.success(`Deleted release note ${receipt.id}`));
|
|
88
|
+
}),
|
|
89
|
+
);
|
|
90
|
+
}
|