@jira-deploy/core 1.0.7 → 1.0.9
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/package.json +1 -1
- package/tools/grayrelease.js +12 -26
- package/tools.test.js +22 -17
package/package.json
CHANGED
package/tools/grayrelease.js
CHANGED
|
@@ -132,7 +132,7 @@ async function findTransitionByName(issueKey, transitionName, jira) {
|
|
|
132
132
|
));
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
-
async function waitForGrayReleaseResult(issueKey, fieldId, label, jira, onProgress = () => {}) {
|
|
135
|
+
async function waitForGrayReleaseResult(issueKey, fieldId, label, jira, onProgress = () => { }) {
|
|
136
136
|
const intervalMs = getPollIntervalMs();
|
|
137
137
|
const timeoutMs = getPollTimeoutMs();
|
|
138
138
|
const startedAt = Date.now();
|
|
@@ -740,6 +740,9 @@ async function executeGrayReleaseDeployFlow(issueKey, ctx) {
|
|
|
740
740
|
}
|
|
741
741
|
|
|
742
742
|
log.push(`🚀 開始執行 GrayRelease Deploy - 環境: ${environment.toUpperCase()}`);
|
|
743
|
+
log.push(' 確保每次都會從 build 開始,先執行: Planning');
|
|
744
|
+
await jira.transitionByName(issueKey, 'Planning');
|
|
745
|
+
await notifier.notify(issueKey, '確保每次都會從 Planning 開始,準備進入部署流程');
|
|
743
746
|
|
|
744
747
|
while (true) {
|
|
745
748
|
const issue = await jira.getIssue(issueKey);
|
|
@@ -983,7 +986,7 @@ async function executeAutoGrayReleaseFlow(issueKey, options, ctx) {
|
|
|
983
986
|
|
|
984
987
|
async function runGrayReleaseDeployStep(issueKey, systemCode, ctx, log) {
|
|
985
988
|
const { jira, notifier } = ctx;
|
|
986
|
-
const needSwitch = await needSwitchExecutionNode(issueKey, systemCode, jira);
|
|
989
|
+
const needSwitch = await needSwitchExecutionNode(issueKey, systemCode, jira, log);
|
|
987
990
|
|
|
988
991
|
if (needSwitch) {
|
|
989
992
|
log.push(' 執行: Switch Execution Node');
|
|
@@ -1011,7 +1014,7 @@ async function runGrayReleaseDeployStep(issueKey, systemCode, ctx, log) {
|
|
|
1011
1014
|
// 若超時則回傳「部署中」訊息,請使用者稍後繼續。
|
|
1012
1015
|
// 每 3 分鐘 輪詢一次
|
|
1013
1016
|
const DEPLOY_WAIT_MS = Math.min(getPollTimeoutMs(), 10 * 60 * 1000);
|
|
1014
|
-
const DEPLOY_POLL_MS = Math.min(getPollIntervalMs(),
|
|
1017
|
+
const DEPLOY_POLL_MS = Math.min(getPollIntervalMs(), 3 * 60 * 1000);
|
|
1015
1018
|
const deployDeadline = Date.now() + DEPLOY_WAIT_MS;
|
|
1016
1019
|
let deployCompleted = false;
|
|
1017
1020
|
let attempts = 0;
|
|
@@ -1048,21 +1051,6 @@ async function runGrayReleaseDeployStep(issueKey, systemCode, ctx, log) {
|
|
|
1048
1051
|
throw new Error(`GrayRelease Deploy 失敗,${GRAY_RELEASE_FIELD_IDS.deployResult}: ${deployResult}`);
|
|
1049
1052
|
}
|
|
1050
1053
|
|
|
1051
|
-
const transitions = await jira.getTransitions?.(issueKey) ?? [];
|
|
1052
|
-
const toVerifyTrans = transitions.find(
|
|
1053
|
-
(t) => t.name.toLowerCase() === 'to verify',
|
|
1054
|
-
);
|
|
1055
|
-
if (isPassingResult(deployResult) || toVerifyTrans) {
|
|
1056
|
-
log.push(' ✅ Deploy 完成,執行: To Verify');
|
|
1057
|
-
if (toVerifyTrans?.id && jira.transitionById) {
|
|
1058
|
-
await jira.transitionById(issueKey, toVerifyTrans.id);
|
|
1059
|
-
} else {
|
|
1060
|
-
await jira.transitionByName(issueKey, 'To Verify');
|
|
1061
|
-
}
|
|
1062
|
-
await notifier.notify(issueKey, 'Deploy 完成,進入驗證階段');
|
|
1063
|
-
deployCompleted = true;
|
|
1064
|
-
break;
|
|
1065
|
-
}
|
|
1066
1054
|
|
|
1067
1055
|
if (Date.now() >= deployDeadline) {
|
|
1068
1056
|
break;
|
|
@@ -1132,7 +1120,7 @@ async function handleGrayReleaseApproval(issueKey, environment, systemCode, ctx)
|
|
|
1132
1120
|
await handleSendJabberMessage(
|
|
1133
1121
|
{
|
|
1134
1122
|
to: jabberTo,
|
|
1135
|
-
message: `[GrayRelease 簽核通知] ${issueKey} 需要您的簽核。環境: STG\n${process.env.JIRA_BASE_URL}/browse/${issueKey}`,
|
|
1123
|
+
message: `[GrayRelease 簽核通知] ${issueKey} 需要您的簽核。環境: STG,系統: ${systemCode}\n${process.env.JIRA_BASE_URL}/browse/${issueKey}`,
|
|
1136
1124
|
},
|
|
1137
1125
|
{},
|
|
1138
1126
|
);
|
|
@@ -1176,7 +1164,7 @@ async function handleGrayReleaseApproval(issueKey, environment, systemCode, ctx)
|
|
|
1176
1164
|
await handleSendJabberMessage(
|
|
1177
1165
|
{
|
|
1178
1166
|
to: jamesJabber,
|
|
1179
|
-
message: `[GrayRelease 簽核通知] ${issueKey} 需要您的簽核並留言確認。環境: UAT\n${process.env.JIRA_BASE_URL}/browse/${issueKey}`,
|
|
1167
|
+
message: `[GrayRelease 簽核通知] ${issueKey} 需要您的簽核並留言確認。環境: UAT,系統: ${systemCode}\n${process.env.JIRA_BASE_URL}/browse/${issueKey}`,
|
|
1180
1168
|
},
|
|
1181
1169
|
{},
|
|
1182
1170
|
);
|
|
@@ -1226,7 +1214,7 @@ async function handleGrayReleaseApproval(issueKey, environment, systemCode, ctx)
|
|
|
1226
1214
|
await handleSendJabberMessage(
|
|
1227
1215
|
{
|
|
1228
1216
|
to: solarJabber,
|
|
1229
|
-
message: `[GrayRelease 簽核通知] ${issueKey} 已由 James Yu 確認,需要您的最終簽核。環境: UAT\n${process.env.JIRA_BASE_URL}/browse/${issueKey}`,
|
|
1217
|
+
message: `[GrayRelease 簽核通知] ${issueKey} 已由 James Yu 確認,需要您的最終簽核。環境: UAT,系統: ${systemCode}\n${process.env.JIRA_BASE_URL}/browse/${issueKey}`,
|
|
1230
1218
|
},
|
|
1231
1219
|
{},
|
|
1232
1220
|
);
|
|
@@ -1249,7 +1237,7 @@ async function handleGrayReleaseApproval(issueKey, environment, systemCode, ctx)
|
|
|
1249
1237
|
* 判斷是否需要執行 Switch Execution Node
|
|
1250
1238
|
* 規則:查詢同系統上次 CD or GrayRelease 部署的環境群組,若與本次不同則需切換
|
|
1251
1239
|
*/
|
|
1252
|
-
async function needSwitchExecutionNode(issueKey, systemCode, jira) {
|
|
1240
|
+
async function needSwitchExecutionNode(issueKey, systemCode, jira, log) {
|
|
1253
1241
|
if (await hasRecentSwitchExecutionNodeComment(issueKey, systemCode, jira)) {
|
|
1254
1242
|
return false;
|
|
1255
1243
|
}
|
|
@@ -1263,10 +1251,11 @@ async function needSwitchExecutionNode(issueKey, systemCode, jira) {
|
|
|
1263
1251
|
const jql_cd = `project = CID AND (issuetype = CD OR issuetype = GrayRelease) AND text ~ "${systemCode}" AND status = Done AND issueKey != "${issueKey}" ORDER BY updated DESC`;
|
|
1264
1252
|
|
|
1265
1253
|
try {
|
|
1254
|
+
log.push(` before 查詢歷史 CD/GrayRelease 單,判斷是否需要切換 Execution Node`);
|
|
1266
1255
|
const cdResults = await jira.searchIssues(jql_cd, ['customfield_13436', 'updated'], 1);
|
|
1256
|
+
log.push(` after 查詢歷史 CD/GrayRelease 單,判斷是否需要切換 Execution Node`, cdResults);
|
|
1267
1257
|
const cdIssue = cdResults[0] ?? null;
|
|
1268
1258
|
|
|
1269
|
-
|
|
1270
1259
|
// 查不到歷史 → 保守執行 Switch
|
|
1271
1260
|
if (!cdIssue) {
|
|
1272
1261
|
return true;
|
|
@@ -1290,9 +1279,6 @@ async function waitForSwitchExecutionNode(issueKey, systemCode, jira) {
|
|
|
1290
1279
|
const deadline = Date.now() + waitMs;
|
|
1291
1280
|
|
|
1292
1281
|
while (true) {
|
|
1293
|
-
if (await hasRecentSwitchExecutionNodeComment(issueKey, systemCode, jira)) {
|
|
1294
|
-
return;
|
|
1295
|
-
}
|
|
1296
1282
|
|
|
1297
1283
|
if (Date.now() >= deadline) {
|
|
1298
1284
|
return;
|
package/tools.test.js
CHANGED
|
@@ -1634,7 +1634,7 @@ describe('auto_grayrelease — approval payload handling', () => {
|
|
|
1634
1634
|
searchIssues: async () => [{ fields: { customfield_13436: { value: 'stg' } } }],
|
|
1635
1635
|
transitionByName: async (issueKey, transitionName) => {
|
|
1636
1636
|
calls.transitionByName.push({ issueKey, transitionName });
|
|
1637
|
-
if (transitionName === '
|
|
1637
|
+
if (transitionName === 'GrayRelease Deploy') {
|
|
1638
1638
|
status = 'VERIFY';
|
|
1639
1639
|
}
|
|
1640
1640
|
return { transitioned: transitionName, toStatus: status };
|
|
@@ -1810,7 +1810,7 @@ describe('auto_grayrelease — build/deploy result fields', () => {
|
|
|
1810
1810
|
assert.deepEqual(output.nextSteps, ['GrayRelease Build']);
|
|
1811
1811
|
});
|
|
1812
1812
|
|
|
1813
|
-
test('deploy_grayrelease
|
|
1813
|
+
test('deploy_grayrelease restarts from Planning and advances via approval without rebuilding', async () => {
|
|
1814
1814
|
useFastPolling();
|
|
1815
1815
|
let status = 'Wait for Build';
|
|
1816
1816
|
const transitions = [];
|
|
@@ -1837,9 +1837,11 @@ describe('auto_grayrelease — build/deploy result fields', () => {
|
|
|
1837
1837
|
searchIssues: async () => [{ fields: { customfield_13436: { value: 'stg' } } }],
|
|
1838
1838
|
transitionByName: async (_issueKey, transitionName) => {
|
|
1839
1839
|
transitions.push(transitionName);
|
|
1840
|
+
if (transitionName === 'Planning') status = 'Planning';
|
|
1841
|
+
if (transitionName === 'Accept') status = 'Wait for Build';
|
|
1840
1842
|
if (transitionName === 'Apply to approval') status = 'Wait Approval';
|
|
1841
1843
|
if (transitionName === 'Approve') status = 'Wait Deploy';
|
|
1842
|
-
if (transitionName === '
|
|
1844
|
+
if (transitionName === 'GrayRelease Deploy') status = 'VERIFY';
|
|
1843
1845
|
},
|
|
1844
1846
|
};
|
|
1845
1847
|
|
|
@@ -1854,17 +1856,18 @@ describe('auto_grayrelease — build/deploy result fields', () => {
|
|
|
1854
1856
|
const output = JSON.parse(result.content[0].text);
|
|
1855
1857
|
assert.equal(output.finalStatus, 'VERIFY');
|
|
1856
1858
|
assert.deepEqual(transitions, [
|
|
1859
|
+
'Planning',
|
|
1860
|
+
'Accept',
|
|
1857
1861
|
'Apply to approval',
|
|
1858
1862
|
'Approve',
|
|
1859
1863
|
'GrayRelease Deploy',
|
|
1860
|
-
'To Verify',
|
|
1861
1864
|
]);
|
|
1862
1865
|
} finally {
|
|
1863
1866
|
restoreEnv();
|
|
1864
1867
|
}
|
|
1865
1868
|
});
|
|
1866
1869
|
|
|
1867
|
-
test('deploy_grayrelease
|
|
1870
|
+
test('deploy_grayrelease keeps rc deploy behavior by resetting from VERIFY to Planning first', async () => {
|
|
1868
1871
|
const transitions = [];
|
|
1869
1872
|
const jira = {
|
|
1870
1873
|
getIssueFields: async () => ({
|
|
@@ -1886,8 +1889,8 @@ describe('auto_grayrelease — build/deploy result fields', () => {
|
|
|
1886
1889
|
assert.ok(!result.isError, result.content[0].text);
|
|
1887
1890
|
const output = JSON.parse(result.content[0].text);
|
|
1888
1891
|
assert.equal(output.finalStatus, 'VERIFY');
|
|
1889
|
-
assert.deepEqual(transitions, []);
|
|
1890
|
-
assert.match(output.log.join('\n'),
|
|
1892
|
+
assert.deepEqual(transitions, ['Planning']);
|
|
1893
|
+
assert.match(output.log.join('\n'), /確保每次都會從 build 開始/);
|
|
1891
1894
|
});
|
|
1892
1895
|
|
|
1893
1896
|
test('Build waits until customfield_13432 becomes pass before Apply to approval', async () => {
|
|
@@ -1936,7 +1939,7 @@ describe('auto_grayrelease — build/deploy result fields', () => {
|
|
|
1936
1939
|
}
|
|
1937
1940
|
});
|
|
1938
1941
|
|
|
1939
|
-
test('Deploy waits until
|
|
1942
|
+
test('Deploy waits until cid jira worker moves status to VERIFY with pass result', async () => {
|
|
1940
1943
|
useFastPolling();
|
|
1941
1944
|
let status = 'WAIT DEPLOY';
|
|
1942
1945
|
let deployResultCalls = 0;
|
|
@@ -1952,13 +1955,15 @@ describe('auto_grayrelease — build/deploy result fields', () => {
|
|
|
1952
1955
|
customfield_13443: { value: 'CWA' },
|
|
1953
1956
|
};
|
|
1954
1957
|
},
|
|
1955
|
-
getIssue: async () => ({
|
|
1958
|
+
getIssue: async () => ({
|
|
1959
|
+
fields: {
|
|
1960
|
+
status: { name: deployResultCalls >= 2 ? 'VERIFY' : status },
|
|
1961
|
+
summary: 'GrayRelease',
|
|
1962
|
+
},
|
|
1963
|
+
}),
|
|
1956
1964
|
searchIssues: async () => [{ fields: { customfield_13436: { value: 'stg' } } }],
|
|
1957
1965
|
transitionByName: async (_issueKey, transitionName) => {
|
|
1958
1966
|
transitions.push(transitionName);
|
|
1959
|
-
if (transitionName === 'To Verify') {
|
|
1960
|
-
status = 'VERIFY';
|
|
1961
|
-
}
|
|
1962
1967
|
},
|
|
1963
1968
|
};
|
|
1964
1969
|
|
|
@@ -1971,7 +1976,7 @@ describe('auto_grayrelease — build/deploy result fields', () => {
|
|
|
1971
1976
|
|
|
1972
1977
|
assert.ok(!result.content[0].text.startsWith('❌'), 'Deploy flow should succeed');
|
|
1973
1978
|
assert.equal(deployResultCalls, 3);
|
|
1974
|
-
assert.deepEqual(transitions, ['GrayRelease Deploy'
|
|
1979
|
+
assert.deepEqual(transitions, ['GrayRelease Deploy']);
|
|
1975
1980
|
} finally {
|
|
1976
1981
|
restoreEnv();
|
|
1977
1982
|
}
|
|
@@ -2002,7 +2007,7 @@ describe('auto_grayrelease — build/deploy result fields', () => {
|
|
|
2002
2007
|
searchIssues: async () => [{ fields: { customfield_13436: { value: 'prd' } } }],
|
|
2003
2008
|
transitionByName: async (_issueKey, transitionName) => {
|
|
2004
2009
|
transitions.push(transitionName);
|
|
2005
|
-
if (transitionName === '
|
|
2010
|
+
if (transitionName === 'GrayRelease Deploy') {
|
|
2006
2011
|
status = 'VERIFY';
|
|
2007
2012
|
}
|
|
2008
2013
|
},
|
|
@@ -2016,7 +2021,7 @@ describe('auto_grayrelease — build/deploy result fields', () => {
|
|
|
2016
2021
|
);
|
|
2017
2022
|
|
|
2018
2023
|
assert.ok(!result.content[0].text.startsWith('❌'), 'Deploy flow should succeed');
|
|
2019
|
-
assert.deepEqual(transitions, ['GrayRelease Deploy'
|
|
2024
|
+
assert.deepEqual(transitions, ['GrayRelease Deploy']);
|
|
2020
2025
|
} finally {
|
|
2021
2026
|
restoreEnv();
|
|
2022
2027
|
}
|
|
@@ -2058,7 +2063,7 @@ describe('auto_grayrelease — build/deploy result fields', () => {
|
|
|
2058
2063
|
},
|
|
2059
2064
|
transitionByName: async (_issueKey, transitionName) => {
|
|
2060
2065
|
transitions.push(transitionName);
|
|
2061
|
-
if (transitionName === '
|
|
2066
|
+
if (transitionName === 'GrayRelease Deploy') {
|
|
2062
2067
|
status = 'VERIFY';
|
|
2063
2068
|
}
|
|
2064
2069
|
},
|
|
@@ -2073,7 +2078,7 @@ describe('auto_grayrelease — build/deploy result fields', () => {
|
|
|
2073
2078
|
|
|
2074
2079
|
assert.ok(!result.content[0].text.startsWith('❌'), 'Deploy flow should succeed');
|
|
2075
2080
|
assert.equal(searchIssuesCalls, 1);
|
|
2076
|
-
assert.deepEqual(transitions, ['Switch Execution Node', 'GrayRelease Deploy'
|
|
2081
|
+
assert.deepEqual(transitions, ['Switch Execution Node', 'GrayRelease Deploy']);
|
|
2077
2082
|
} finally {
|
|
2078
2083
|
restoreEnv();
|
|
2079
2084
|
}
|