@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/constants/config.js +183 -0
- package/constants/defaults.js +4 -38
- package/constants/environments.js +7 -29
- package/constants/field-ids.js +9 -41
- package/constants/index.js +3 -1
- package/constants/issue-types.js +7 -19
- package/constants/modules.js +5 -32
- package/constants/repos.js +5 -53
- package/constants/server.js +5 -98
- package/constants/system-codes.js +9 -68
- package/constants/users.js +4 -24
- package/jira-client.js +6 -3
- package/package.json +17 -5
- package/platform-config.js +2 -7
- package/scripts/jabber_notify.py +2 -2
- package/tools/grayrelease.js +43 -32
- package/tools/helpers.js +6 -22
- package/tools/index.js +16 -16
- package/tools/jabber.js +3 -3
- package/tools/release.js +17 -11
- package/dry-run.js +0 -695
- package/tools/ci.test.js +0 -154
- package/tools/jabber.test.js +0 -54
- package/tools/release.test.js +0 -137
- package/tools.test.js +0 -2775
|
@@ -1,79 +1,20 @@
|
|
|
1
|
-
|
|
2
|
-
* 系統代碼及其映射
|
|
3
|
-
*/
|
|
1
|
+
import {getDeployConfig} from './config.js';
|
|
4
2
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
EIB: 'EIB',
|
|
9
|
-
EVT: 'EVT',
|
|
10
|
-
NPM: 'NPM',
|
|
11
|
-
BOF: 'BOF',
|
|
12
|
-
};
|
|
3
|
+
const config = getDeployConfig();
|
|
4
|
+
|
|
5
|
+
export const SYSTEM_CODES = config.systemCodes;
|
|
13
6
|
|
|
14
7
|
/** 系統 → 部門代碼映射 */
|
|
15
|
-
export const SYSTEM_TO_DEPT_MAP =
|
|
16
|
-
IBK: 'CH014',
|
|
17
|
-
CWA: 'CH014',
|
|
18
|
-
NPM: 'CH014',
|
|
19
|
-
EVT: 'CH015',
|
|
20
|
-
EIB: 'CH015',
|
|
21
|
-
BOF: 'CH015',
|
|
22
|
-
};
|
|
8
|
+
export const SYSTEM_TO_DEPT_MAP = config.systemToDept;
|
|
23
9
|
|
|
24
10
|
/** 系統代碼的 Jira ID */
|
|
25
|
-
export const SYSTEM_CODE_JIRA_IDS =
|
|
26
|
-
// Library 中的系統代碼 ID
|
|
27
|
-
library: {
|
|
28
|
-
IBK: '15600',
|
|
29
|
-
EVT: '15509',
|
|
30
|
-
CWA: '15513',
|
|
31
|
-
BOF: '15800',
|
|
32
|
-
EIB: '15803',
|
|
33
|
-
NPM: '15807',
|
|
34
|
-
},
|
|
35
|
-
// CI 中的系統代碼 ID
|
|
36
|
-
ci: {
|
|
37
|
-
EVT: '15508',
|
|
38
|
-
BOF: '15802',
|
|
39
|
-
EIB: '15806',
|
|
40
|
-
IBK: '14800',
|
|
41
|
-
CWA: '15516',
|
|
42
|
-
},
|
|
43
|
-
};
|
|
11
|
+
export const SYSTEM_CODE_JIRA_IDS = config.systemCodeJiraIds;
|
|
44
12
|
|
|
45
13
|
/** 各系統支援的模組 */
|
|
46
|
-
export const SYSTEM_MODULES =
|
|
47
|
-
IBK: ['ibk', 'wealth', 'ssr', 'a11y'],
|
|
48
|
-
CWA: ['cwa'],
|
|
49
|
-
BOF: ['web'],
|
|
50
|
-
EIB: ['web_cust', 'web_agnt'],
|
|
51
|
-
EVT: ['evt007', 'evt009', 'evt005', 'nextjs_web'],
|
|
52
|
-
NPM: ['utils'],
|
|
53
|
-
};
|
|
14
|
+
export const SYSTEM_MODULES = config.systemModules;
|
|
54
15
|
|
|
55
16
|
/** 模組名稱 → Git Repo 映射 */
|
|
56
|
-
export const MODULE_TO_REPO_MAP =
|
|
57
|
-
ibk: 'tw-bank-web',
|
|
58
|
-
ssr: 'tw-bank-web-ssr',
|
|
59
|
-
wealth: 'tw-bank-web-wealth',
|
|
60
|
-
a11y: 'tw-bank-web-a11y',
|
|
61
|
-
cwa: 'tw-bank-webapp',
|
|
62
|
-
web: 'tw-bank-admin-web',
|
|
63
|
-
web_cust: 'tw-bank-enterprise-cust',
|
|
64
|
-
web_agnt: 'tw-bank-enterprise-agent',
|
|
65
|
-
evt007: 'tw-bank-evt',
|
|
66
|
-
evt009: 'tw-bank-evt',
|
|
67
|
-
evt005: 'tw-bank-evt',
|
|
68
|
-
nextjs_web: 'tw-bank-web-evt',
|
|
69
|
-
utils: 'tw-bank-npm',
|
|
70
|
-
};
|
|
17
|
+
export const MODULE_TO_REPO_MAP = config.moduleToRepo;
|
|
71
18
|
|
|
72
19
|
/** 系統代碼 → CI 倉庫映射 */
|
|
73
|
-
export const SYSTEM_TO_CI_REPO_MAP =
|
|
74
|
-
IBK: 'ch014.ibk.assembly',
|
|
75
|
-
CWA: 'ch014.cwa.assembly',
|
|
76
|
-
BOF: 'ch015.bof.assembly',
|
|
77
|
-
EIB: 'ch015.eib.assembly',
|
|
78
|
-
EVT: 'ch015.evt.assembly',
|
|
79
|
-
};
|
|
20
|
+
export const SYSTEM_TO_CI_REPO_MAP = config.systemToCiRepo;
|
package/constants/users.js
CHANGED
|
@@ -1,45 +1,25 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
* 使用方式:resolveAccountId('Solar') → 'BK00129'
|
|
5
|
-
*/
|
|
6
|
-
export const USER_MAP = {
|
|
7
|
-
// 格式:'名稱/暱稱(不分大小寫)': 'accountId'
|
|
8
|
-
'Solar Chen': 'BK00325',
|
|
9
|
-
'Rex Li': 'BK00325',
|
|
10
|
-
'James Yu': 'BK00325',
|
|
11
|
-
'Alvin Wang': 'BK00325',
|
|
12
|
-
'Chester Kuo': 'BK00325',
|
|
13
|
-
'Riemann Tseng': 'BK00325',
|
|
14
|
-
// 'Solar Chen': 'BK00129',
|
|
15
|
-
// 'Rex Li': 'BK00136',
|
|
16
|
-
// 'James Yu': 'BK00178',
|
|
17
|
-
// 'Alvin Wang': 'BK00236',
|
|
18
|
-
// 'Chester Kuo': 'BK00172',
|
|
19
|
-
// 'Riemann Tseng': 'BK00003',
|
|
20
|
-
};
|
|
1
|
+
import {getDeployConfig} from './config.js';
|
|
2
|
+
|
|
3
|
+
export const USER_MAP = getDeployConfig().users.aliases;
|
|
21
4
|
|
|
22
5
|
/**
|
|
23
6
|
* 依名稱模糊查找 accountId(不分大小寫)
|
|
24
7
|
* 優先順序:完全相符 → key 包含輸入 → 輸入包含 key
|
|
25
|
-
* @param {string} name -
|
|
8
|
+
* @param {string} name - 顯示名稱或暱稱
|
|
26
9
|
* @returns {string|null} accountId 或 null(查無此人)
|
|
27
10
|
*/
|
|
28
11
|
export function resolveAccountId(name) {
|
|
29
12
|
if (!name) return null;
|
|
30
13
|
const query = name.toLowerCase();
|
|
31
14
|
|
|
32
|
-
// 1. 完全相符
|
|
33
15
|
const exact = USER_MAP[query];
|
|
34
16
|
if (exact) return exact;
|
|
35
17
|
|
|
36
18
|
const entries = Object.entries(USER_MAP);
|
|
37
19
|
|
|
38
|
-
// 2. key 包含輸入(例如 "solar chen" 包含 "solar")
|
|
39
20
|
const keyContains = entries.find(([k]) => k.toLowerCase().includes(query));
|
|
40
21
|
if (keyContains) return keyContains[1];
|
|
41
22
|
|
|
42
|
-
// 3. 輸入包含 key(例如 "solar chen" 被輸入,key 是 "solar")
|
|
43
23
|
const inputContains = entries.find(([k]) => query.includes(k.toLowerCase()));
|
|
44
24
|
if (inputContains) return inputContains[1];
|
|
45
25
|
|
package/jira-client.js
CHANGED
|
@@ -255,7 +255,8 @@ export class JiraClient {
|
|
|
255
255
|
|
|
256
256
|
// 取得 Bitbucket repo 的原始檔案內容(Bitbucket REST API 1.0)
|
|
257
257
|
async getBitbucketFileContent(project, repo, filePath, branch) {
|
|
258
|
-
const BB_BASE = process.env.BITBUCKET_URL ??
|
|
258
|
+
const BB_BASE = process.env.BITBUCKET_URL ?? process.env.BITBUCKET_BASE_URL;
|
|
259
|
+
if (!BB_BASE) throw new Error('Missing required Bitbucket env var: BITBUCKET_URL');
|
|
259
260
|
const url = `${BB_BASE}/rest/api/1.0/projects/${project}/repos/${repo}/raw/${filePath}`;
|
|
260
261
|
const res = await axios
|
|
261
262
|
.get(url, {
|
|
@@ -280,7 +281,8 @@ export class JiraClient {
|
|
|
280
281
|
repo,
|
|
281
282
|
{filterValue = '', orderBy = 'MODIFICATION', limit = 1} = {},
|
|
282
283
|
) {
|
|
283
|
-
const BB_BASE = process.env.BITBUCKET_URL ??
|
|
284
|
+
const BB_BASE = process.env.BITBUCKET_URL ?? process.env.BITBUCKET_BASE_URL;
|
|
285
|
+
if (!BB_BASE) throw new Error('Missing required Bitbucket env var: BITBUCKET_URL');
|
|
284
286
|
const url = `${BB_BASE}/rest/api/1.0/projects/${project}/repos/${repo}/tags`;
|
|
285
287
|
const res = await axios
|
|
286
288
|
.get(url, {
|
|
@@ -305,7 +307,8 @@ export class JiraClient {
|
|
|
305
307
|
repo,
|
|
306
308
|
{filterValue = '', orderBy = 'MODIFICATION', limit = 1} = {},
|
|
307
309
|
) {
|
|
308
|
-
const BB_BASE = process.env.BITBUCKET_URL ??
|
|
310
|
+
const BB_BASE = process.env.BITBUCKET_URL ?? process.env.BITBUCKET_BASE_URL;
|
|
311
|
+
if (!BB_BASE) throw new Error('Missing required Bitbucket env var: BITBUCKET_URL');
|
|
309
312
|
const url = `${BB_BASE}/rest/api/1.0/projects/${project}/repos/${repo}/branches`;
|
|
310
313
|
const res = await axios
|
|
311
314
|
.get(url, {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jira-deploy/core",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.13",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./index.js",
|
|
6
6
|
"repository": {
|
|
@@ -21,15 +21,27 @@
|
|
|
21
21
|
},
|
|
22
22
|
"files": [
|
|
23
23
|
"constants/**/*.js",
|
|
24
|
-
"scripts
|
|
25
|
-
"tools
|
|
26
|
-
"
|
|
24
|
+
"scripts/jabber_notify.py",
|
|
25
|
+
"tools/cd.js",
|
|
26
|
+
"tools/ci.js",
|
|
27
|
+
"tools/grayrelease.js",
|
|
28
|
+
"tools/helpers.js",
|
|
29
|
+
"tools/index.js",
|
|
30
|
+
"tools/jabber.js",
|
|
31
|
+
"tools/library.js",
|
|
32
|
+
"tools/release.js",
|
|
33
|
+
"tools/workflows.js",
|
|
34
|
+
"index.js",
|
|
35
|
+
"jira-client.js",
|
|
36
|
+
"notifier.js",
|
|
37
|
+
"platform-config.js",
|
|
38
|
+
"poller.js"
|
|
27
39
|
],
|
|
28
40
|
"dependencies": {
|
|
29
41
|
"axios": "^1.6.0",
|
|
30
42
|
"dotenv": "^16.3.0"
|
|
31
43
|
},
|
|
32
44
|
"scripts": {
|
|
33
|
-
"test": "node --test tools.test.js tools/jabber.test.js"
|
|
45
|
+
"test": "node --import ./test-env.js --test tools.test.js tools/jabber.test.js config.test.js"
|
|
34
46
|
}
|
|
35
47
|
}
|
package/platform-config.js
CHANGED
|
@@ -12,17 +12,12 @@ import {getServerList} from './tools/helpers.js';
|
|
|
12
12
|
* 平台映射 - 將使用者友善的平台名稱對應到系統代碼
|
|
13
13
|
*/
|
|
14
14
|
export const PLATFORM_TO_SYSTEM_CODE = {
|
|
15
|
-
|
|
16
|
-
ibk: SYSTEM_CODES.IBK,
|
|
17
|
-
eib: SYSTEM_CODES.EIB,
|
|
18
|
-
evt: SYSTEM_CODES.EVT,
|
|
19
|
-
bof: SYSTEM_CODES.BOF,
|
|
20
|
-
npm: SYSTEM_CODES.NPM,
|
|
15
|
+
...Object.fromEntries(Object.values(SYSTEM_CODES).map((code) => [code.toLowerCase(), code])),
|
|
21
16
|
};
|
|
22
17
|
|
|
23
18
|
/**
|
|
24
19
|
* 取得平台設定 - 根據平台名稱回傳相關設定
|
|
25
|
-
* @param {string} platformName - 平台名稱
|
|
20
|
+
* @param {string} platformName - 平台名稱
|
|
26
21
|
* @param {string} environment - 環境 (例如 'stg', 'prd')
|
|
27
22
|
* @returns {object} 平台設定
|
|
28
23
|
*/
|
package/scripts/jabber_notify.py
CHANGED
|
@@ -13,8 +13,8 @@ Required environment variables:
|
|
|
13
13
|
JABBER_KEYCHAIN_ACCOUNT
|
|
14
14
|
|
|
15
15
|
One of the following must be set:
|
|
16
|
-
JABBER_ROOM — MUC room JID (e.g.
|
|
17
|
-
JABBER_TO — Direct message recipient JID (e.g.
|
|
16
|
+
JABBER_ROOM — MUC room JID (e.g. room@conference.example.internal)
|
|
17
|
+
JABBER_TO — Direct message recipient JID (e.g. user@example.internal)
|
|
18
18
|
|
|
19
19
|
Optional environment variables:
|
|
20
20
|
JABBER_PORT (defaults to 5222)
|
package/tools/grayrelease.js
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import {
|
|
11
11
|
SYSTEM_CODES,
|
|
12
|
+
getDeployConfig,
|
|
12
13
|
ENV_CODES,
|
|
13
14
|
SUPPORTED_ENVS,
|
|
14
15
|
DEPT_CODES,
|
|
@@ -45,7 +46,7 @@ import { handleSendJabberMessage } from './jabber.js';
|
|
|
45
46
|
* 簽核規則:
|
|
46
47
|
* - DEV:直接 Approve
|
|
47
48
|
* - STG:指派給當日 Release Manager,等待簽核
|
|
48
|
-
* - UAT
|
|
49
|
+
* - UAT:依 env/config 指派第一階段與最終簽核人
|
|
49
50
|
*
|
|
50
51
|
* Rebuild 規則:
|
|
51
52
|
* VERIFY 狀態只有在使用者明確要求 build/rebuild 時,才可透過 build_ticket({rebuild: true})
|
|
@@ -113,6 +114,21 @@ function progress(ctx, event) {
|
|
|
113
114
|
}
|
|
114
115
|
}
|
|
115
116
|
|
|
117
|
+
function getJabberJid(accountId) {
|
|
118
|
+
const domain = process.env.JABBER_DOMAIN ?? getDeployConfig().jabber?.domain;
|
|
119
|
+
return domain ? `${accountId}@${domain}` : accountId;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function getGrayReleaseUatApprovers() {
|
|
123
|
+
const config = getDeployConfig().release.grayReleaseUatApprovers ?? {};
|
|
124
|
+
return {
|
|
125
|
+
commentReviewerAlias:
|
|
126
|
+
process.env.GRAYRELEASE_UAT_COMMENT_REVIEWER_ALIAS ?? config.commentReviewerAlias,
|
|
127
|
+
finalApproverAlias:
|
|
128
|
+
process.env.GRAYRELEASE_UAT_FINAL_APPROVER_ALIAS ?? config.finalApproverAlias,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
116
132
|
function isPassingResult(value) {
|
|
117
133
|
return ['pass', 'passed', 'success', 'succeeded', 'done'].includes(
|
|
118
134
|
String(value ?? '').trim().toLowerCase(),
|
|
@@ -1110,7 +1126,7 @@ async function handleGrayReleaseApproval(issueKey, environment, systemCode, ctx)
|
|
|
1110
1126
|
await notifier.notify(issueKey, `已指派給 STG 值班組長 ${managerName}`);
|
|
1111
1127
|
|
|
1112
1128
|
// 發送 jabber 通知
|
|
1113
|
-
const jabberTo =
|
|
1129
|
+
const jabberTo = getJabberJid(accountId);
|
|
1114
1130
|
progress(ctx, {
|
|
1115
1131
|
phase: 'waiting',
|
|
1116
1132
|
title: '發送 GrayRelease 簽核通知',
|
|
@@ -1136,45 +1152,43 @@ async function handleGrayReleaseApproval(issueKey, environment, systemCode, ctx)
|
|
|
1136
1152
|
return { approved: true, by: managerName };
|
|
1137
1153
|
}
|
|
1138
1154
|
|
|
1139
|
-
// UAT: assign
|
|
1155
|
+
// UAT: assign first reviewer → 等待留言 → 轉 final approver → 等待 approve
|
|
1140
1156
|
if (env === 'uat') {
|
|
1141
|
-
const
|
|
1142
|
-
|
|
1143
|
-
|
|
1157
|
+
const {commentReviewerAlias, finalApproverAlias} = getGrayReleaseUatApprovers();
|
|
1158
|
+
const commentReviewerAccountId = resolveAccountId(commentReviewerAlias);
|
|
1159
|
+
if (!commentReviewerAccountId) {
|
|
1160
|
+
throw new Error(`找不到 UAT 第一階段簽核人 accountId,請設定 GRAYRELEASE_UAT_COMMENT_REVIEWER_ALIAS 或 release.grayReleaseUatApprovers.commentReviewerAlias`);
|
|
1144
1161
|
}
|
|
1145
1162
|
|
|
1146
|
-
// Assign 給 James Yu
|
|
1147
1163
|
progress(ctx, {
|
|
1148
1164
|
phase: 'action',
|
|
1149
1165
|
title: '指派 GrayRelease 簽核人',
|
|
1150
|
-
detail:
|
|
1166
|
+
detail: commentReviewerAlias,
|
|
1151
1167
|
issueKey,
|
|
1152
1168
|
});
|
|
1153
|
-
await jira.updateAssignee(issueKey,
|
|
1154
|
-
await notifier.notify(issueKey,
|
|
1169
|
+
await jira.updateAssignee(issueKey, commentReviewerAccountId);
|
|
1170
|
+
await notifier.notify(issueKey, `已指派給 ${commentReviewerAlias},等待留言確認`);
|
|
1155
1171
|
|
|
1156
|
-
|
|
1157
|
-
const jamesJabber = `${jamesAccountId}@linebank.com.tw`;
|
|
1172
|
+
const commentReviewerJabber = getJabberJid(commentReviewerAccountId);
|
|
1158
1173
|
progress(ctx, {
|
|
1159
1174
|
phase: 'waiting',
|
|
1160
1175
|
title: '發送 GrayRelease 簽核通知',
|
|
1161
|
-
detail: `to
|
|
1176
|
+
detail: `to ${commentReviewerAlias} (${commentReviewerJabber})`,
|
|
1162
1177
|
issueKey,
|
|
1163
1178
|
});
|
|
1164
1179
|
await handleSendJabberMessage(
|
|
1165
1180
|
{
|
|
1166
|
-
to:
|
|
1181
|
+
to: commentReviewerJabber,
|
|
1167
1182
|
message: `[GrayRelease 簽核通知] ${issueKey} 需要您的簽核並留言確認。環境: UAT,系統: ${systemCode}\n${process.env.JIRA_BASE_URL}/browse/${issueKey}`,
|
|
1168
1183
|
},
|
|
1169
1184
|
{},
|
|
1170
1185
|
);
|
|
1171
1186
|
|
|
1172
|
-
// 等待 James Yu 留言 "Approved"
|
|
1173
1187
|
const commentResult = await handleWaitForComment(
|
|
1174
1188
|
{
|
|
1175
1189
|
issueKey,
|
|
1176
1190
|
keyword: 'approved',
|
|
1177
|
-
authorAccountId:
|
|
1191
|
+
authorAccountId: commentReviewerAccountId,
|
|
1178
1192
|
timeoutMs: parseInt(process.env.POLL_TIMEOUT_MS ?? '3600000'),
|
|
1179
1193
|
intervalMs: parseInt(process.env.POLL_INTERVAL_MS ?? '30000'),
|
|
1180
1194
|
},
|
|
@@ -1183,43 +1197,40 @@ async function handleGrayReleaseApproval(issueKey, environment, systemCode, ctx)
|
|
|
1183
1197
|
const commentData = parseToolResult(commentResult);
|
|
1184
1198
|
|
|
1185
1199
|
if (!commentData?.found) {
|
|
1186
|
-
throw new Error(
|
|
1200
|
+
throw new Error(`等待 ${commentReviewerAlias} 留言超時`);
|
|
1187
1201
|
}
|
|
1188
1202
|
|
|
1189
|
-
await notifier.notify(issueKey,
|
|
1203
|
+
await notifier.notify(issueKey, `${commentReviewerAlias} 已留言確認: ${commentData.comment}`);
|
|
1190
1204
|
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
throw new Error('找不到 Solar Chen 的 accountId');
|
|
1205
|
+
const finalApproverAccountId = resolveAccountId(finalApproverAlias);
|
|
1206
|
+
if (!finalApproverAccountId) {
|
|
1207
|
+
throw new Error(`找不到 UAT 最終簽核人 accountId,請設定 GRAYRELEASE_UAT_FINAL_APPROVER_ALIAS 或 release.grayReleaseUatApprovers.finalApproverAlias`);
|
|
1195
1208
|
}
|
|
1196
1209
|
|
|
1197
1210
|
progress(ctx, {
|
|
1198
1211
|
phase: 'action',
|
|
1199
1212
|
title: '指派 GrayRelease 簽核人',
|
|
1200
|
-
detail:
|
|
1213
|
+
detail: finalApproverAlias,
|
|
1201
1214
|
issueKey,
|
|
1202
1215
|
});
|
|
1203
|
-
await jira.updateAssignee(issueKey,
|
|
1204
|
-
await notifier.notify(issueKey,
|
|
1216
|
+
await jira.updateAssignee(issueKey, finalApproverAccountId);
|
|
1217
|
+
await notifier.notify(issueKey, `已轉單給 ${finalApproverAlias},等待最終簽核`);
|
|
1205
1218
|
|
|
1206
|
-
|
|
1207
|
-
const solarJabber = `${solarAccountId}@linebank.com.tw`;
|
|
1219
|
+
const finalApproverJabber = getJabberJid(finalApproverAccountId);
|
|
1208
1220
|
progress(ctx, {
|
|
1209
1221
|
phase: 'waiting',
|
|
1210
1222
|
title: '發送 GrayRelease 簽核通知',
|
|
1211
|
-
detail: `to
|
|
1223
|
+
detail: `to ${finalApproverAlias} (${finalApproverJabber})`,
|
|
1212
1224
|
issueKey,
|
|
1213
1225
|
});
|
|
1214
1226
|
await handleSendJabberMessage(
|
|
1215
1227
|
{
|
|
1216
|
-
to:
|
|
1217
|
-
message: `[GrayRelease 簽核通知] ${issueKey} 已由
|
|
1228
|
+
to: finalApproverJabber,
|
|
1229
|
+
message: `[GrayRelease 簽核通知] ${issueKey} 已由 ${commentReviewerAlias} 確認,需要您的最終簽核。環境: UAT,系統: ${systemCode}\n${process.env.JIRA_BASE_URL}/browse/${issueKey}`,
|
|
1218
1230
|
},
|
|
1219
1231
|
{},
|
|
1220
1232
|
);
|
|
1221
1233
|
|
|
1222
|
-
// 輪詢等待 Solar Approve(狀態變為 WAIT DEPLOY)
|
|
1223
1234
|
const poller = new Poller(jira);
|
|
1224
1235
|
await poller.waitForStatus(issueKey, 'WAIT DEPLOY', {
|
|
1225
1236
|
intervalMs: parseInt(process.env.POLL_INTERVAL_MS ?? '30000'),
|
|
@@ -1227,7 +1238,7 @@ async function handleGrayReleaseApproval(issueKey, environment, systemCode, ctx)
|
|
|
1227
1238
|
onProgress: ctx.progress,
|
|
1228
1239
|
});
|
|
1229
1240
|
|
|
1230
|
-
return { approved: true, by:
|
|
1241
|
+
return { approved: true, by: `${commentReviewerAlias} → ${finalApproverAlias}` };
|
|
1231
1242
|
}
|
|
1232
1243
|
|
|
1233
1244
|
throw new Error(`不支援的環境: ${environment}`);
|
package/tools/helpers.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {SERVERS, SERVER_MODULE_MAP} from '../constants/index.js';
|
|
1
|
+
import {getDeployConfig, META_TEST_NODES, SERVERS, SERVER_MODULE_MAP} from '../constants/index.js';
|
|
2
2
|
|
|
3
3
|
export function ok(data) {
|
|
4
4
|
return {content: [{type: 'text', text: JSON.stringify(data, null, 2)}]};
|
|
@@ -31,8 +31,7 @@ export function getServerList(systemCode, env, metaTest, module) {
|
|
|
31
31
|
|
|
32
32
|
// metaTest 模式:ibk / cwa 只保留單一節點
|
|
33
33
|
if (metaTest) {
|
|
34
|
-
|
|
35
|
-
if (META_TEST_NODE[systemCode]) return [META_TEST_NODE[systemCode]];
|
|
34
|
+
if (META_TEST_NODES[systemCode]) return [META_TEST_NODES[systemCode]];
|
|
36
35
|
}
|
|
37
36
|
|
|
38
37
|
// 如果是嵌套結構 (EIB, EVT),依 SERVER_MODULE_MAP 選擇 was 或 web
|
|
@@ -51,27 +50,12 @@ export function getClusterList(systemCode, env, module) {
|
|
|
51
50
|
}
|
|
52
51
|
|
|
53
52
|
/**
|
|
54
|
-
* 依 systemCode + module + versionName 組出
|
|
55
|
-
* 例:getModuleName('IBK', 'ssr', '1.3.1.0') → 'IBK_ssr_1.3.1.0'
|
|
56
|
-
* getModuleName('IBK', 'ibk', '1.5.2.0') → 'IBK_1.5.2.0'
|
|
57
|
-
* getModuleName('BOF', 'web', '1.0.0.0') → 'COP_WEB_1.0.0.0'
|
|
53
|
+
* 依 systemCode + module + versionName 組出 release 版本名稱。
|
|
58
54
|
*/
|
|
59
55
|
export function getModuleName(systemCode, module, versionName) {
|
|
60
|
-
const
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
ibk: '',
|
|
64
|
-
ssr: 'ssr',
|
|
65
|
-
wealth: 'wealth',
|
|
66
|
-
a11y: 'accessibility',
|
|
67
|
-
web: 'WEB',
|
|
68
|
-
web_cust: 'CUST',
|
|
69
|
-
web_agnt: 'AGENT',
|
|
70
|
-
evt007: '',
|
|
71
|
-
evt009: '',
|
|
72
|
-
evt005: '',
|
|
73
|
-
nextjs_web: 'WEB',
|
|
74
|
-
};
|
|
56
|
+
const moduleNameConfig = getDeployConfig().moduleName;
|
|
57
|
+
const systemMap = moduleNameConfig.systemAliases ?? {};
|
|
58
|
+
const moduleMap = moduleNameConfig.moduleAliases ?? {};
|
|
75
59
|
return (
|
|
76
60
|
(systemMap[systemCode] || systemCode) +
|
|
77
61
|
'_' +
|
package/tools/index.js
CHANGED
|
@@ -89,7 +89,7 @@ export function getToolDefinitions() {
|
|
|
89
89
|
type: 'object',
|
|
90
90
|
required: ['issueKey', 'transitionName'],
|
|
91
91
|
properties: {
|
|
92
|
-
issueKey: { type: 'string', description: '
|
|
92
|
+
issueKey: { type: 'string', description: 'Jira issue key' },
|
|
93
93
|
transitionName: {
|
|
94
94
|
type: 'string',
|
|
95
95
|
description: '狀態名稱,例如 "Pending Approval"、"Approved"、"In Progress"、"Done"',
|
|
@@ -164,8 +164,8 @@ export function getToolDefinitions() {
|
|
|
164
164
|
type: 'object',
|
|
165
165
|
required: ['inwardKey', 'outwardKey'],
|
|
166
166
|
properties: {
|
|
167
|
-
inwardKey: { type: 'string', description: '被包含方 issue key
|
|
168
|
-
outwardKey: { type: 'string', description: '包含方 issue key
|
|
167
|
+
inwardKey: { type: 'string', description: '被包含方 issue key' },
|
|
168
|
+
outwardKey: { type: 'string', description: '包含方 issue key' },
|
|
169
169
|
linkType: {
|
|
170
170
|
type: 'string',
|
|
171
171
|
description:
|
|
@@ -185,7 +185,7 @@ export function getToolDefinitions() {
|
|
|
185
185
|
properties: {
|
|
186
186
|
issueKey: {
|
|
187
187
|
type: 'string',
|
|
188
|
-
description: '要 build 的 issue key,例如 CI
|
|
188
|
+
description: '要 build 的 issue key,例如 CI、Library 或 GrayRelease 單',
|
|
189
189
|
},
|
|
190
190
|
rebuild: {
|
|
191
191
|
type: 'boolean',
|
|
@@ -204,7 +204,7 @@ export function getToolDefinitions() {
|
|
|
204
204
|
properties: {
|
|
205
205
|
issueKey: {
|
|
206
206
|
type: 'string',
|
|
207
|
-
description: 'CI issue key
|
|
207
|
+
description: 'CI issue key',
|
|
208
208
|
},
|
|
209
209
|
},
|
|
210
210
|
},
|
|
@@ -219,7 +219,7 @@ export function getToolDefinitions() {
|
|
|
219
219
|
properties: {
|
|
220
220
|
issueKey: {
|
|
221
221
|
type: 'string',
|
|
222
|
-
description: 'CI issue key
|
|
222
|
+
description: 'CI issue key',
|
|
223
223
|
},
|
|
224
224
|
},
|
|
225
225
|
},
|
|
@@ -234,7 +234,7 @@ export function getToolDefinitions() {
|
|
|
234
234
|
properties: {
|
|
235
235
|
cdIssueKey: {
|
|
236
236
|
type: 'string',
|
|
237
|
-
description: 'CD 單 issue key
|
|
237
|
+
description: 'CD 單 issue key',
|
|
238
238
|
},
|
|
239
239
|
environment: {
|
|
240
240
|
type: 'string',
|
|
@@ -258,7 +258,7 @@ export function getToolDefinitions() {
|
|
|
258
258
|
properties: {
|
|
259
259
|
issueKey: {
|
|
260
260
|
type: 'string',
|
|
261
|
-
description: 'CD 單 issue key
|
|
261
|
+
description: 'CD 單 issue key',
|
|
262
262
|
},
|
|
263
263
|
environment: {
|
|
264
264
|
type: 'string',
|
|
@@ -271,14 +271,14 @@ export function getToolDefinitions() {
|
|
|
271
271
|
{
|
|
272
272
|
name: 'cancel_release',
|
|
273
273
|
description:
|
|
274
|
-
'取消目前進行中的 CI 單,並回傳關聯的 Library 模組清單(含 gitBranch)與其他關聯 CI 單,供後續 reroll
|
|
274
|
+
'取消目前進行中的 CI 單,並回傳關聯的 Library 模組清單(含 gitBranch)與其他關聯 CI 單,供後續 reroll 流程使用。',
|
|
275
275
|
inputSchema: {
|
|
276
276
|
type: 'object',
|
|
277
277
|
required: ['systemCode'],
|
|
278
278
|
properties: {
|
|
279
279
|
systemCode: {
|
|
280
280
|
type: 'string',
|
|
281
|
-
description: '
|
|
281
|
+
description: '系統代碼',
|
|
282
282
|
},
|
|
283
283
|
ciIssueKey: {
|
|
284
284
|
type: 'string',
|
|
@@ -290,14 +290,14 @@ export function getToolDefinitions() {
|
|
|
290
290
|
{
|
|
291
291
|
name: 'get_release_status',
|
|
292
292
|
description:
|
|
293
|
-
'查詢指定系統代號的 Release 現況。以最新 CI 單為錨點,列出關聯的 Library 單、CD
|
|
293
|
+
'查詢指定系統代號的 Release 現況。以最新 CI 單為錨點,列出關聯的 Library 單、CD 單狀態與建議下一步。',
|
|
294
294
|
inputSchema: {
|
|
295
295
|
type: 'object',
|
|
296
296
|
required: ['systemCode'],
|
|
297
297
|
properties: {
|
|
298
298
|
systemCode: {
|
|
299
299
|
type: 'string',
|
|
300
|
-
description: '
|
|
300
|
+
description: '系統代碼',
|
|
301
301
|
},
|
|
302
302
|
},
|
|
303
303
|
},
|
|
@@ -305,22 +305,22 @@ export function getToolDefinitions() {
|
|
|
305
305
|
{
|
|
306
306
|
name: 'update_assignee',
|
|
307
307
|
description:
|
|
308
|
-
'更新 Jira issue 的 Assignee
|
|
308
|
+
'更新 Jira issue 的 Assignee。支援直接輸入 displayName(會從 env/config 查找 accountId)或直接指定 accountId。',
|
|
309
309
|
inputSchema: {
|
|
310
310
|
type: 'object',
|
|
311
311
|
required: ['issueKey'],
|
|
312
312
|
properties: {
|
|
313
313
|
issueKey: {
|
|
314
314
|
type: 'string',
|
|
315
|
-
description: '要更新的 issue key
|
|
315
|
+
description: '要更新的 issue key',
|
|
316
316
|
},
|
|
317
317
|
accountId: {
|
|
318
318
|
type: 'string',
|
|
319
|
-
description: 'Jira 用戶 accountId
|
|
319
|
+
description: 'Jira 用戶 accountId(與 displayName 擇一填寫即可)',
|
|
320
320
|
},
|
|
321
321
|
displayName: {
|
|
322
322
|
type: 'string',
|
|
323
|
-
description: '
|
|
323
|
+
description: '用戶顯示名稱或暱稱(系統會自動查找對應的 accountId)',
|
|
324
324
|
},
|
|
325
325
|
},
|
|
326
326
|
},
|
package/tools/jabber.js
CHANGED
|
@@ -55,7 +55,7 @@ export function getJabberToolDefinitions() {
|
|
|
55
55
|
{
|
|
56
56
|
name: 'send_jabber_message',
|
|
57
57
|
description:
|
|
58
|
-
'發送訊息到 Jabber。可傳給個人(to
|
|
58
|
+
'發送訊息到 Jabber。可傳給個人(to 參數使用完整 JID)或 MUC 群組(room 參數)。' +
|
|
59
59
|
'若 to/room 都未提供,則使用環境變數 JABBER_TO 或 JABBER_ROOM。' +
|
|
60
60
|
'用於上版通知、請主管簽單、上版完成公告等。',
|
|
61
61
|
inputSchema: {
|
|
@@ -68,11 +68,11 @@ export function getJabberToolDefinitions() {
|
|
|
68
68
|
},
|
|
69
69
|
to: {
|
|
70
70
|
type: 'string',
|
|
71
|
-
description: '
|
|
71
|
+
description: '直接傳給個人的完整 JID,例如 user@example.internal',
|
|
72
72
|
},
|
|
73
73
|
room: {
|
|
74
74
|
type: 'string',
|
|
75
|
-
description: 'MUC 群組 JID,例如
|
|
75
|
+
description: 'MUC 群組 JID,例如 room@conference.example.internal',
|
|
76
76
|
},
|
|
77
77
|
},
|
|
78
78
|
},
|