@jira-deploy/core 1.0.11 → 1.0.13

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/dry-run.js DELETED
@@ -1,695 +0,0 @@
1
- /**
2
- * Dry-run / Debug 腳本
3
- *
4
- * 模式 1 — Mock(預設):全部 API 呼叫改用假資料
5
- * node src/dry-run.js <tool> '<args_json>'
6
- *
7
- * 模式 2 — Live(真實 Jira,讀取不寫入):
8
- * node src/dry-run.js <tool> '<args_json>' --live
9
- * 需要 .env 有 JIRA_BASE_URL / JIRA_API_TOKEN
10
- *
11
- * 附加 Node.js debugger:
12
- * node --inspect-brk src/dry-run.js <tool> '<args_json>' [--live]
13
- * → 開 Chrome chrome://inspect 連上後下斷點
14
- *
15
- * ── 常用指令範例 ──────────────────────────────────────────────────────
16
- *
17
- * # Library 票
18
- * node src/dry-run.js create_library_ticket '{"systemCode":"CWA","moduleChild":"cwa","gitBranch":"release/v1.5.2.0"}'
19
- * node src/dry-run.js build_ticket '{"issueKey":"CID-1708"}' --live
20
- *
21
- * # CI 票
22
- * node src/dry-run.js get_next_ci_version '{"systemCode":"IBK","branch":"master"}'
23
- * node src/dry-run.js get_next_ci_version '{"systemCode":"CWA","branch":"master"}' --live
24
- * node src/dry-run.js create_ci_ticket '{"systemCode":"IBK","relatesTo":["CID-1708"]}'
25
- * node src/dry-run.js create_ci_ticket '{"systemCode":"IBK","relatesTo":["CID-1178","CID-1182"]}'
26
- * node src/dry-run.js build_ticket '{"issueKey":"CID-1709"}' --live
27
- * node src/dry-run.js wait_to_dev '{"issueKey":"CID-1709"}' --live
28
- * node src/dry-run.js wait_to_stg '{"issueKey":"CID-1709"}' --live
29
- *
30
- * # CD 票
31
- * node src/dry-run.js create_cd_ticket '{"systemCode":"IBK","environment":"dev","ciTicket":"CID-1709"}'
32
- * node src/dry-run.js create_cd_ticket '{"systemCode":"IBK","environment":"stg","ciTicket":"CID-1709"}'
33
- * node src/dry-run.js create_cd_ticket '{"systemCode":"CWA","environment":"prd","ciTicket":"CID-1709","metaTest":true}'
34
- * node src/dry-run.js create_cd_ticket '{"systemCode":"IBK","environment":"prd","ciTicket":"CID-1709","metaTest":true}' --live
35
- * node src/dry-run.js prepare_cd_deployment '{"issueKey":"CID-1710","environment":"dev"}' --live
36
- * node src/dry-run.js prepare_cd_deployment '{"issueKey":"CID-1710","environment":"stg"}' --live
37
- *
38
- * # Deployment sub-task 觸發(新)
39
- * node src/dry-run.js trigger_deployment '{"cdIssueKey":"CID-1710","environment":"dev"}' --live
40
- * node src/dry-run.js trigger_deployment '{"cdIssueKey":"CID-1710","environment":"stg"}' --live
41
- * node src/dry-run.js trigger_deployment '{"cdIssueKey":"CID-1713","environment":"uat"}' --live
42
- * node src/dry-run.js trigger_deployment '{"cdIssueKey":"CID-1716","environment":"stg","applyForClose":true}' --live
43
- *
44
- * # CD 開單 → 建立 Deployment → 部署 全流程 mock 測試
45
- * node src/dry-run.js prepare_cd_deployment '{"issueKey":"CID-1716","environment":"stg"}'
46
- * node src/dry-run.js trigger_deployment '{"cdIssueKey":"CID-1716","environment":"stg","applyForClose":true}'
47
- *
48
- * # Assignee 切換
49
- * node src/dry-run.js update_assignee '{"issueKey":"CID-1718","displayName":"Solar"}'
50
- * node src/dry-run.js update_assignee '{"issueKey":"CID-1718","accountId":"BK00129"}' --live
51
- *
52
- * # 計算下一個 Library Release 版號流水號
53
- * node src/dry-run.js get_next_lib_version '{"moduleChild":"ibk","branch":"release/v1.5.2.0","fixVersion":"v1.5.2.0"}'
54
- * node src/dry-run.js get_next_lib_version '{"moduleChild":"cwa","branch":"release/v1.5.2.0","fixVersion":"v1.5.2.0"}' --live
55
- *
56
- * # 狀態查詢 / transitions
57
- * node src/dry-run.js list_transitions '{"issueKey":"CID-1710"}' --live
58
- * node src/dry-run.js get_issue_status '{"issueKey":"CID-1709"}' --live
59
- *
60
- * # Release 現況查詢
61
- * node src/dry-run.js get_release_status '{"systemCode":"IBK"}' --live
62
- * node src/dry-run.js get_release_status '{"systemCode":"IBK"}'
63
- *
64
- * # Release Reroll — cancel CI 並取得 Library 模組清單
65
- * node src/dry-run.js cancel_release '{"systemCode":"IBK"}' --live
66
- * node src/dry-run.js cancel_release '{"systemCode":"IBK","ciIssueKey":"CID-1799"}' --live
67
- * node src/dry-run.js cancel_release '{"systemCode":"IBK"}'
68
- * ──────────────────────────────────────────────────────────────────────
69
- */
70
- import 'dotenv/config';
71
- import { executeTool } from './tools/index.js';
72
-
73
- const TOOL = process.argv[2] || 'create_library_ticket';
74
- const ARGS = JSON.parse(process.argv[3] || '{}');
75
- const LIVE = process.argv.includes('--live');
76
-
77
- process.env.JIRA_BASE_URL = process.env.JIRA_BASE_URL || 'https://jira.example.com';
78
- const BASE_URL = `${process.env.JIRA_BASE_URL}/rest/api/2`;
79
-
80
- // ── 彩色 log helpers ──────────────────────────────────────────────
81
- const c = {
82
- cyan: (s) => `\x1b[36m${s}\x1b[0m`,
83
- yellow: (s) => `\x1b[33m${s}\x1b[0m`,
84
- green: (s) => `\x1b[32m${s}\x1b[0m`,
85
- red: (s) => `\x1b[31m${s}\x1b[0m`,
86
- gray: (s) => `\x1b[90m${s}\x1b[0m`,
87
- };
88
-
89
- function printCall(method, path, body) {
90
- console.log('\n' + '═'.repeat(70));
91
- console.log(c.cyan(`${method.toUpperCase()} ${BASE_URL}${path}`));
92
- if (body !== undefined) {
93
- console.log(c.gray('Body:'));
94
- console.log(JSON.stringify(body, null, 2));
95
- }
96
- console.log('═'.repeat(70));
97
- }
98
-
99
- function printRead(key, fieldNames, result) {
100
- console.log(c.gray(` ← GET /issue/${key}?fields=${fieldNames.join(',')}`));
101
- console.log(c.gray(' Response: ' + JSON.stringify(result)));
102
- }
103
-
104
- // ─────────────────────────────────────────────────────────────────
105
- // Mock Jira(模式 1)
106
- // ─────────────────────────────────────────────────────────────────
107
- const mockJira = {
108
- createIssue: async (fields) => {
109
- printCall('POST', '/issue', { fields });
110
- return { id: 'DRY-RUN', key: 'CID-DRYRUN', self: `${BASE_URL}/issue/CID-DRYRUN` };
111
- },
112
- getIssueFields: async (key, fieldNames) => {
113
- console.log(c.gray(` [MOCK] GET /issue/${key}?fields=${fieldNames.join(',')}`));
114
- // CI 單:issuelinks(關聯 Library + 關聯 CI)
115
- if (fieldNames.includes('issuelinks')) {
116
- return {
117
- summary: '[MOCK] IBK CI',
118
- status: { name: 'Wait To STG' },
119
- customfield_13438: '{"ibk_ap_version":"0.0.18"}',
120
- issuelinks: [
121
- {
122
- type: {name: 'Relates', inward: 'relates to', outward: 'relates to'},
123
- outwardIssue: {
124
- key: 'CID-LIB-IBK-MOCK',
125
- fields: {
126
- summary: '[STG][Lib] IBK_IBK_MOCK',
127
- status: {name: 'Released'},
128
- issuetype: {name: 'Library'},
129
- },
130
- },
131
- },
132
- {
133
- type: {name: 'Relates', inward: 'relates to', outward: 'relates to'},
134
- outwardIssue: {
135
- key: 'CID-LIB-SSR-MOCK',
136
- fields: {
137
- summary: '[STG][Lib] IBK_SSR_MOCK',
138
- status: {name: 'Released'},
139
- issuetype: {name: 'Library'},
140
- },
141
- },
142
- },
143
- {
144
- type: {name: 'Relates', inward: 'relates to', outward: 'relates to'},
145
- outwardIssue: {
146
- key: 'CID-CI-OLD-MOCK',
147
- fields: {
148
- summary: '[MOCK] IBK Old CI',
149
- status: {name: 'Done'},
150
- issuetype: {name: 'CI'},
151
- },
152
- },
153
- },
154
- ],
155
- };
156
- }
157
- // CI 單 release_version
158
- if (fieldNames.includes('customfield_13438')) {
159
- return {customfield_13438: '{"ibk_ap_version":"0.0.18"}'};
160
- }
161
- // Library 單:gitBranch + systemModule
162
- if (fieldNames.includes('customfield_13431')) {
163
- const moduleMap = {
164
- 'CID-LIB-IBK-MOCK': {customfield_13431: 'release/v1.5.2.0', customfield_13444: {value: 'ibk'}},
165
- 'CID-LIB-SSR-MOCK': {customfield_13431: 'release/v1.3.1.0', customfield_13444: {value: 'ssr'}},
166
- };
167
- return moduleMap[key] ?? {customfield_13431: 'release/v1.0.0.0', customfield_13444: {value: 'unknown'}};
168
- }
169
- // systemCode (customfield_13443)
170
- if (fieldNames.includes('customfield_13443')) {
171
- return {customfield_13443: {value: 'IBK'}};
172
- }
173
- return {};
174
- },
175
- updateIssue: async (key, fields) => {
176
- printCall('PUT', `/issue/${key}`, { fields });
177
- return {};
178
- },
179
- linkIssue: async (inward, outward, type) => {
180
- printCall('POST', '/issueLink', {
181
- type: { name: type },
182
- inwardIssue: { key: inward },
183
- outwardIssue: { key: outward },
184
- });
185
- return {};
186
- },
187
- addRemoteLink: async (issueKey, url, title) => {
188
- printCall('POST', `/issue/${issueKey}/remotelink`, { object: { url, title } });
189
- return {};
190
- },
191
- getIssue: async (key) => ({ fields: { status: { name: 'To Do' }, summary: `[MOCK] ${key}` } }),
192
- // stateful mock:模擬 CD 單逐步推進的 transition 清單
193
- _cdTransitionStep: 0,
194
- getTransitions: async function (key) {
195
- // Deployment sub-task mock
196
- if (key === 'CID-DEPLOY-MOCK') {
197
- const mockTrans = [
198
- {id: '11', name: 'To AutoDeploy', to: {name: 'Pre Auto Deploy'}},
199
- {id: '12', name: 'Trigger AutoDeploy', to: {name: 'Auto Deploy'}},
200
- ];
201
- console.log(c.gray(` [MOCK] getTransitions(${key}) → [${mockTrans.map((t) => t.name).join(', ')}]`));
202
- return mockTrans;
203
- }
204
- // CI 單 mock:包含 Cancelled transition(供 cancel_release 使用)
205
- if (key === 'CID-CI-MOCK' || key.startsWith('CID-CI')) {
206
- const trans = [{id: '71', name: 'Cancelled', to: {name: 'Cancelled'}}];
207
- console.log(c.gray(` [MOCK] getTransitions(${key}) → [${trans.map((t) => t.name).join(', ')}]`));
208
- return trans;
209
- }
210
- // CD 單 mock:依呼叫次數模擬流程推進
211
- // step 0-1: TO DO → Accept 可用
212
- // step 2-3: Wait For Send Notice Email → Prepare to create deployment ticket 可用
213
- // step 4-5: Prepare For Deploy → Apply for approval 可用
214
- // step 6+: Wait Approval → Apply for close 可用
215
- const CD_STEP_TRANSITIONS = [
216
- [{ id: '10', name: 'Accept', to: { name: 'Wait For Send Notice Email' } }],
217
- [{ id: '10', name: 'Accept', to: { name: 'Wait For Send Notice Email' } }],
218
- [
219
- {
220
- id: '71',
221
- name: 'Prepare to create deployment ticket',
222
- to: { name: 'Prepare For Deploy' },
223
- },
224
- ],
225
- [
226
- {
227
- id: '71',
228
- name: 'Prepare to create deployment ticket',
229
- to: { name: 'Prepare For Deploy' },
230
- },
231
- ],
232
- [{ id: '21', name: 'Apply for approval', to: { name: 'Wait Approval' } }],
233
- [{ id: '21', name: 'Apply for approval', to: { name: 'Wait Approval' } }],
234
- [{ id: '99', name: 'Apply for close', to: { name: 'Wait For Close Approval' } }],
235
- ];
236
- const step = Math.min(this._cdTransitionStep++, CD_STEP_TRANSITIONS.length - 1);
237
- const trans = CD_STEP_TRANSITIONS[step];
238
- console.log(
239
- c.gray(
240
- ` [MOCK] getTransitions(${key}) step=${step} → [${trans.map((t) => t.name).join(', ')}]`,
241
- ),
242
- );
243
- return trans;
244
- },
245
- transitionById: async (key, id) => {
246
- console.log(c.gray(` [MOCK] transitionById(${key}, ${id})`));
247
- },
248
- transitionByName: async () => ({ transitioned: 'Done', toStatus: 'Done' }),
249
- addComment: async (key, body) => {
250
- printCall('POST', `/issue/${key}/comment`, { body });
251
- return {};
252
- },
253
- searchIssues: async (jql, fields, max) => {
254
- console.log(c.gray(` [MOCK] searchIssues(${jql.slice(0, 80)}...)`));
255
- // cancel_release / get_release_status 查 CI 單
256
- if (jql.includes('issuetype = CI')) {
257
- return [{
258
- key: 'CID-CI-MOCK',
259
- fields: {
260
- summary: '[IBK][CI] Mock CI 單',
261
- status: {name: 'Wait To STG'},
262
- customfield_13438: '{"ibk_ap_version":"0.0.18"}',
263
- updated: new Date().toISOString(),
264
- issuelinks: [
265
- {
266
- type: {name: 'Relates', inward: 'relates to', outward: 'relates to'},
267
- outwardIssue: {
268
- key: 'CID-LIB-IBK-MOCK',
269
- fields: {
270
- summary: '[STG][Lib] IBK_IBK_MOCK',
271
- status: {name: 'Released'},
272
- issuetype: {name: 'Library'},
273
- },
274
- },
275
- },
276
- {
277
- type: {name: 'Relates', inward: 'relates to', outward: 'relates to'},
278
- outwardIssue: {
279
- key: 'CID-LIB-SSR-MOCK',
280
- fields: {
281
- summary: '[STG][Lib] IBK_SSR_MOCK',
282
- status: {name: 'Released'},
283
- issuetype: {name: 'Library'},
284
- },
285
- },
286
- },
287
- ],
288
- },
289
- }];
290
- }
291
- // get_release_status 查 CD 單
292
- if (jql.includes('issuetype = CD')) {
293
- return [{
294
- key: 'CID-CD-STG-MOCK',
295
- fields: {
296
- summary: '[IBK][STG] CD MOCK',
297
- status: {name: 'Wait Deploy'},
298
- customfield_13436: {value: 'stg'},
299
- customfield_14101: '{"ibk_ap_version":"0.0.18"}',
300
- updated: new Date().toISOString(),
301
- },
302
- }];
303
- }
304
- return [];
305
- },
306
- getBitbucketTags: async () => [],
307
- getBitbucketBranches: async () => [],
308
- getBitbucketFileContent: async (project, repo, filePath, branch) => {
309
- console.log(
310
- c.gray(` [MOCK] getBitbucketFileContent(${project}/${repo}/${filePath}@${branch})`),
311
- );
312
- // pom.xml → CI Golden Image 格式
313
- if (filePath === 'pom.xml') {
314
- return `<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
315
- <modelVersion>4.0.0</modelVersion>
316
-
317
- <parent>
318
- <groupId>com.line.bank</groupId>
319
- <artifactId>assembly.parent</artifactId>
320
- <version>0.0.1</version>
321
- </parent>
322
-
323
- <groupId>com.line.bank</groupId>
324
- <artifactId>ch014.ibk.assembly</artifactId>
325
- <version>0.0.12-SNAPSHOT</version>
326
- <packaging>pom</packaging>
327
-
328
- <name>ch014.ibk.assembly</name>
329
- <description>IBK Golden Image</description>
330
-
331
- <scm>
332
- <connection>scm:git:https://bitbucket.linebank.com.tw/scm/lbtwgol/ch014.ibk.assembly.git</connection>
333
- <developerConnection>scm:git:https://bitbucket.linebank.com.tw/scm/lbtwgol/ch014.ibk.assembly.git</developerConnection>
334
- <url>https://bitbucket.linebank.com.tw/scm/lbtwgol/ch014.ibk.assembly</url>
335
- <tag>HEAD</tag>
336
- </scm>
337
-
338
- <properties>
339
- <ssr.version>release-v1.3.1.0-0.0.1</ssr.version>
340
- <wealth.version>release-v1.1.0.0-0.0.1</wealth.version>
341
- <ibk.version>release-v1.5.2.0-0.0.1</ibk.version>
342
- <a11y.version>release-v1.5.1.0-0.0.4</a11y.version>
343
- </properties>
344
-
345
- <build>
346
- <plugins>
347
- <plugin>
348
- <groupId>org.apache.maven.plugins</groupId>
349
- <artifactId>maven-resources-plugin</artifactId>
350
- <version>3.3.1</version>
351
- <executions>
352
- <execution>
353
- <id>copy-dependency-libs</id>
354
- <phase>process-resources</phase>
355
- <goals>
356
- <goal>copy-resources</goal>
357
- </goals>
358
- <configuration>
359
- <overwrite>true</overwrite>
360
- <includeEmptyDirs>true</includeEmptyDirs>
361
- <outputDirectory>../../target/checkout/libs</outputDirectory>
362
- <resources>
363
- <resource>
364
- <directory>../../libs</directory>
365
- </resource>
366
- </resources>
367
- </configuration>
368
- </execution>
369
- <execution>
370
- <id>copy-wealth</id>
371
- <phase>process-resources</phase>
372
- <goals>
373
- <goal>copy-resources</goal>
374
- </goals>
375
- <configuration>
376
- <overwrite>true</overwrite>
377
- <includeEmptyDirs>true</includeEmptyDirs>
378
- <outputDirectory>./target/wealth</outputDirectory>
379
- <resources>
380
- <resource>
381
- <directory>libs/wealth</directory>
382
- </resource>
383
- </resources>
384
- </configuration>
385
- </execution>
386
- <execution>
387
- <id>copy-ssr</id>
388
- <phase>process-resources</phase>
389
- <goals>
390
- <goal>copy-resources</goal>
391
- </goals>
392
- <configuration>
393
- <overwrite>true</overwrite>
394
- <includeEmptyDirs>true</includeEmptyDirs>
395
- <outputDirectory>./target/ssr</outputDirectory>
396
- <resources>
397
- <resource>
398
- <directory>libs/ssr</directory>
399
- </resource>
400
- </resources>
401
- </configuration>
402
- </execution>
403
- <execution>
404
- <id>copy-ibk</id>
405
- <phase>process-resources</phase>
406
- <goals>
407
- <goal>copy-resources</goal>
408
- </goals>
409
- <configuration>
410
- <overwrite>true</overwrite>
411
- <includeEmptyDirs>true</includeEmptyDirs>
412
- <outputDirectory>./target/ibk</outputDirectory>
413
- <resources>
414
- <resource>
415
- <directory>libs/ibk</directory>
416
- </resource>
417
- </resources>
418
- </configuration>
419
- </execution>
420
- <execution>
421
- <id>copy-a11y</id>
422
- <phase>process-resources</phase>
423
- <goals>
424
- <goal>copy-resources</goal>
425
- </goals>
426
- <configuration>
427
- <overwrite>true</overwrite>
428
- <includeEmptyDirs>true</includeEmptyDirs>
429
- <outputDirectory>./target/a11y</outputDirectory>
430
- <resources>
431
- <resource>
432
- <directory>libs/a11y</directory>
433
- </resource>
434
- </resources>
435
- </configuration>
436
- </execution>
437
- </executions>
438
- </plugin>
439
- <plugin>
440
- <groupId>org.apache.maven.plugins</groupId>
441
- <artifactId>maven-assembly-plugin</artifactId>
442
- <version>3.6.0</version>
443
- <executions>
444
- <execution>
445
- <id>create-wealth</id>
446
- <phase>package</phase>
447
- <goals>
448
- <goal>single</goal>
449
- </goals>
450
- <configuration>
451
- <appendAssemblyId>true</appendAssemblyId>
452
- <descriptors>
453
- <descriptor>src/main/resources/build-wealth.xml</descriptor>
454
- </descriptors>
455
- </configuration>
456
- </execution>
457
- <execution>
458
- <id>create-ssr</id>
459
- <phase>package</phase>
460
- <goals>
461
- <goal>single</goal>
462
- </goals>
463
- <configuration>
464
- <appendAssemblyId>true</appendAssemblyId>
465
- <descriptors>
466
- <descriptor>src/main/resources/build-ssr.xml</descriptor>
467
- </descriptors>
468
- </configuration>
469
- </execution>
470
- <execution>
471
- <id>create-ibk</id>
472
- <phase>package</phase>
473
- <goals>
474
- <goal>single</goal>
475
- </goals>
476
- <configuration>
477
- <appendAssemblyId>true</appendAssemblyId>
478
- <descriptors>
479
- <descriptor>src/main/resources/build-ibk.xml</descriptor>
480
- </descriptors>
481
- </configuration>
482
- </execution>
483
- <execution>
484
- <id>create-a11y</id>
485
- <phase>package</phase>
486
- <goals>
487
- <goal>single</goal>
488
- </goals>
489
- <configuration>
490
- <appendAssemblyId>true</appendAssemblyId>
491
- <descriptors>
492
- <descriptor>src/main/resources/build-a11y.xml</descriptor>
493
- </descriptors>
494
- </configuration>
495
- </execution>
496
- </executions>
497
- </plugin>
498
- </plugins>
499
- </build>
500
-
501
-
502
- </project>`;
503
- }
504
- // *.xml → Library Release 格式(假設現版 = release-v1.5.1.1-0.0.2)
505
- return `<?xml version="1.0"?>\n<root>\n <version>release-v1.5.1.1-0.0.2-SNAPSHOT</version>\n</root>`;
506
- },
507
- getProjectVersions: async (projectKey, { name } = {}) => {
508
- console.log(
509
- c.gray(
510
- ` [MOCK] getProjectVersions(${projectKey}, {name=${name}}) → null (mock:模擬找不到版本)`,
511
- ),
512
- );
513
- return null;
514
- },
515
- // trigger_deployment 用:回傳 mock sub-task
516
- getSubTasks: async (key) => {
517
- const mockSubTasks = [
518
- {
519
- id: 'MOCK-1',
520
- key: 'CID-DEPLOY-MOCK',
521
- fields: {
522
- summary: `[STG] Deployment for ${key}`,
523
- status: { name: 'Open' },
524
- issuetype: { name: 'Deployment' },
525
- },
526
- },
527
- ];
528
- console.log(
529
- c.gray(` [MOCK] getSubTasks(${key}) → ${mockSubTasks.map((t) => t.key).join(', ')}`),
530
- );
531
- return mockSubTasks;
532
- },
533
- updateAssignee: async (issueKey, accountId) => {
534
- printCall('PUT', `/issue/${issueKey}/assignee`, { name: accountId });
535
- return { issueKey, accountId };
536
- },
537
- getUnreleasedVersionsList: async (projectKey) => {
538
- console.log(c.gray(` [MOCK] getUnreleasedVersionsList(${projectKey})`));
539
- return [
540
- { id: '10001', name: 'IBK_1.5.2.0', released: false, archived: false },
541
- { id: '10002', name: 'IBK_ssr_1.3.1.0', released: false, archived: false },
542
- { id: '10003', name: 'IBK_wealth_1.1.0.0', released: false, archived: false },
543
- { id: '10004', name: 'IBK_accessibility_1.5.1.0', released: false, archived: false },
544
- { id: '10005', name: 'CWA_1.5.4.0', released: false, archived: false },
545
- ];
546
- },
547
- getComments: async (issueKey) => {
548
- console.log(c.gray(` [MOCK] getComments(${issueKey})`));
549
- return [
550
- {
551
- author: { name: 'BK00178', displayName: 'James Yu' },
552
- body: 'Approved',
553
- created: new Date().toISOString(),
554
- },
555
- ];
556
- },
557
- };
558
-
559
- // ─────────────────────────────────────────────────────────────────
560
- // Live Jira(模式 2):讀取用真實 API,寫入全攔截
561
- // ─────────────────────────────────────────────────────────────────
562
- async function buildLiveJira() {
563
- const { JiraClient } = await import('./jira-client.js');
564
- const real = new JiraClient();
565
-
566
- return {
567
- // ── 讀取:直接呼叫真實 API,印出 response ──
568
- getIssueFields: async (key, fieldNames) => {
569
- const result = await real.getIssueFields(key, fieldNames);
570
- printRead(key, fieldNames, result);
571
- return result;
572
- },
573
- getIssue: async (key) => {
574
- const result = await real.getIssue(key);
575
- console.log(c.gray(` ← getIssue(${key}) status=${result.fields?.status?.name}`));
576
- return result;
577
- },
578
- getTransitions: async (key) => {
579
- const result = await real.getTransitions(key);
580
- console.log(c.gray(` ← getTransitions(${key}) [${result.map((t) => t.name).join(', ')}]`));
581
- return result;
582
- },
583
- getSubTasks: async (key) => {
584
- const result = await real.getSubTasks(key);
585
- console.log(
586
- c.gray(
587
- ` ← getSubTasks(${key}) → [${result.map((t) => `${t.key}(${t.fields?.summary?.slice(0, 30)})`).join(', ')}]`,
588
- ),
589
- );
590
- return result;
591
- },
592
- searchIssues: async (jql, fields, max) => {
593
- const result = await real.searchIssues(jql, fields, max);
594
- console.log(c.gray(` ← searchIssues(${jql.slice(0, 60)}...) → ${result.length} results`));
595
- return result;
596
- },
597
- getBitbucketTags: async (project, repo, opts) => {
598
- const result = await real.getBitbucketTags(project, repo, opts);
599
- console.log(
600
- c.gray(` ← getBitbucketTags(${repo}) → [${result.map((t) => t.displayId).join(', ')}]`),
601
- );
602
- return result;
603
- },
604
- getBitbucketBranches: async (project, repo, opts) => {
605
- const result = await real.getBitbucketBranches(project, repo, opts);
606
- console.log(
607
- c.gray(
608
- ` ← getBitbucketBranches(${repo}) → [${result.map((b) => b.displayId).join(', ')}]`,
609
- ),
610
- );
611
- return result;
612
- },
613
- getBitbucketFileContent: async (project, repo, filePath, branch) => {
614
- const result = await real.getBitbucketFileContent(project, repo, filePath, branch);
615
- console.log(
616
- c.gray(
617
- ` ← getBitbucketFileContent(${repo}/${filePath}@${branch}) → ${result.slice(0, 80)}...`,
618
- ),
619
- );
620
- return result;
621
- },
622
- getProjectVersions: async (projectKey, opts = {}) => {
623
- const result = await real.getProjectVersions(projectKey, opts);
624
- console.log(
625
- c.gray(` ← getProjectVersions(${projectKey}, {name=${opts.name}}) → ${result ?? 'null'}`),
626
- );
627
- return result;
628
- },
629
- getUnreleasedVersionsList: async (projectKey) => {
630
- const result = await real.getUnreleasedVersionsList(projectKey);
631
- console.log(
632
- c.gray(` ← getUnreleasedVersionsList(${projectKey}) → ${result.length} versions`),
633
- );
634
- return result;
635
- },
636
- getComments: async (issueKey) => {
637
- const result = await real.getComments(issueKey);
638
- console.log(c.gray(` ← getComments(${issueKey}) → ${result.length} comments`));
639
- return result;
640
- },
641
-
642
- // ── 寫入:全部攔截,只印出不執行 ──
643
- createIssue: async (fields) => {
644
- printCall('POST [INTERCEPTED]', '/issue', { fields });
645
- return { id: 'DRY-RUN', key: 'CID-DRYRUN', self: '' };
646
- },
647
- updateIssue: async (key, fields) => {
648
- printCall('PUT [INTERCEPTED]', `/issue/${key}`, { fields });
649
- return {};
650
- },
651
- linkIssue: async (inward, outward, type) => {
652
- printCall('POST [INTERCEPTED]', '/issueLink', {
653
- type: { name: type },
654
- inwardIssue: { key: inward },
655
- outwardIssue: { key: outward },
656
- });
657
- return {};
658
- },
659
- addRemoteLink: async (issueKey, url, title) => {
660
- printCall('POST [INTERCEPTED]', `/issue/${issueKey}/remotelink`, { object: { url, title } });
661
- return {};
662
- },
663
- transitionById: async (key, id) => {
664
- printCall('POST [INTERCEPTED]', `/issue/${key}/transitions`, { transition: { id } });
665
- return {};
666
- },
667
- transitionByName: async (key, name) => {
668
- printCall('POST [INTERCEPTED]', `/issue/${key}/transitions`, { transition: { name } });
669
- return { transitioned: name, toStatus: name };
670
- },
671
- addComment: async (key, body) => {
672
- printCall('POST [INTERCEPTED]', `/issue/${key}/comment`, { body });
673
- return {};
674
- },
675
- updateAssignee: async (issueKey, accountId) => {
676
- printCall('PUT [INTERCEPTED]', `/issue/${issueKey}/assignee`, { name: accountId });
677
- return { issueKey, accountId };
678
- },
679
- };
680
- }
681
-
682
- // ─────────────────────────────────────────────────────────────────
683
- // Main
684
- // ─────────────────────────────────────────────────────────────────
685
- const mockNotifier = { notify: async () => [] };
686
-
687
- console.log(c.yellow(`\n🔍 Dry-run [${LIVE ? 'LIVE' : 'MOCK'}]: ${TOOL}`));
688
- console.log('Args:', JSON.stringify(ARGS, null, 2));
689
- if (LIVE) console.log(c.yellow('⚠️ Live mode: 讀取用真實 Jira,寫入全部攔截不執行'));
690
-
691
- const jira = LIVE ? await buildLiveJira() : mockJira;
692
-
693
- const result = await executeTool(TOOL, ARGS, { jira, notifier: mockNotifier });
694
- console.log(c.green('\n✅ Tool result:'));
695
- console.log(result.content[0].text);