@tutti-os/nexight-devops 0.5.3 → 0.6.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/README.md +7 -20
- package/dist/cli.js +717 -1640
- package/dist/cli.js.map +1 -1
- package/package.json +3 -3
package/dist/cli.js
CHANGED
|
@@ -213,7 +213,7 @@ var tuttiComputerPath = (computerId) => computerId ? `${TUTTI_COMPUTER_API_BASE_
|
|
|
213
213
|
var runtimeLogRecallCollectionPath = (computerId) => `${tuttiComputerPath(computerId)}/runtime-log-recalls`;
|
|
214
214
|
var runtimeLogRecallPath = (computerId, taskId) => `${runtimeLogRecallCollectionPath(computerId)}/${encodeURIComponent(taskId)}`;
|
|
215
215
|
var runtimeLogRecallDownloadPath = (computerId, taskId) => `${runtimeLogRecallPath(computerId, taskId)}:create-download-url`;
|
|
216
|
-
var appendQuery = (
|
|
216
|
+
var appendQuery = (path, values) => {
|
|
217
217
|
const query = new URLSearchParams();
|
|
218
218
|
for (const [key, value] of Object.entries(values)) {
|
|
219
219
|
if (value !== void 0 && value !== "") {
|
|
@@ -221,7 +221,7 @@ var appendQuery = (path2, values) => {
|
|
|
221
221
|
}
|
|
222
222
|
}
|
|
223
223
|
const encoded = query.toString();
|
|
224
|
-
return encoded ? `${
|
|
224
|
+
return encoded ? `${path}?${encoded}` : path;
|
|
225
225
|
};
|
|
226
226
|
var defaultSleep = async (durationMs) => new Promise((resolveSleep) => {
|
|
227
227
|
setTimeout(resolveSleep, durationMs);
|
|
@@ -357,7 +357,7 @@ var AdminApi = class {
|
|
|
357
357
|
this.runtime = runtime;
|
|
358
358
|
this.fetchImpl = fetchImpl;
|
|
359
359
|
}
|
|
360
|
-
async request(method,
|
|
360
|
+
async request(method, path, body) {
|
|
361
361
|
const headers = {
|
|
362
362
|
accept: "application/json"
|
|
363
363
|
};
|
|
@@ -369,7 +369,7 @@ var AdminApi = class {
|
|
|
369
369
|
headers["content-type"] = "application/json";
|
|
370
370
|
payload = JSON.stringify(body);
|
|
371
371
|
}
|
|
372
|
-
const response = await this.fetchImpl(`${this.runtime.apiOrigin}${
|
|
372
|
+
const response = await this.fetchImpl(`${this.runtime.apiOrigin}${path}`, {
|
|
373
373
|
method,
|
|
374
374
|
headers,
|
|
375
375
|
body: payload
|
|
@@ -385,11 +385,11 @@ var AdminApi = class {
|
|
|
385
385
|
}
|
|
386
386
|
return parsed;
|
|
387
387
|
}
|
|
388
|
-
get(
|
|
389
|
-
return this.request("GET",
|
|
388
|
+
get(path) {
|
|
389
|
+
return this.request("GET", path);
|
|
390
390
|
}
|
|
391
|
-
post(
|
|
392
|
-
return this.request("POST",
|
|
391
|
+
post(path, body) {
|
|
392
|
+
return this.request("POST", path, body);
|
|
393
393
|
}
|
|
394
394
|
inspectToken(token) {
|
|
395
395
|
return this.post(ADMIN_TOKEN_INSPECT_PATH, token ? { token_value: token } : {});
|
|
@@ -1616,8 +1616,8 @@ var createDataFinderSdkTransport = (options) => {
|
|
|
1616
1616
|
options.baseUrl
|
|
1617
1617
|
);
|
|
1618
1618
|
return {
|
|
1619
|
-
request: async (
|
|
1620
|
-
const response = await client.dataFinder(
|
|
1619
|
+
request: async (path, requestOptions) => {
|
|
1620
|
+
const response = await client.dataFinder(path, {
|
|
1621
1621
|
...requestOptions,
|
|
1622
1622
|
...requestOptions.body === void 0 ? {} : {
|
|
1623
1623
|
body: typeof requestOptions.body === "string" ? requestOptions.body : JSON.stringify(requestOptions.body)
|
|
@@ -1666,11 +1666,11 @@ var DataFinderClient = class {
|
|
|
1666
1666
|
get pathScope() {
|
|
1667
1667
|
return this.publicConfig.pathMode === "project" ? `projects/${this.publicConfig.projectId}` : this.publicConfig.appId;
|
|
1668
1668
|
}
|
|
1669
|
-
async request(
|
|
1670
|
-
if (!
|
|
1669
|
+
async request(path, options = {}, label = `DataFinder request ${path}`) {
|
|
1670
|
+
if (!path.startsWith("/openapi/")) {
|
|
1671
1671
|
throw new Error("DataFinder request path must start with /openapi/");
|
|
1672
1672
|
}
|
|
1673
|
-
const response = await this.transport.request(
|
|
1673
|
+
const response = await this.transport.request(path, options);
|
|
1674
1674
|
let payload;
|
|
1675
1675
|
try {
|
|
1676
1676
|
payload = JSON.parse(response.text);
|
|
@@ -2082,8 +2082,198 @@ var registerDataFinderCommand = (program) => {
|
|
|
2082
2082
|
registerDataFinderQueryCommand(datafinder);
|
|
2083
2083
|
};
|
|
2084
2084
|
|
|
2085
|
-
// src/
|
|
2086
|
-
|
|
2085
|
+
// src/commands/doctor.ts
|
|
2086
|
+
var registerDoctorCommand = (program) => {
|
|
2087
|
+
program.command("doctor").description("Run a local readiness check for the CLI shell").action(() => {
|
|
2088
|
+
logger.step("Running basic readiness checks");
|
|
2089
|
+
logger.success("nexight-devops is ready");
|
|
2090
|
+
});
|
|
2091
|
+
};
|
|
2092
|
+
|
|
2093
|
+
// src/lib/datafinder/types.ts
|
|
2094
|
+
var DEFAULT_DATAFINDER_BASE_URL = "https://analytics.volcengineapi.com";
|
|
2095
|
+
var DEFAULT_DATAFINDER_TIMEZONE = "Asia/Shanghai";
|
|
2096
|
+
|
|
2097
|
+
// src/locales/zh-CN.ts
|
|
2098
|
+
var zhCN = {
|
|
2099
|
+
common: {
|
|
2100
|
+
continue: "\u7EE7\u7EED",
|
|
2101
|
+
skip: "\u8DF3\u8FC7"
|
|
2102
|
+
},
|
|
2103
|
+
errors: {
|
|
2104
|
+
missingAws: "\u672A\u68C0\u6D4B\u5230 aws\uFF0C\u8BF7\u5148\u5B8C\u6210 AWS CLI \u5B89\u88C5\u3002",
|
|
2105
|
+
missingGh: "\u672A\u68C0\u6D4B\u5230 gh\uFF0C\u8BF7\u5148\u5B8C\u6210 GitHub CLI \u5B89\u88C5\u3002"
|
|
2106
|
+
},
|
|
2107
|
+
onboard: {
|
|
2108
|
+
adminLoginSucceeded: "\u7BA1\u7406\u540E\u53F0\u767B\u5F55\u6210\u529F",
|
|
2109
|
+
adminSection: "\u7BA1\u7406\u540E\u53F0\u767B\u5F55\u8BBE\u7F6E",
|
|
2110
|
+
adminTokenRequired: "\u7BA1\u7406\u540E\u53F0 Token \u4E0D\u80FD\u4E3A\u7A7A",
|
|
2111
|
+
awsSection: "AWS CLI \u8BBE\u7F6E",
|
|
2112
|
+
complete: "Onboard \u5B8C\u6210",
|
|
2113
|
+
chooseAwsProfile: "\u8BF7\u9009\u62E9\u9ED8\u8BA4 AWS Profile",
|
|
2114
|
+
chooseLocale: "\u8BF7\u9009\u62E9\u547D\u4EE4\u884C\u8BED\u8A00",
|
|
2115
|
+
datafinderAppPathMode: "\u5E94\u7528\u8DEF\u5F84\uFF08\u4F20\u7EDF SaaS / \u79C1\u6709\u5316\uFF09",
|
|
2116
|
+
datafinderCredentialAction: "\u5DF2\u914D\u7F6E DataFinder \u51ED\u8BC1\uFF0C\u8BF7\u9009\u62E9\u5904\u7406\u65B9\u5F0F",
|
|
2117
|
+
datafinderKeepCredentials: "\u4FDD\u7559\u73B0\u6709\u51ED\u8BC1",
|
|
2118
|
+
datafinderProjectPathMode: "\u9879\u76EE\u8DEF\u5F84\uFF08SaaS \u4E91\u539F\u751F\uFF0C\u63A8\u8350\uFF09",
|
|
2119
|
+
datafinderReplaceCredentials: "\u91CD\u65B0\u8F93\u5165\u51ED\u8BC1",
|
|
2120
|
+
datafinderSection: "DataFinder OpenAPI \u8BBE\u7F6E",
|
|
2121
|
+
datafinderSucceeded: "DataFinder \u8FDE\u63A5\u6210\u529F\uFF1A{dashboardName}\uFF0C\u5171 {reportCount} \u4E2A\u62A5\u8868",
|
|
2122
|
+
enterGhToken: "\u8BF7\u8F93\u5165 GitHub Token",
|
|
2123
|
+
enterAdminToken: "\u8BF7\u8F93\u5165 Tutti \u7BA1\u7406\u540E\u53F0 Token",
|
|
2124
|
+
enterDataFinderAk: "\u8BF7\u8F93\u5165 DataFinder AccessKeyId",
|
|
2125
|
+
enterDataFinderAppId: "\u8BF7\u8F93\u5165 DataFinder \u5E94\u7528 ID",
|
|
2126
|
+
enterDataFinderDashboardId: "\u8BF7\u8F93\u5165\u9ED8\u8BA4 DataFinder \u770B\u677F ID",
|
|
2127
|
+
enterDataFinderProjectId: "\u8BF7\u8F93\u5165 DataFinder \u9879\u76EE ID",
|
|
2128
|
+
enterDataFinderSk: "\u8BF7\u8F93\u5165 DataFinder SecretAccessKey",
|
|
2129
|
+
enterDataFinderTimezone: "\u8BF7\u8F93\u5165 DataFinder \u67E5\u8BE2\u65F6\u533A",
|
|
2130
|
+
enterDataFinderUrl: "\u8BF7\u8F93\u5165 DataFinder OpenAPI \u5730\u5740",
|
|
2131
|
+
ghInstallDeferred: "\u672A\u5B8C\u6210 gh \u5B89\u88C5\u3002\u4F60\u53EF\u4EE5\u7A0D\u540E\u8FD0\u884C nexight-devops onboard --set-mode gh",
|
|
2132
|
+
ghLoginToken: "\u4F7F\u7528 Token",
|
|
2133
|
+
ghLoginWeb: "\u6D4F\u89C8\u5668\u767B\u5F55\uFF08\u63A8\u8350\uFF09",
|
|
2134
|
+
ghSection: "GitHub CLI \u8BBE\u7F6E",
|
|
2135
|
+
installAwsPrompt: "\u672A\u68C0\u6D4B\u5230 aws\uFF0C\u662F\u5426\u73B0\u5728\u5C1D\u8BD5\u5B89\u88C5\uFF1F",
|
|
2136
|
+
installBrewFailed: "Homebrew \u5B89\u88C5\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u624B\u52A8\u91CD\u8BD5\u3002",
|
|
2137
|
+
installBrewPrompt: "\u672A\u68C0\u6D4B\u5230 Homebrew\uFF0C\u662F\u5426\u73B0\u5728\u5C1D\u8BD5\u5B89\u88C5\uFF1F",
|
|
2138
|
+
installGhPrompt: "\u672A\u68C0\u6D4B\u5230 gh\uFF0C\u662F\u5426\u73B0\u5728\u5C1D\u8BD5\u5B89\u88C5\uFF1F",
|
|
2139
|
+
installManualPrefix: "\u8BF7\u5148\u5B89\u88C5 Homebrew\uFF0C\u518D\u6267\u884C",
|
|
2140
|
+
selectDataFinderPathMode: "\u8BF7\u9009\u62E9 DataFinder OpenAPI \u8DEF\u5F84\u6A21\u5F0F",
|
|
2141
|
+
selectGhLoginMode: "\u8BF7\u9009\u62E9 GitHub \u767B\u5F55\u65B9\u5F0F",
|
|
2142
|
+
skipGhSetup: "\u8DF3\u8FC7 GitHub CLI \u8BBE\u7F6E",
|
|
2143
|
+
skipAdminSetup: "\u8DF3\u8FC7\u7BA1\u7406\u540E\u53F0\u767B\u5F55\u8BBE\u7F6E",
|
|
2144
|
+
skipAwsSetup: "\u8DF3\u8FC7 AWS CLI \u8BBE\u7F6E",
|
|
2145
|
+
skipDataFinderSetup: "\u8DF3\u8FC7 DataFinder OpenAPI \u8BBE\u7F6E",
|
|
2146
|
+
awsInstallDeferred: "\u672A\u5B8C\u6210 AWS CLI \u5B89\u88C5\u3002\u4F60\u53EF\u4EE5\u7A0D\u540E\u8FD0\u884C nexight-devops onboard --set-mode aws",
|
|
2147
|
+
title: "\u5F00\u59CB\u521D\u59CB\u5316 nexight-devops"
|
|
2148
|
+
},
|
|
2149
|
+
release: {
|
|
2150
|
+
common: {
|
|
2151
|
+
approvalIssueUrl: "\u5BA1\u6279\u5355\u5730\u5740: {approvalIssueUrl}",
|
|
2152
|
+
candidateTag: "\u5019\u9009\u7248\u672C: {candidateTag}",
|
|
2153
|
+
controlRunUrl: "\u63A7\u5236\u9762\u8FD0\u884C\u5730\u5740: {controlRunUrl}",
|
|
2154
|
+
releaseBranch: "\u53D1\u5E03\u5206\u652F: {releaseBranch}",
|
|
2155
|
+
releaseProfile: "\u53D1\u5E03 Profile: {releaseProfile}",
|
|
2156
|
+
targetRepo: "\u76EE\u6807\u4ED3\u5E93: {targetRepo}"
|
|
2157
|
+
},
|
|
2158
|
+
approve: {
|
|
2159
|
+
approvalIssueMissing: "\u672A\u627E\u5230\u53EF\u5BA1\u6279\u7684\u53D1\u5E03\u5355\uFF0C\u8BF7\u91CD\u65B0\u9009\u62E9\u76EE\u6807\u4ED3\u5E93\u6216\u68C0\u67E5\u63A7\u5236\u9762\u72B6\u6001\u3002",
|
|
2160
|
+
approvalWorkflowRunUrl: "\u5BA1\u6279\u5DE5\u4F5C\u6D41\u5730\u5740: {approvalWorkflowRunUrl}",
|
|
2161
|
+
cancelled: "\u5DF2\u53D6\u6D88 release approve\u3002",
|
|
2162
|
+
commenting: "\u6B63\u5728\u5BF9\u53D1\u5E03\u5355 #{issueNumber} \u6267\u884C /release-approve",
|
|
2163
|
+
closeIssueWarning: "\u5BA1\u6279\u5355 #{issueNumber} \u81EA\u52A8\u5173\u95ED\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u624B\u52A8\u5173\u95ED\u3002",
|
|
2164
|
+
confirm: "\u786E\u8BA4\u5BF9\u53D1\u5E03\u5355 #{issueNumber}\uFF08{candidateTag} / {releaseBranch}\uFF09\u6267\u884C /release-approve\uFF1F",
|
|
2165
|
+
feishuReminder: "\u98DE\u4E66\u63D0\u9192\uFF1A\u6B63\u5F0F\u53D1\u7248\u5DF2\u5F00\u59CB\uFF0C\u8BF7\u7ACB\u5373\u5173\u6CE8\u98DE\u4E66\u7FA4\uFF0C\u6301\u7EED\u8DDF\u8FDB\u53D1\u5E03\u72B6\u6001\u3002",
|
|
2166
|
+
noPendingIssues: "\u5F53\u524D\u6CA1\u6709\u5F85\u5BA1\u6279\u7684\u53D1\u5E03\u5355\uFF0C\u76EE\u6807\u4ED3\u5E93 {targetRepo} \u6682\u65E0\u53EF\u5904\u7406\u9879\u3002",
|
|
2167
|
+
selectApprovalIssue: "\u8BF7\u9009\u62E9\u8981\u5BA1\u6279\u7684\u53D1\u5E03\u5355",
|
|
2168
|
+
selectTarget: "\u8BF7\u9009\u62E9\u8981\u5BA1\u6279\u7684\u4ED3\u5E93",
|
|
2169
|
+
success: "release approve \u5DF2\u5B8C\u6210\uFF0C\u6B63\u5F0F\u53D1\u7248\u5DF2\u5F00\u59CB",
|
|
2170
|
+
workflowFailed: "\u5BA1\u6279\u5DE5\u4F5C\u6D41\u6267\u884C\u5931\u8D25\uFF0C\u8BF7\u6253\u5F00\u5BA1\u6279\u5DE5\u4F5C\u6D41\u8FD0\u884C\u67E5\u770B\u8BE6\u60C5: {approvalWorkflowRunUrl}"
|
|
2171
|
+
},
|
|
2172
|
+
hotfix: {
|
|
2173
|
+
allowUnmergedHotfix: "\u662F\u5426\u5141\u8BB8 release \u5206\u652F\u4E0A\u7684\u8865\u4E01\u5C1A\u672A\u88AB main \u5438\u6536\u65F6\u76F4\u63A5\u53D1\u8D77 hotfix\uFF1F",
|
|
2174
|
+
approvalIssueAmbiguous: "\u68C0\u6D4B\u5230\u591A\u4E2A\u65B0\u7684 hotfix \u5BA1\u6279\u5355\uFF0C\u8BF7\u68C0\u67E5\u4EE5\u4E0B\u5019\u9009\u9879: {candidateIssueUrls}\uFF1B\u63A7\u5236\u9762\u8FD0\u884C: {controlRunUrl}",
|
|
2175
|
+
approvalIssueMissing: "\u672A\u627E\u5230\u65B0\u521B\u5EFA\u7684 hotfix \u5BA1\u6279\u5355\uFF0C\u8BF7\u6253\u5F00\u63A7\u5236\u9762\u8FD0\u884C\u67E5\u770B\u8BE6\u60C5: {controlRunUrl}",
|
|
2176
|
+
cancelled: "\u5DF2\u53D6\u6D88 release hotfix\u3002",
|
|
2177
|
+
confirm: "\u786E\u8BA4\u5BF9 {repoName} \u53D1\u8D77 hotfix\uFF0C\u5206\u652F {releaseBranch}\uFF1F",
|
|
2178
|
+
dispatching: "\u6B63\u5728\u51C6\u5907 hotfix: {repoName}",
|
|
2179
|
+
enterReleaseBranch: "\u8BF7\u8F93\u5165 hotfix \u7684 release \u5206\u652F\uFF08\u5FC5\u987B\u4EE5 release/ \u5F00\u5934\uFF09",
|
|
2180
|
+
feishuReminder: "\u98DE\u4E66\u63D0\u9192\uFF1A\u81EA\u6D4B\u53D1\u7248\u901A\u77E5\u5DF2\u53D1\u9001\u5230\u98DE\u4E66\u7FA4\uFF0C\u8BF7\u5173\u6CE8\u98DE\u4E66\u7FA4\u5E76\u5B8C\u6210\u9A8C\u8BC1\uFF0C\u968F\u540E\u6267\u884C nexight-devops release approve\u3002",
|
|
2181
|
+
invalidReleaseBranch: "release \u5206\u652F\u5FC5\u987B\u662F release/<name> \u683C\u5F0F\u3002",
|
|
2182
|
+
nextSteps: "\u4E0B\u4E00\u6B65\uFF1A\u5B8C\u6210\u9A8C\u8BC1\u540E\uFF0C\u6267\u884C nexight-devops release approve\u3002",
|
|
2183
|
+
overrideMode: "Override \u6A21\u5F0F: {overrideMode}",
|
|
2184
|
+
overrideModeDisabled: "\u672A\u5F00\u542F",
|
|
2185
|
+
overrideModeEnabled: "\u5DF2\u5F00\u542F",
|
|
2186
|
+
overrideReason: "\u8BF7\u8F93\u5165 release \u5206\u652F\u4E0A\u7684\u8865\u4E01\u5C1A\u672A\u88AB main \u5438\u6536\u65F6\u7EE7\u7EED hotfix \u7684\u539F\u56E0",
|
|
2187
|
+
overrideReasonRequired: "\u5F00\u542F release \u5206\u652F\u8865\u4E01 override \u65F6\u5FC5\u987B\u586B\u5199\u539F\u56E0\u3002",
|
|
2188
|
+
workflowFailureReason: "\u5931\u8D25\u539F\u56E0: {failureMessage}",
|
|
2189
|
+
selectTarget: "\u8BF7\u9009\u62E9\u8981\u6267\u884C hotfix \u7684\u4ED3\u5E93",
|
|
2190
|
+
success: "release hotfix \u5DF2\u5B8C\u6210",
|
|
2191
|
+
waiting: "\u63A7\u5236\u9762 hotfix \u5DE5\u4F5C\u6D41\u5DF2\u89E6\u53D1\uFF0C\u6B63\u5728\u7B49\u5F85\u5B8C\u6210: {repoName}",
|
|
2192
|
+
workflowFailed: "controlled-hotfix-release \u5DE5\u4F5C\u6D41\u6267\u884C\u5931\u8D25\uFF0C\u8BF7\u6253\u5F00\u63A7\u5236\u9762\u8FD0\u884C\u67E5\u770B\u8BE6\u60C5: {controlRunUrl}"
|
|
2193
|
+
},
|
|
2194
|
+
prepare: {
|
|
2195
|
+
approvalIssueAmbiguous: "\u68C0\u6D4B\u5230\u591A\u4E2A\u65B0\u7684\u53D1\u5E03\u5BA1\u6279\u5355\uFF0C\u8BF7\u68C0\u67E5\u4EE5\u4E0B\u5019\u9009\u9879: {candidateIssueUrls}\uFF1B\u63A7\u5236\u9762\u8FD0\u884C: {controlRunUrl}",
|
|
2196
|
+
approvalIssueMissing: "\u672A\u627E\u5230\u65B0\u521B\u5EFA\u7684\u53D1\u5E03\u5BA1\u6279\u5355\uFF0C\u8BF7\u6253\u5F00\u63A7\u5236\u9762\u8FD0\u884C\u67E5\u770B\u8BE6\u60C5: {controlRunUrl}",
|
|
2197
|
+
cancelled: "\u5DF2\u53D6\u6D88 release prepare\u3002",
|
|
2198
|
+
confirm: "\u786E\u8BA4\u5BF9 {repoName} \u6267\u884C release prepare\uFF1F",
|
|
2199
|
+
dispatching: "\u6B63\u5728\u51C6\u5907\u53D1\u5E03: {repoName}",
|
|
2200
|
+
feishuReminder: "\u98DE\u4E66\u63D0\u9192\uFF1A\u81EA\u6D4B\u53D1\u7248\u901A\u77E5\u5DF2\u53D1\u9001\u5230\u98DE\u4E66\u7FA4\uFF0C\u8BF7\u5173\u6CE8\u98DE\u4E66\u7FA4\u5E76\u5B8C\u6210\u9A8C\u8BC1\uFF0C\u968F\u540E\u6267\u884C nexight-devops release approve\u3002",
|
|
2201
|
+
nextSteps: "\u4E0B\u4E00\u6B65\uFF1A\u5B8C\u6210\u9A8C\u8BC1\u540E\uFF0C\u6267\u884C nexight-devops release approve\u3002",
|
|
2202
|
+
selectTarget: "\u8BF7\u9009\u62E9\u8981\u51C6\u5907\u53D1\u5E03\u7684\u4ED3\u5E93",
|
|
2203
|
+
success: "release prepare \u5DF2\u5B8C\u6210",
|
|
2204
|
+
waiting: "\u63A7\u5236\u9762\u53D1\u5E03\u5DE5\u4F5C\u6D41\u5DF2\u89E6\u53D1\uFF0C\u6B63\u5728\u7B49\u5F85\u5B8C\u6210: {repoName}",
|
|
2205
|
+
workflowFailed: "controlled-release \u5DE5\u4F5C\u6D41\u6267\u884C\u5931\u8D25\uFF0C\u8BF7\u6253\u5F00\u63A7\u5236\u9762\u8FD0\u884C\u67E5\u770B\u8BE6\u60C5: {controlRunUrl}"
|
|
2206
|
+
}
|
|
2207
|
+
},
|
|
2208
|
+
ppeLane: {
|
|
2209
|
+
destroy: {
|
|
2210
|
+
atLeastOneRepo: "\u81F3\u5C11\u9009\u62E9\u4E00\u4E2A\u4ED3\u5E93",
|
|
2211
|
+
blankLane: "PPE lane \u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A",
|
|
2212
|
+
cancelled: "\u5DF2\u53D6\u6D88\u9500\u6BC1",
|
|
2213
|
+
confirmLane: "\u8FD9\u662F\u9500\u6BC1\u64CD\u4F5C\uFF0C\u8BF7\u518D\u6B21\u8F93\u5165 {lane} \u786E\u8BA4",
|
|
2214
|
+
enterLane: "\u8BF7\u8F93\u5165\u9700\u8981\u79FB\u9664\u7684 PPE lane \u540D\u79F0",
|
|
2215
|
+
failed: "\u4E00\u4E2A\u6216\u591A\u4E2A PPE lane \u9500\u6BC1\u5DE5\u4F5C\u6D41\u6267\u884C\u5931\u8D25",
|
|
2216
|
+
noRecords: "\u8BE5 ppe-lane \u6CA1\u67E5\u8BE2\u5230\u4EFB\u4F55\u90E8\u7F72\u8BB0\u5F55",
|
|
2217
|
+
running: "\u6B63\u5728\u9500\u6BC1 lane {lane}",
|
|
2218
|
+
selectRepos: "\u8BF7\u9009\u62E9\u9700\u8981\u9500\u6BC1\u7684\u4ED3\u5E93",
|
|
2219
|
+
skippedReason: "Cloud Map \u4E2D\u672A\u627E\u5230\u90E8\u7F72\u8BB0\u5F55",
|
|
2220
|
+
warning: "\u5C06\u9500\u6BC1\u4EE5\u4E0B\u4ED3\u5E93: {repos}"
|
|
2221
|
+
}
|
|
2222
|
+
},
|
|
2223
|
+
versionUpdate: {
|
|
2224
|
+
alreadyLatest: "\u5F53\u524D\u5DF2\u662F\u6700\u65B0\u7248\u672C {currentVersion}\u3002",
|
|
2225
|
+
available: "\u68C0\u6D4B\u5230\u65B0\u7248\u672C {latestVersion}\uFF0C\u5F53\u524D\u7248\u672C\u4E3A {currentVersion}\u3002",
|
|
2226
|
+
checkingRegistry: "\u6B63\u5728\u68C0\u67E5\u7248\u672C\u4FE1\u606F",
|
|
2227
|
+
detectingInstallSource: "\u6B63\u5728\u68C0\u6D4B\u5B89\u88C5\u6765\u6E90",
|
|
2228
|
+
majorRequired: "\u68C0\u6D4B\u5230\u5927\u7248\u672C\u66F4\u65B0 {latestVersion}\uFF0C\u5F53\u524D\u7248\u672C {currentVersion} \u5FC5\u987B\u5148\u5347\u7EA7\u540E\u518D\u7EE7\u7EED\u3002",
|
|
2229
|
+
manualUpgrade: "\u65E0\u6CD5\u8BC6\u522B\u5F53\u524D\u5B89\u88C5\u6765\u6E90\uFF0C\u8BF7\u624B\u52A8\u6267\u884C\u4EE5\u4E0B\u4EFB\u4E00\u5347\u7EA7\u547D\u4EE4\uFF1A\n{commands}",
|
|
2230
|
+
optionalUpgradeFailed: "\u81EA\u52A8\u5347\u7EA7\u5931\u8D25\uFF0C\u672C\u6B21\u7EE7\u7EED\u4F7F\u7528\u5F53\u524D\u7248\u672C\u3002\u4F60\u53EF\u4EE5\u7A0D\u540E\u624B\u52A8\u6267\u884C\uFF1A{command}",
|
|
2231
|
+
registryCheckFailed: "\u7248\u672C\u68C0\u67E5\u5931\u8D25\uFF0C\u5DF2\u7EE7\u7EED\u6267\u884C\u5F53\u524D\u547D\u4EE4\u3002",
|
|
2232
|
+
registryCheckFailedManual: "\u7248\u672C\u68C0\u67E5\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002",
|
|
2233
|
+
rerun: "\u5347\u7EA7\u5B8C\u6210\u540E\u8BF7\u91CD\u65B0\u6267\u884C\u521A\u624D\u7684\u547D\u4EE4\u3002",
|
|
2234
|
+
selectAction: "\u68C0\u6D4B\u5230\u53EF\u7528\u66F4\u65B0\uFF0C\u662F\u5426\u73B0\u5728\u5347\u7EA7\uFF1F",
|
|
2235
|
+
skipThisRun: "\u8DF3\u8FC7\u672C\u6B21",
|
|
2236
|
+
skippedForRun: "\u5DF2\u8DF3\u8FC7\u672C\u6B21\u5347\u7EA7\uFF0C\u7EE7\u7EED\u6267\u884C\u5F53\u524D\u547D\u4EE4\u3002",
|
|
2237
|
+
upgradeFailed: "\u81EA\u52A8\u5347\u7EA7\u5931\u8D25\uFF0C\u8BF7\u5148\u624B\u52A8\u6267\u884C\uFF1A{command}",
|
|
2238
|
+
upgradeNow: "\u7ACB\u5373\u5347\u7EA7",
|
|
2239
|
+
upgradeSucceeded: "CLI \u5DF2\u66F4\u65B0\u5230\u6700\u65B0\u7248\u672C {latestVersion}\u3002",
|
|
2240
|
+
upgrading: "\u6B63\u5728\u6267\u884C\u5347\u7EA7\u547D\u4EE4\uFF1A{command}"
|
|
2241
|
+
}
|
|
2242
|
+
};
|
|
2243
|
+
|
|
2244
|
+
// src/lib/i18n.ts
|
|
2245
|
+
var locales = {
|
|
2246
|
+
"zh-CN": zhCN
|
|
2247
|
+
};
|
|
2248
|
+
var getNestedValue = (source, key) => {
|
|
2249
|
+
const value = key.split(".").reduce((current, part) => {
|
|
2250
|
+
if (current && typeof current === "object" && part in current) {
|
|
2251
|
+
return current[part];
|
|
2252
|
+
}
|
|
2253
|
+
return void 0;
|
|
2254
|
+
}, source);
|
|
2255
|
+
return typeof value === "string" ? value : key;
|
|
2256
|
+
};
|
|
2257
|
+
var interpolate = (message, values) => {
|
|
2258
|
+
if (!values) {
|
|
2259
|
+
return message;
|
|
2260
|
+
}
|
|
2261
|
+
return message.replace(/\{(\w+)\}/g, (placeholder, key) => {
|
|
2262
|
+
const value = values[key];
|
|
2263
|
+
if (value === void 0) {
|
|
2264
|
+
return placeholder;
|
|
2265
|
+
}
|
|
2266
|
+
return String(value);
|
|
2267
|
+
});
|
|
2268
|
+
};
|
|
2269
|
+
var createI18n = (locale) => {
|
|
2270
|
+
const messages = locales[locale];
|
|
2271
|
+
return (key, values) => {
|
|
2272
|
+
const messageFromLocale = messages ? getNestedValue(messages, key) : key;
|
|
2273
|
+
const message = messageFromLocale === key ? getNestedValue(zhCN, key) : messageFromLocale;
|
|
2274
|
+
return interpolate(message, values);
|
|
2275
|
+
};
|
|
2276
|
+
};
|
|
2087
2277
|
|
|
2088
2278
|
// src/lib/shell.ts
|
|
2089
2279
|
import { execa } from "execa";
|
|
@@ -2099,598 +2289,435 @@ var runCommand = async (command, args = [], options = {}) => {
|
|
|
2099
2289
|
};
|
|
2100
2290
|
};
|
|
2101
2291
|
|
|
2102
|
-
// src/
|
|
2103
|
-
var
|
|
2104
|
-
var
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
};
|
|
2112
|
-
var runAwsLogs = async (run, args, operation) => {
|
|
2113
|
-
const result = await run("aws", args);
|
|
2114
|
-
if (result.exitCode !== 0) {
|
|
2115
|
-
throw new CliError(result.stderr.trim() || `AWS ${operation} failed`, 1);
|
|
2116
|
-
}
|
|
2117
|
-
return result.stdout;
|
|
2292
|
+
// src/commands/onboard.ts
|
|
2293
|
+
var onboardModes = ["all", "admin", "aws", "datafinder", "gh", "locale"];
|
|
2294
|
+
var resolveBrewCommand = async () => {
|
|
2295
|
+
const result = await runCommand("/bin/bash", [
|
|
2296
|
+
"-lc",
|
|
2297
|
+
"command -v brew || { [ -x /opt/homebrew/bin/brew ] && printf /opt/homebrew/bin/brew; } || { [ -x /usr/local/bin/brew ] && printf /usr/local/bin/brew; }"
|
|
2298
|
+
]);
|
|
2299
|
+
const brewPath = result.stdout.trim();
|
|
2300
|
+
return brewPath ? brewPath : null;
|
|
2118
2301
|
};
|
|
2119
|
-
var
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
bytesScanned: stats.bytesScanned ?? 0,
|
|
2128
|
-
logGroupsScanned: stats.logGroupsScanned ?? 0,
|
|
2129
|
-
recordsMatched: stats.recordsMatched ?? 0,
|
|
2130
|
-
recordsScanned: stats.recordsScanned ?? 0
|
|
2131
|
-
});
|
|
2132
|
-
var epochSeconds = (value) => String(Math.floor(value.getTime() / 1e3));
|
|
2133
|
-
var defaultSleep2 = async (durationMs) => new Promise((resolve5) => {
|
|
2134
|
-
setTimeout(resolve5, durationMs);
|
|
2135
|
-
});
|
|
2136
|
-
var queryCloudWatchLogs = async (input2, dependencies = {}) => {
|
|
2137
|
-
const run = dependencies.run ?? runCommand;
|
|
2138
|
-
const now = dependencies.now ?? Date.now;
|
|
2139
|
-
const sleep2 = dependencies.sleep ?? defaultSleep2;
|
|
2140
|
-
const timeoutMs = input2.timeoutMs ?? DEFAULT_QUERY_TIMEOUT_MS;
|
|
2141
|
-
const pollIntervalMs = input2.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
2142
|
-
const limit = Math.max(1, Math.min(1e4, Math.floor(input2.limit ?? 500)));
|
|
2143
|
-
const startedAt = now();
|
|
2144
|
-
const startReply = parseAwsJson(
|
|
2145
|
-
await runAwsLogs(
|
|
2146
|
-
run,
|
|
2147
|
-
[
|
|
2148
|
-
"logs",
|
|
2149
|
-
"start-query",
|
|
2150
|
-
"--log-group-name",
|
|
2151
|
-
input2.logGroup,
|
|
2152
|
-
"--start-time",
|
|
2153
|
-
epochSeconds(input2.startTime),
|
|
2154
|
-
"--end-time",
|
|
2155
|
-
epochSeconds(input2.endTime),
|
|
2156
|
-
"--query-string",
|
|
2157
|
-
input2.query,
|
|
2158
|
-
"--limit",
|
|
2159
|
-
String(limit),
|
|
2160
|
-
"--profile",
|
|
2161
|
-
input2.profile,
|
|
2162
|
-
"--region",
|
|
2163
|
-
input2.region,
|
|
2164
|
-
"--output",
|
|
2165
|
-
"json"
|
|
2166
|
-
],
|
|
2167
|
-
"Logs Insights start-query"
|
|
2168
|
-
),
|
|
2169
|
-
"Logs Insights start-query"
|
|
2302
|
+
var installHomebrew = async () => {
|
|
2303
|
+
const result = await runCommand(
|
|
2304
|
+
"/bin/bash",
|
|
2305
|
+
[
|
|
2306
|
+
"-lc",
|
|
2307
|
+
'/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"'
|
|
2308
|
+
],
|
|
2309
|
+
{ stdio: "inherit" }
|
|
2170
2310
|
);
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
const
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
queryId,
|
|
2184
|
-
"--profile",
|
|
2185
|
-
input2.profile,
|
|
2186
|
-
"--region",
|
|
2187
|
-
input2.region,
|
|
2188
|
-
"--output",
|
|
2189
|
-
"json"
|
|
2190
|
-
],
|
|
2191
|
-
"Logs Insights get-query-results"
|
|
2192
|
-
),
|
|
2193
|
-
"Logs Insights get-query-results"
|
|
2194
|
-
);
|
|
2195
|
-
const status = reply.status ?? "Unknown";
|
|
2196
|
-
if (status === "Complete") {
|
|
2197
|
-
return {
|
|
2198
|
-
queryId,
|
|
2199
|
-
rows: queryRows(reply.results),
|
|
2200
|
-
stats: queryStats(reply.statistics)
|
|
2201
|
-
};
|
|
2311
|
+
return result.exitCode === 0;
|
|
2312
|
+
};
|
|
2313
|
+
var ensureBrewPackage = async (packageName, t) => {
|
|
2314
|
+
let brewCommand = await resolveBrewCommand();
|
|
2315
|
+
if (!brewCommand) {
|
|
2316
|
+
const shouldInstallBrew = await ui.confirm({
|
|
2317
|
+
message: t("onboard.installBrewPrompt"),
|
|
2318
|
+
default: true
|
|
2319
|
+
});
|
|
2320
|
+
if (!shouldInstallBrew) {
|
|
2321
|
+
logger.warn(`${t("onboard.installManualPrefix")} brew install ${packageName}`);
|
|
2322
|
+
return false;
|
|
2202
2323
|
}
|
|
2203
|
-
if (
|
|
2204
|
-
|
|
2324
|
+
if (!await installHomebrew()) {
|
|
2325
|
+
logger.warn(t("onboard.installBrewFailed"));
|
|
2326
|
+
return false;
|
|
2327
|
+
}
|
|
2328
|
+
brewCommand = await resolveBrewCommand();
|
|
2329
|
+
if (!brewCommand) {
|
|
2330
|
+
logger.warn(t("onboard.installBrewFailed"));
|
|
2331
|
+
return false;
|
|
2205
2332
|
}
|
|
2206
|
-
await sleep2(pollIntervalMs);
|
|
2207
2333
|
}
|
|
2208
|
-
await
|
|
2209
|
-
|
|
2210
|
-
"stop-query",
|
|
2211
|
-
"--query-id",
|
|
2212
|
-
queryId,
|
|
2213
|
-
"--profile",
|
|
2214
|
-
input2.profile,
|
|
2215
|
-
"--region",
|
|
2216
|
-
input2.region,
|
|
2217
|
-
"--output",
|
|
2218
|
-
"json"
|
|
2219
|
-
]);
|
|
2220
|
-
throw new CliError(`AWS Logs Insights query ${queryId} exceeded ${timeoutMs}ms`, 1);
|
|
2334
|
+
const installResult = await runCommand(brewCommand, ["install", packageName], { stdio: "inherit" });
|
|
2335
|
+
return installResult.exitCode === 0;
|
|
2221
2336
|
};
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
var DEFAULT_TSH_LOG_GROUP = "/aws/containerinsights/nexight-ojo-us-east-1/application";
|
|
2227
|
-
var DEFAULT_TSH_NAMESPACE = "tsh";
|
|
2228
|
-
var DEFAULT_TSH_CONTAINER = "tsh-server";
|
|
2229
|
-
var DEFAULT_DIAGNOSIS_WINDOW_MINUTES = 30;
|
|
2230
|
-
var MAX_DIAGNOSIS_WINDOW_MINUTES = 24 * 60;
|
|
2231
|
-
var MAX_IDENTIFIER_LENGTH = 256;
|
|
2232
|
-
var QUERY_LIMIT = 500;
|
|
2233
|
-
var RESULT_EVENT_LIMIT = 50;
|
|
2234
|
-
var PRODUCT_DEVICE_ID = /^(?:dev_[0-9a-f]{32}|[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})$/;
|
|
2235
|
-
var PRODUCT_AUTHORITY_ID = /^deva_[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
|
|
2236
|
-
var SAFE_IDENTIFIER = /^[A-Za-z0-9][A-Za-z0-9._:-]*$/;
|
|
2237
|
-
var asString2 = (value) => typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
2238
|
-
var asBoolean2 = (value) => typeof value === "boolean" ? value : void 0;
|
|
2239
|
-
var asNumber = (value) => {
|
|
2240
|
-
if (typeof value === "number" && Number.isFinite(value)) {
|
|
2241
|
-
return value;
|
|
2242
|
-
}
|
|
2243
|
-
if (typeof value === "string" && value.trim()) {
|
|
2244
|
-
const parsed = Number(value);
|
|
2245
|
-
return Number.isFinite(parsed) ? parsed : void 0;
|
|
2246
|
-
}
|
|
2247
|
-
return void 0;
|
|
2248
|
-
};
|
|
2249
|
-
var timestampValue = (row) => {
|
|
2250
|
-
const value = row["@timestamp"];
|
|
2251
|
-
const normalized = value && /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}(?:\.\d+)?$/.test(value) ? `${value.replace(" ", "T")}Z` : value;
|
|
2252
|
-
const parsed = normalized ? new Date(normalized) : void 0;
|
|
2253
|
-
return parsed && !Number.isNaN(parsed.getTime()) ? parsed.toISOString() : value || (/* @__PURE__ */ new Date(0)).toISOString();
|
|
2254
|
-
};
|
|
2255
|
-
var newestFirst = (events) => events.sort((left, right) => Date.parse(right.timestamp) - Date.parse(left.timestamp));
|
|
2256
|
-
var validateDiagnosticIdentifier = (label, value) => {
|
|
2257
|
-
const normalized = value.trim();
|
|
2258
|
-
if (!normalized || normalized.length > MAX_IDENTIFIER_LENGTH || !SAFE_IDENTIFIER.test(normalized)) {
|
|
2259
|
-
throw new CliError(`${label} must use 1-${MAX_IDENTIFIER_LENGTH} letters, digits, '.', '_', ':', or '-'`, 1);
|
|
2260
|
-
}
|
|
2261
|
-
return normalized;
|
|
2262
|
-
};
|
|
2263
|
-
var parseDiagnosisAnchor = (value) => {
|
|
2264
|
-
const normalized = value.trim();
|
|
2265
|
-
if (!/(?:z|[+-]\d{2}:\d{2})$/i.test(normalized)) {
|
|
2266
|
-
throw new CliError("--at must include an explicit timezone offset or Z", 1);
|
|
2267
|
-
}
|
|
2268
|
-
const parsed = new Date(normalized);
|
|
2269
|
-
if (Number.isNaN(parsed.getTime())) {
|
|
2270
|
-
throw new CliError(`Invalid --at timestamp: ${value}`, 1);
|
|
2271
|
-
}
|
|
2272
|
-
return parsed;
|
|
2273
|
-
};
|
|
2274
|
-
var resolveDiagnosisWindow = (anchor, windowMinutes = DEFAULT_DIAGNOSIS_WINDOW_MINUTES, now = /* @__PURE__ */ new Date()) => {
|
|
2275
|
-
if (!Number.isInteger(windowMinutes) || windowMinutes < 5 || windowMinutes > MAX_DIAGNOSIS_WINDOW_MINUTES) {
|
|
2276
|
-
throw new CliError(`--window-minutes must be an integer between 5 and ${MAX_DIAGNOSIS_WINDOW_MINUTES}`, 1);
|
|
2337
|
+
var ensureGhInstalled = async (t) => {
|
|
2338
|
+
const ghVersion = await runCommand("gh", ["--version"]);
|
|
2339
|
+
if (ghVersion.exitCode === 0) {
|
|
2340
|
+
return true;
|
|
2277
2341
|
}
|
|
2278
|
-
if (
|
|
2279
|
-
|
|
2342
|
+
if (process.platform === "darwin") {
|
|
2343
|
+
const shouldInstall = await ui.confirm({ message: t("onboard.installGhPrompt"), default: true });
|
|
2344
|
+
if (shouldInstall) {
|
|
2345
|
+
return ensureBrewPackage("gh", t);
|
|
2346
|
+
}
|
|
2280
2347
|
}
|
|
2281
|
-
|
|
2282
|
-
return
|
|
2283
|
-
from: new Date(anchor.getTime() - halfWindowMs),
|
|
2284
|
-
to: new Date(Math.min(anchor.getTime() + halfWindowMs, now.getTime()))
|
|
2285
|
-
};
|
|
2348
|
+
logger.warn(t("onboard.ghInstallDeferred"));
|
|
2349
|
+
return false;
|
|
2286
2350
|
};
|
|
2287
|
-
var
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
};
|
|
2292
|
-
var baseDiagnosticQuery = (namespace, container, prefix) => [
|
|
2293
|
-
"fields @timestamp, log",
|
|
2294
|
-
`| filter kubernetes.namespace_name = ${JSON.stringify(namespace)}`,
|
|
2295
|
-
`| filter kubernetes.container_name = ${JSON.stringify(container)}`,
|
|
2296
|
-
`| filter log like /diagnostic_prefix=${regexEscape(prefix)}/`,
|
|
2297
|
-
"| parse log /diagnostic_payload=(?<diagnosticPayload>\\{.*\\})$/",
|
|
2298
|
-
"| filter ispresent(diagnosticPayload)"
|
|
2299
|
-
];
|
|
2300
|
-
var buildDescriptorQuery = (input2) => {
|
|
2301
|
-
const commands = baseDiagnosticQuery(input2.namespace, input2.container, "RELAY_DESCRIPTOR_DIAGNOSTIC");
|
|
2302
|
-
commands.push(`| filter ${jsonFieldFilter("roomId", input2.roomId)}`);
|
|
2303
|
-
if (input2.sharedAgentId) {
|
|
2304
|
-
commands.push(`| filter ${jsonFieldFilter("sharedAgentId", input2.sharedAgentId)}`);
|
|
2305
|
-
}
|
|
2306
|
-
commands.push("| display @timestamp, diagnosticPayload", "| sort @timestamp desc", `| limit ${QUERY_LIMIT}`);
|
|
2307
|
-
return commands.join("\n");
|
|
2308
|
-
};
|
|
2309
|
-
var buildHTTPFailureQuery = (input2) => {
|
|
2310
|
-
const commands = baseDiagnosticQuery(input2.namespace, input2.container, "HTTP_FAILURE_DIAGNOSTIC");
|
|
2311
|
-
const filters = [jsonFieldFilter("roomId", input2.roomId)];
|
|
2312
|
-
if (input2.ownerDeviceId) {
|
|
2313
|
-
filters.push(jsonFieldFilter("deviceId", input2.ownerDeviceId));
|
|
2314
|
-
}
|
|
2315
|
-
if (input2.authorityId) {
|
|
2316
|
-
filters.push(`diagnosticPayload like /"urlPath"\\s*:\\s*"[^"]*${regexEscape(input2.authorityId)}[^"]*"/`);
|
|
2317
|
-
}
|
|
2318
|
-
commands.push(`| filter (${filters.join(" or ")})`);
|
|
2319
|
-
commands.push("| display @timestamp, diagnosticPayload", "| sort @timestamp desc", `| limit ${QUERY_LIMIT}`);
|
|
2320
|
-
return commands.join("\n");
|
|
2321
|
-
};
|
|
2322
|
-
var buildOwnerLeaseQuery = (input2) => {
|
|
2323
|
-
const refs = [...new Set(input2.refs.filter(Boolean))];
|
|
2324
|
-
if (refs.length === 0) {
|
|
2325
|
-
throw new CliError("Owner lease query requires at least one pseudonymous device or authority reference", 1);
|
|
2326
|
-
}
|
|
2327
|
-
return [
|
|
2328
|
-
"fields @timestamp, log",
|
|
2329
|
-
`| filter kubernetes.namespace_name = ${JSON.stringify(input2.namespace)}`,
|
|
2330
|
-
`| filter kubernetes.container_name = ${JSON.stringify(input2.container)}`,
|
|
2331
|
-
"| filter log like /shared-agent owner lease observation accepted/",
|
|
2332
|
-
`| filter (${refs.map((ref) => `log like /${regexEscape(ref)}/`).join(" or ")})`,
|
|
2333
|
-
"| display @timestamp, log",
|
|
2334
|
-
"| sort @timestamp desc",
|
|
2335
|
-
`| limit ${QUERY_LIMIT}`
|
|
2336
|
-
].join("\n");
|
|
2337
|
-
};
|
|
2338
|
-
var parseDiagnosticPayload = (row) => {
|
|
2339
|
-
const payload = row.diagnosticPayload;
|
|
2340
|
-
if (!payload) {
|
|
2341
|
-
return void 0;
|
|
2342
|
-
}
|
|
2343
|
-
try {
|
|
2344
|
-
const parsed = JSON.parse(payload);
|
|
2345
|
-
return parsed && typeof parsed === "object" ? parsed : void 0;
|
|
2346
|
-
} catch {
|
|
2347
|
-
return void 0;
|
|
2351
|
+
var ensureAwsInstalled = async (t) => {
|
|
2352
|
+
const awsVersion = await runCommand("aws", ["--version"]);
|
|
2353
|
+
if (awsVersion.exitCode === 0) {
|
|
2354
|
+
return true;
|
|
2348
2355
|
}
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
const outcome = asString2(payload?.outcome);
|
|
2354
|
-
const roomId = asString2(payload?.roomId);
|
|
2355
|
-
const sharedAgentId = asString2(payload?.sharedAgentId);
|
|
2356
|
-
if (!payload || payload.prefix !== "RELAY_DESCRIPTOR_DIAGNOSTIC" || !outcome || !roomId || !sharedAgentId) {
|
|
2357
|
-
return [];
|
|
2358
|
-
}
|
|
2359
|
-
return [
|
|
2360
|
-
{
|
|
2361
|
-
timestamp: timestampValue(row),
|
|
2362
|
-
outcome,
|
|
2363
|
-
channel: asString2(payload.channel) ?? "unknown",
|
|
2364
|
-
roomId,
|
|
2365
|
-
sharedAgentId,
|
|
2366
|
-
...asString2(payload.ownerUserId) ? { ownerUserId: asString2(payload.ownerUserId) } : {},
|
|
2367
|
-
...asString2(payload.ownerDeviceId) ? { ownerDeviceId: asString2(payload.ownerDeviceId) } : {},
|
|
2368
|
-
...asString2(payload.reason) ? { reason: asString2(payload.reason) } : {},
|
|
2369
|
-
...asBoolean2(payload.directEligible) !== void 0 ? { directEligible: asBoolean2(payload.directEligible) } : {}
|
|
2370
|
-
}
|
|
2371
|
-
];
|
|
2372
|
-
})
|
|
2373
|
-
).slice(0, RESULT_EVENT_LIMIT);
|
|
2374
|
-
var extractLogValue = (log, field2) => {
|
|
2375
|
-
const escapedField = regexEscape(field2);
|
|
2376
|
-
const jsonMatch = log.match(new RegExp(`"${escapedField}"\\s*:\\s*(?:"([^"]*)"|([^,}\\s]+))`));
|
|
2377
|
-
if (jsonMatch) {
|
|
2378
|
-
return (jsonMatch[1] ?? jsonMatch[2])?.trim();
|
|
2379
|
-
}
|
|
2380
|
-
const keyValueMatch = log.match(new RegExp(`(?:^|\\s)${escapedField}=(?:"([^"]*)"|([^\\s]+))`));
|
|
2381
|
-
return (keyValueMatch?.[1] ?? keyValueMatch?.[2])?.trim();
|
|
2382
|
-
};
|
|
2383
|
-
var parseOwnerLeaseEvidence = (rows) => newestFirst(
|
|
2384
|
-
rows.flatMap((row) => {
|
|
2385
|
-
const log = row.log ?? "";
|
|
2386
|
-
const outcome = extractLogValue(log, "outcome");
|
|
2387
|
-
const reasonCode = extractLogValue(log, "reason_code");
|
|
2388
|
-
if (!log.includes("shared-agent owner lease observation accepted") || !outcome || !reasonCode) {
|
|
2389
|
-
return [];
|
|
2390
|
-
}
|
|
2391
|
-
const retryable = extractLogValue(log, "retryable");
|
|
2392
|
-
return [
|
|
2393
|
-
{
|
|
2394
|
-
timestamp: timestampValue(row),
|
|
2395
|
-
outcome,
|
|
2396
|
-
reasonCode,
|
|
2397
|
-
clientVersion: extractLogValue(log, "client_version") ?? "unknown",
|
|
2398
|
-
leaseStateBefore: extractLogValue(log, "lease_state_before") ?? "unknown",
|
|
2399
|
-
leaseStateAfter: extractLogValue(log, "lease_state_after") ?? "unknown",
|
|
2400
|
-
...extractLogValue(log, "observation_id") ? { observationId: extractLogValue(log, "observation_id") } : {},
|
|
2401
|
-
...extractLogValue(log, "owner_device_ref") ? { ownerDeviceRef: extractLogValue(log, "owner_device_ref") } : {},
|
|
2402
|
-
...extractLogValue(log, "authority_ref") ? { authorityRef: extractLogValue(log, "authority_ref") } : {},
|
|
2403
|
-
...retryable === "true" || retryable === "false" ? { retryable: retryable === "true" } : {}
|
|
2404
|
-
}
|
|
2405
|
-
];
|
|
2406
|
-
})
|
|
2407
|
-
).slice(0, RESULT_EVENT_LIMIT);
|
|
2408
|
-
var parseHTTPFailureEvidence = (rows) => newestFirst(
|
|
2409
|
-
rows.flatMap((row) => {
|
|
2410
|
-
const payload = parseDiagnosticPayload(row);
|
|
2411
|
-
if (!payload || payload.prefix !== "HTTP_FAILURE_DIAGNOSTIC") {
|
|
2412
|
-
return [];
|
|
2356
|
+
if (process.platform === "darwin") {
|
|
2357
|
+
const shouldInstall = await ui.confirm({ message: t("onboard.installAwsPrompt"), default: true });
|
|
2358
|
+
if (shouldInstall) {
|
|
2359
|
+
return ensureBrewPackage("awscli", t);
|
|
2413
2360
|
}
|
|
2414
|
-
return [
|
|
2415
|
-
{
|
|
2416
|
-
timestamp: timestampValue(row),
|
|
2417
|
-
...asNumber(payload.statusCode) !== void 0 ? { statusCode: asNumber(payload.statusCode) } : {},
|
|
2418
|
-
...asString2(payload.route) ? { route: asString2(payload.route) } : {},
|
|
2419
|
-
...asString2(payload.urlPath) ? { urlPath: asString2(payload.urlPath) } : {},
|
|
2420
|
-
...asString2(payload.reason) ? { reason: asString2(payload.reason) } : {},
|
|
2421
|
-
...asString2(payload.requestId) ? { requestId: asString2(payload.requestId) } : {},
|
|
2422
|
-
...asString2(payload.roomId) ? { roomId: asString2(payload.roomId) } : {},
|
|
2423
|
-
...asString2(payload.deviceId) ? { deviceId: asString2(payload.deviceId) } : {},
|
|
2424
|
-
...asString2(payload.clientVersion) ? { clientVersion: asString2(payload.clientVersion) } : {}
|
|
2425
|
-
}
|
|
2426
|
-
];
|
|
2427
|
-
})
|
|
2428
|
-
).slice(0, RESULT_EVENT_LIMIT);
|
|
2429
|
-
var ownerGatewayRef = (kind, value) => {
|
|
2430
|
-
const normalized = value?.trim().toLowerCase();
|
|
2431
|
-
if (!normalized) {
|
|
2432
|
-
return void 0;
|
|
2433
|
-
}
|
|
2434
|
-
const eligible = kind === "device" ? PRODUCT_DEVICE_ID.test(normalized) : PRODUCT_AUTHORITY_ID.test(normalized);
|
|
2435
|
-
if (!eligible) {
|
|
2436
|
-
return void 0;
|
|
2437
|
-
}
|
|
2438
|
-
return createHash("sha256").update(["shared-agent-owner-gateway/v1", kind, normalized].join("\0")).digest("hex").slice(0, 24);
|
|
2439
|
-
};
|
|
2440
|
-
var ownerDeviceRef = (deviceId) => ownerGatewayRef("device", deviceId);
|
|
2441
|
-
var ownerAuthorityRef = (authorityId) => ownerGatewayRef("authority", authorityId);
|
|
2442
|
-
var attemptQuery = async (name, queryLogs, input2) => {
|
|
2443
|
-
try {
|
|
2444
|
-
const result = await queryLogs(input2);
|
|
2445
|
-
return { result, report: { name, status: "complete", queryId: result.queryId, stats: result.stats } };
|
|
2446
|
-
} catch (error) {
|
|
2447
|
-
return { report: { name, status: "failed", error: toErrorMessage(error) } };
|
|
2448
2361
|
}
|
|
2362
|
+
logger.warn(t("onboard.awsInstallDeferred"));
|
|
2363
|
+
return false;
|
|
2449
2364
|
};
|
|
2450
|
-
var
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
if (latestLease && ["failed", "rejected"].includes(latestLease.outcome) && (!latestDescriptor || Date.parse(latestLease.timestamp) >= Date.parse(latestDescriptor.timestamp))) {
|
|
2454
|
-
return {
|
|
2455
|
-
code: "OWNER_LEASE_UNHEALTHY",
|
|
2456
|
-
confidence: "high",
|
|
2457
|
-
summary: `Owner \u7EED\u7EA6\u4E3A ${latestLease.outcome}/${latestLease.reasonCode}\uFF0C\u63A7\u5236\u9762\u5DF2\u6536\u5230\u5F02\u5E38\u7EED\u7EA6\u8BC1\u636E\u3002`
|
|
2458
|
-
};
|
|
2459
|
-
}
|
|
2460
|
-
if (latestDescriptor?.outcome === "authority_offline") {
|
|
2461
|
-
if (latestLease?.outcome === "recovered" && Date.parse(latestLease.timestamp) >= Date.parse(latestDescriptor.timestamp)) {
|
|
2462
|
-
return {
|
|
2463
|
-
code: "OWNER_RECOVERED",
|
|
2464
|
-
confidence: "high",
|
|
2465
|
-
summary: "Descriptor \u8BF7\u6C42\u9047\u5230 Owner \u79BB\u7EBF\uFF0C\u968F\u540E\u670D\u52A1\u7AEF\u89C2\u5BDF\u5230 Owner \u7EED\u7EA6\u6062\u590D\u3002"
|
|
2466
|
-
};
|
|
2467
|
-
}
|
|
2468
|
-
return {
|
|
2469
|
-
code: "OWNER_OFFLINE",
|
|
2470
|
-
confidence: "high",
|
|
2471
|
-
summary: "\u63A7\u5236\u9762\u660E\u786E\u8FD4\u56DE authority_offline\uFF0COwner Presence lease \u5728\u8BF7\u6C42\u65F6\u4E0D\u53EF\u7528\u3002"
|
|
2472
|
-
};
|
|
2365
|
+
var runGhSetup = async (t) => {
|
|
2366
|
+
if (!await ensureGhInstalled(t)) {
|
|
2367
|
+
return false;
|
|
2473
2368
|
}
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
confidence: "high",
|
|
2478
|
-
summary: "Descriptor \u7B7E\u53D1\u53D1\u751F\u670D\u52A1\u7AEF\u9519\u8BEF\uFF0C\u9700\u8981\u6309 requestId \u6216\u76F8\u90BB\u670D\u52A1\u7AEF\u65E5\u5FD7\u7EE7\u7EED\u5B9A\u4F4D\u3002"
|
|
2479
|
-
};
|
|
2369
|
+
const authStatus = await runCommand("gh", ["auth", "status"]);
|
|
2370
|
+
if (authStatus.exitCode === 0) {
|
|
2371
|
+
return true;
|
|
2480
2372
|
}
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2373
|
+
const loginMode = await ui.select({
|
|
2374
|
+
message: t("onboard.selectGhLoginMode"),
|
|
2375
|
+
choices: [
|
|
2376
|
+
{ value: "web", name: t("onboard.ghLoginWeb") },
|
|
2377
|
+
{ value: "token", name: t("onboard.ghLoginToken") }
|
|
2378
|
+
]
|
|
2379
|
+
});
|
|
2380
|
+
if (loginMode === "web") {
|
|
2381
|
+
await runCommand("gh", ["auth", "login", "--web"], { stdio: "inherit" });
|
|
2382
|
+
} else {
|
|
2383
|
+
const token = await ui.password({ message: t("onboard.enterGhToken") });
|
|
2384
|
+
await runCommand("gh", ["auth", "login", "--with-token"], {
|
|
2385
|
+
input: token
|
|
2386
|
+
});
|
|
2487
2387
|
}
|
|
2488
|
-
|
|
2388
|
+
return (await runCommand("gh", ["auth", "status"])).exitCode === 0;
|
|
2389
|
+
};
|
|
2390
|
+
var runAwsSetup = async (defaultProfile, t) => {
|
|
2391
|
+
if (!await ensureAwsInstalled(t)) {
|
|
2489
2392
|
return {
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
summary: latestLease ? "\u63A7\u5236\u9762\u5DF2\u7B7E\u53D1 descriptor\uFF0C\u4E14\u5B58\u5728\u5BF9\u5E94 Owner \u7EED\u7EA6\u8BC1\u636E\uFF1B\u540E\u7EED\u5E94\u68C0\u67E5\u5BA2\u6237\u7AEF\u5230 Device Gateway \u7684\u8FDE\u63A5\u9636\u6BB5\u3002" : "\u63A7\u5236\u9762\u5DF2\u7B7E\u53D1 descriptor\uFF0C\u4F46\u5F53\u524D\u7A97\u53E3\u6CA1\u6709\u5BF9\u5E94 Owner \u7EED\u7EA6\u89C2\u6D4B\uFF0C\u540E\u7EED\u5E94\u68C0\u67E5 Device Gateway \u8FDE\u63A5\u9636\u6BB5\u3002"
|
|
2393
|
+
profile: defaultProfile,
|
|
2394
|
+
configured: false
|
|
2493
2395
|
};
|
|
2494
2396
|
}
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2397
|
+
const profile = await ui.input({
|
|
2398
|
+
message: t("onboard.chooseAwsProfile"),
|
|
2399
|
+
default: defaultProfile
|
|
2400
|
+
});
|
|
2401
|
+
const listProfiles = await runCommand("aws", ["configure", "list-profiles"]);
|
|
2402
|
+
const profiles = listProfiles.stdout.split("\n").map((item) => item.trim()).filter(Boolean);
|
|
2403
|
+
if (!profiles.includes(profile)) {
|
|
2404
|
+
await runCommand("aws", ["configure", "sso", "--profile", profile], { stdio: "inherit" });
|
|
2501
2405
|
}
|
|
2502
|
-
|
|
2406
|
+
const loginResult = await runCommand("aws", ["sso", "login", "--profile", profile], { stdio: "inherit" });
|
|
2407
|
+
if (loginResult.exitCode !== 0) {
|
|
2503
2408
|
return {
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
summary: "\u53EA\u627E\u5230\u76F8\u5173 HTTP \u5931\u8D25\uFF0C\u672A\u627E\u5230 descriptor \u6216 Owner \u7EED\u7EA6\u8BC1\u636E\u3002"
|
|
2409
|
+
profile,
|
|
2410
|
+
configured: false
|
|
2507
2411
|
};
|
|
2508
2412
|
}
|
|
2413
|
+
const identityResult = await runCommand("aws", [
|
|
2414
|
+
"sts",
|
|
2415
|
+
"get-caller-identity",
|
|
2416
|
+
"--profile",
|
|
2417
|
+
profile,
|
|
2418
|
+
"--output",
|
|
2419
|
+
"json"
|
|
2420
|
+
]);
|
|
2509
2421
|
return {
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
summary: "\u5F53\u524D\u65F6\u95F4\u7A97\u6CA1\u6709\u8DB3\u591F\u8BC1\u636E\u5224\u65AD\u5171\u4EAB Agent \u8FDE\u63A5\u5931\u8D25\u9636\u6BB5\u3002"
|
|
2422
|
+
profile,
|
|
2423
|
+
configured: identityResult.exitCode === 0
|
|
2513
2424
|
};
|
|
2514
2425
|
};
|
|
2515
|
-
var
|
|
2516
|
-
const
|
|
2517
|
-
const
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
const roomId = validateDiagnosticIdentifier("room ID", rawInput.roomId);
|
|
2521
|
-
const sharedAgentId = rawInput.sharedAgentId ? validateDiagnosticIdentifier("shared Agent ID", rawInput.sharedAgentId) : void 0;
|
|
2522
|
-
const providedOwnerDeviceId = rawInput.ownerDeviceId ? validateDiagnosticIdentifier("Owner device ID", rawInput.ownerDeviceId).toLowerCase() : void 0;
|
|
2523
|
-
const authorityId = rawInput.authorityId ? validateDiagnosticIdentifier("Authority ID", rawInput.authorityId).toLowerCase() : void 0;
|
|
2524
|
-
const window = resolveDiagnosisWindow(rawInput.anchor, rawInput.windowMinutes, now);
|
|
2525
|
-
const commonQueryInput = {
|
|
2526
|
-
startTime: window.from,
|
|
2527
|
-
endTime: window.to,
|
|
2528
|
-
logGroup: rawInput.logGroup,
|
|
2529
|
-
profile: rawInput.profile,
|
|
2530
|
-
region: rawInput.region,
|
|
2531
|
-
limit: QUERY_LIMIT
|
|
2532
|
-
};
|
|
2533
|
-
const descriptorAttempt = await attemptQuery("descriptor", queryLogs, {
|
|
2534
|
-
...commonQueryInput,
|
|
2535
|
-
query: buildDescriptorQuery({ namespace, container, roomId, sharedAgentId })
|
|
2536
|
-
});
|
|
2537
|
-
const descriptors = parseDescriptorEvidence(descriptorAttempt.result?.rows ?? []);
|
|
2538
|
-
const descriptorWithOwner = descriptors.find((event) => event.ownerDeviceId || event.ownerUserId);
|
|
2539
|
-
const ownerDeviceId = providedOwnerDeviceId ?? descriptorWithOwner?.ownerDeviceId?.toLowerCase();
|
|
2540
|
-
const ownerUserId = descriptorWithOwner?.ownerUserId;
|
|
2541
|
-
const deviceRef = ownerDeviceId ? ownerDeviceRef(ownerDeviceId) : void 0;
|
|
2542
|
-
const authorityRef = authorityId ? ownerAuthorityRef(authorityId) : void 0;
|
|
2543
|
-
const refs = [deviceRef, authorityRef].filter((value) => Boolean(value));
|
|
2544
|
-
const httpAttemptPromise = attemptQuery("http-failure", queryLogs, {
|
|
2545
|
-
...commonQueryInput,
|
|
2546
|
-
query: buildHTTPFailureQuery({ namespace, container, roomId, ownerDeviceId, authorityId })
|
|
2547
|
-
});
|
|
2548
|
-
const leaseAttemptPromise = refs.length > 0 ? attemptQuery("owner-lease", queryLogs, {
|
|
2549
|
-
...commonQueryInput,
|
|
2550
|
-
query: buildOwnerLeaseQuery({ namespace, container, refs })
|
|
2551
|
-
}) : Promise.resolve({ report: { name: "owner-lease", status: "skipped" } });
|
|
2552
|
-
const [httpAttempt, leaseAttempt] = await Promise.all([httpAttemptPromise, leaseAttemptPromise]);
|
|
2553
|
-
const failures = parseHTTPFailureEvidence(httpAttempt.result?.rows ?? []);
|
|
2554
|
-
const leases = parseOwnerLeaseEvidence(leaseAttempt.result?.rows ?? []);
|
|
2555
|
-
const queryReports = [descriptorAttempt.report, httpAttempt.report, leaseAttempt.report];
|
|
2556
|
-
if (!queryReports.some((report) => report.status === "complete")) {
|
|
2557
|
-
throw new CliError(
|
|
2558
|
-
`All Shared Agent evidence queries failed: ${queryReports.filter((report) => report.status === "failed").map((report) => `${report.name}: ${report.error ?? "unknown error"}`).join("; ")}`,
|
|
2559
|
-
1
|
|
2560
|
-
);
|
|
2561
|
-
}
|
|
2562
|
-
const warnings = queryReports.flatMap(
|
|
2563
|
-
(report) => report.status === "failed" ? [`${report.name} query failed: ${report.error ?? "unknown error"}`] : []
|
|
2564
|
-
);
|
|
2565
|
-
if (ownerDeviceId && !deviceRef) {
|
|
2566
|
-
warnings.push("Owner device ID is not a canonical product ID, so server-side pseudonymous lease events cannot be joined.");
|
|
2567
|
-
}
|
|
2568
|
-
if (authorityId && !authorityRef) {
|
|
2569
|
-
warnings.push("Authority ID is not canonical, so server-side pseudonymous lease events cannot be joined.");
|
|
2570
|
-
}
|
|
2571
|
-
const ownerRenewalFailure = failures.find(
|
|
2572
|
-
(failure) => `${failure.route ?? ""} ${failure.urlPath ?? ""}`.includes("/lease/renew")
|
|
2573
|
-
);
|
|
2574
|
-
const ownerClientVersion = leases[0]?.clientVersion ?? ownerRenewalFailure?.clientVersion;
|
|
2575
|
-
const missingEvidence = [];
|
|
2576
|
-
if (descriptors.length === 0) {
|
|
2577
|
-
missingEvidence.push("No RELAY_DESCRIPTOR_DIAGNOSTIC event matched this room and time window.");
|
|
2578
|
-
}
|
|
2579
|
-
if (!ownerDeviceId) {
|
|
2580
|
-
missingEvidence.push("Owner device identity was not supplied and no issued descriptor exposed it.");
|
|
2581
|
-
}
|
|
2582
|
-
if (leaseAttempt.report.status === "skipped") {
|
|
2583
|
-
missingEvidence.push("Owner lease evidence could not be queried without a canonical Owner device or Authority ID.");
|
|
2584
|
-
} else if (leases.length === 0) {
|
|
2585
|
-
missingEvidence.push("No sampled Owner lease observation matched the derived pseudonymous reference.");
|
|
2426
|
+
var runAdminSetup = async (config, options, t) => {
|
|
2427
|
+
const apiOrigin = normalizeAdminApiOrigin(options.adminUrl ?? config.admin?.apiOrigin);
|
|
2428
|
+
const token = (await ui.password({ message: t("onboard.enterAdminToken") })).trim();
|
|
2429
|
+
if (!token) {
|
|
2430
|
+
throw new CliError(t("onboard.adminTokenRequired"), 1);
|
|
2586
2431
|
}
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
backend: {
|
|
2592
|
-
type: "aws-cloudwatch",
|
|
2593
|
-
account: rawInput.account,
|
|
2594
|
-
profile: rawInput.profile,
|
|
2595
|
-
region: rawInput.region,
|
|
2596
|
-
logGroup: rawInput.logGroup
|
|
2597
|
-
},
|
|
2598
|
-
subject: { roomId, ...sharedAgentId ? { sharedAgentId } : {} },
|
|
2599
|
-
window: {
|
|
2600
|
-
anchor: rawInput.anchor.toISOString(),
|
|
2601
|
-
from: window.from.toISOString(),
|
|
2602
|
-
to: window.to.toISOString()
|
|
2603
|
-
},
|
|
2604
|
-
verdict: classify({ descriptors, failures, leases }),
|
|
2605
|
-
owner: {
|
|
2606
|
-
...ownerUserId ? { userId: ownerUserId } : {},
|
|
2607
|
-
...ownerDeviceId ? { deviceId: ownerDeviceId } : {},
|
|
2608
|
-
...deviceRef ? { deviceRef } : {},
|
|
2609
|
-
...ownerClientVersion ? { clientVersion: ownerClientVersion } : {}
|
|
2610
|
-
},
|
|
2611
|
-
descriptor: {
|
|
2612
|
-
attemptCount: descriptors.length,
|
|
2613
|
-
events: descriptors,
|
|
2614
|
-
...descriptors[0] ? { latest: descriptors[0] } : {}
|
|
2615
|
-
},
|
|
2616
|
-
ownerLease: {
|
|
2617
|
-
observationCount: leases.length,
|
|
2618
|
-
events: leases,
|
|
2619
|
-
...leases[0] ? { latest: leases[0] } : {}
|
|
2620
|
-
},
|
|
2621
|
-
httpFailures: { count: failures.length, events: failures },
|
|
2622
|
-
queryReports,
|
|
2623
|
-
partial: queryReports.some((report) => report.status === "failed"),
|
|
2624
|
-
warnings,
|
|
2625
|
-
missingEvidence
|
|
2432
|
+
await new AdminApi({ apiOrigin, token }).inspectToken(token);
|
|
2433
|
+
config.admin = {
|
|
2434
|
+
apiOrigin,
|
|
2435
|
+
token
|
|
2626
2436
|
};
|
|
2437
|
+
logger.success(t("onboard.adminLoginSucceeded"));
|
|
2627
2438
|
};
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
var
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
throw new CliError(result.stderr || `AWS command failed: aws ${args.join(" ")}`, 1);
|
|
2439
|
+
var inputWithDefault = async (message, value) => (await ui.input({
|
|
2440
|
+
message,
|
|
2441
|
+
...value ? { default: value } : {}
|
|
2442
|
+
})).trim();
|
|
2443
|
+
var requiredDataFinderValue = (value, label) => {
|
|
2444
|
+
if (!value) {
|
|
2445
|
+
throw new CliError(`DataFinder ${label} \u4E0D\u80FD\u4E3A\u7A7A`, 1);
|
|
2636
2446
|
}
|
|
2637
|
-
return
|
|
2447
|
+
return value;
|
|
2638
2448
|
};
|
|
2639
|
-
var
|
|
2640
|
-
const
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2449
|
+
var runDataFinderSetup = async (config, options, t) => {
|
|
2450
|
+
const previous = config.datafinder;
|
|
2451
|
+
let accessKeyId = previous?.accessKeyId ?? "";
|
|
2452
|
+
let secretAccessKey = previous?.secretAccessKey ?? "";
|
|
2453
|
+
if (previous?.accessKeyId && previous.secretAccessKey) {
|
|
2454
|
+
const credentialAction = await ui.select({
|
|
2455
|
+
message: t("onboard.datafinderCredentialAction"),
|
|
2456
|
+
choices: [
|
|
2457
|
+
{ value: "keep", name: t("onboard.datafinderKeepCredentials") },
|
|
2458
|
+
{ value: "replace", name: t("onboard.datafinderReplaceCredentials") }
|
|
2459
|
+
]
|
|
2460
|
+
});
|
|
2461
|
+
if (credentialAction === "replace") {
|
|
2462
|
+
accessKeyId = "";
|
|
2463
|
+
secretAccessKey = "";
|
|
2464
|
+
}
|
|
2653
2465
|
}
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2466
|
+
if (!accessKeyId || !secretAccessKey) {
|
|
2467
|
+
accessKeyId = (await ui.password({ message: t("onboard.enterDataFinderAk") })).trim();
|
|
2468
|
+
secretAccessKey = (await ui.password({ message: t("onboard.enterDataFinderSk") })).trim();
|
|
2657
2469
|
}
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2470
|
+
const pathMode = parseDataFinderPathMode(
|
|
2471
|
+
options.datafinderPathMode ?? await ui.select({
|
|
2472
|
+
message: t("onboard.selectDataFinderPathMode"),
|
|
2473
|
+
choices: [
|
|
2474
|
+
{ value: "project", name: t("onboard.datafinderProjectPathMode") },
|
|
2475
|
+
{ value: "app", name: t("onboard.datafinderAppPathMode") }
|
|
2476
|
+
],
|
|
2477
|
+
default: previous?.pathMode ?? "project"
|
|
2478
|
+
})
|
|
2662
2479
|
);
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
"
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2480
|
+
const candidate = {
|
|
2481
|
+
accessKeyId: requiredDataFinderValue(accessKeyId, "AccessKeyId"),
|
|
2482
|
+
appId: requiredDataFinderValue(
|
|
2483
|
+
await inputWithDefault(
|
|
2484
|
+
t("onboard.enterDataFinderAppId"),
|
|
2485
|
+
options.datafinderAppId ?? previous?.appId
|
|
2486
|
+
),
|
|
2487
|
+
"appId"
|
|
2488
|
+
),
|
|
2489
|
+
baseUrl: normalizeDataFinderBaseUrl(
|
|
2490
|
+
requiredDataFinderValue(
|
|
2491
|
+
await inputWithDefault(
|
|
2492
|
+
t("onboard.enterDataFinderUrl"),
|
|
2493
|
+
options.datafinderUrl ?? previous?.baseUrl ?? DEFAULT_DATAFINDER_BASE_URL
|
|
2494
|
+
),
|
|
2495
|
+
"baseUrl"
|
|
2496
|
+
)
|
|
2497
|
+
),
|
|
2498
|
+
defaultDashboardId: requiredDataFinderValue(
|
|
2499
|
+
await inputWithDefault(
|
|
2500
|
+
t("onboard.enterDataFinderDashboardId"),
|
|
2501
|
+
options.datafinderDashboardId ?? previous?.defaultDashboardId
|
|
2502
|
+
),
|
|
2503
|
+
"defaultDashboardId"
|
|
2504
|
+
),
|
|
2505
|
+
pathMode,
|
|
2506
|
+
projectId: requiredDataFinderValue(
|
|
2507
|
+
await inputWithDefault(
|
|
2508
|
+
t("onboard.enterDataFinderProjectId"),
|
|
2509
|
+
options.datafinderProjectId ?? previous?.projectId
|
|
2510
|
+
),
|
|
2511
|
+
"projectId"
|
|
2512
|
+
),
|
|
2513
|
+
secretAccessKey: requiredDataFinderValue(secretAccessKey, "SecretAccessKey"),
|
|
2514
|
+
timezone: requiredDataFinderValue(
|
|
2515
|
+
await inputWithDefault(
|
|
2516
|
+
t("onboard.enterDataFinderTimezone"),
|
|
2517
|
+
previous?.timezone ?? DEFAULT_DATAFINDER_TIMEZONE
|
|
2518
|
+
),
|
|
2519
|
+
"timezone"
|
|
2520
|
+
)
|
|
2521
|
+
};
|
|
2522
|
+
const summary = await new DataFinderClient(candidate).checkConnection();
|
|
2523
|
+
config.datafinder = toStoredDataFinderConfig(candidate);
|
|
2524
|
+
config.setup.datafinderConfigured = true;
|
|
2525
|
+
logger.success(
|
|
2526
|
+
t("onboard.datafinderSucceeded", {
|
|
2527
|
+
dashboardName: summary.dashboardName ?? summary.dashboardId,
|
|
2528
|
+
reportCount: summary.reportCount
|
|
2529
|
+
})
|
|
2530
|
+
);
|
|
2531
|
+
};
|
|
2532
|
+
var runOnboard = async (options) => {
|
|
2533
|
+
const config = await loadConfig();
|
|
2534
|
+
const t = createI18n(config.locale);
|
|
2535
|
+
const mode = options.setMode ?? "all";
|
|
2536
|
+
if (!onboardModes.includes(mode)) {
|
|
2537
|
+
throw new CliError(`Invalid onboard mode: ${mode}`, 1);
|
|
2538
|
+
}
|
|
2539
|
+
if (mode === "all" || mode === "locale") {
|
|
2540
|
+
const locale = await ui.select({
|
|
2541
|
+
message: t("onboard.chooseLocale"),
|
|
2542
|
+
choices: [
|
|
2543
|
+
{ value: "zh-CN", name: "\u4E2D\u6587" },
|
|
2544
|
+
{ value: "skip", name: t("common.skip") }
|
|
2545
|
+
]
|
|
2546
|
+
});
|
|
2547
|
+
if (locale !== "skip") {
|
|
2548
|
+
config.locale = locale;
|
|
2549
|
+
}
|
|
2550
|
+
config.setup.localeConfigured = true;
|
|
2551
|
+
}
|
|
2552
|
+
if (mode === "all" || mode === "admin") {
|
|
2553
|
+
const action = mode === "admin" ? "continue" : await ui.select({
|
|
2554
|
+
message: t("onboard.adminSection"),
|
|
2555
|
+
choices: [
|
|
2556
|
+
{ value: "continue", name: t("common.continue") },
|
|
2557
|
+
{ value: "skip", name: t("common.skip") }
|
|
2558
|
+
]
|
|
2559
|
+
});
|
|
2560
|
+
if (action === "continue") {
|
|
2561
|
+
await runAdminSetup(config, options, t);
|
|
2562
|
+
} else {
|
|
2563
|
+
logger.info(t("onboard.skipAdminSetup"));
|
|
2564
|
+
}
|
|
2565
|
+
}
|
|
2566
|
+
if (mode === "all" || mode === "datafinder") {
|
|
2567
|
+
const action = mode === "datafinder" ? "continue" : await ui.select({
|
|
2568
|
+
message: t("onboard.datafinderSection"),
|
|
2569
|
+
choices: [
|
|
2570
|
+
{ value: "continue", name: t("common.continue") },
|
|
2571
|
+
{ value: "skip", name: t("common.skip") }
|
|
2572
|
+
]
|
|
2573
|
+
});
|
|
2574
|
+
if (action === "continue") {
|
|
2575
|
+
await runDataFinderSetup(config, options, t);
|
|
2576
|
+
} else {
|
|
2577
|
+
logger.info(t("onboard.skipDataFinderSetup"));
|
|
2578
|
+
}
|
|
2579
|
+
}
|
|
2580
|
+
if (mode === "all" || mode === "gh") {
|
|
2581
|
+
const action = mode === "gh" ? "continue" : await ui.select({
|
|
2582
|
+
message: t("onboard.ghSection"),
|
|
2583
|
+
choices: [
|
|
2584
|
+
{ value: "continue", name: t("common.continue") },
|
|
2585
|
+
{ value: "skip", name: t("common.skip") }
|
|
2586
|
+
]
|
|
2587
|
+
});
|
|
2588
|
+
if (action === "continue") {
|
|
2589
|
+
config.setup.ghConfigured = await runGhSetup(t);
|
|
2590
|
+
} else {
|
|
2591
|
+
logger.info(t("onboard.skipGhSetup"));
|
|
2592
|
+
config.setup.ghConfigured = true;
|
|
2593
|
+
}
|
|
2594
|
+
}
|
|
2595
|
+
if (mode === "all" || mode === "aws") {
|
|
2596
|
+
const action = mode === "aws" ? "continue" : await ui.select({
|
|
2597
|
+
message: t("onboard.awsSection"),
|
|
2598
|
+
choices: [
|
|
2599
|
+
{ value: "continue", name: t("common.continue") },
|
|
2600
|
+
{ value: "skip", name: t("common.skip") }
|
|
2601
|
+
]
|
|
2602
|
+
});
|
|
2603
|
+
if (action === "continue") {
|
|
2604
|
+
const result = await runAwsSetup(config.defaultAwsProfile, t);
|
|
2605
|
+
config.setup.awsConfigured = result.configured;
|
|
2606
|
+
if (result.configured) {
|
|
2607
|
+
config.defaultAwsProfile = result.profile;
|
|
2608
|
+
}
|
|
2609
|
+
} else {
|
|
2610
|
+
logger.info(t("onboard.skipAwsSetup"));
|
|
2611
|
+
config.setup.awsConfigured = true;
|
|
2612
|
+
}
|
|
2613
|
+
}
|
|
2614
|
+
await saveConfig(config);
|
|
2615
|
+
logger.success(t("onboard.complete"));
|
|
2616
|
+
};
|
|
2617
|
+
var registerOnboardCommand = (program) => {
|
|
2618
|
+
program.command("onboard").description("Install or configure local prerequisites").option("--set-mode <mode>", "setup only one flow: all|admin|aws|datafinder|gh|locale").option("--admin-url <url>", "management API URL or admin.tutti.sh page URL").option("--datafinder-url <url>", "DataFinder OpenAPI origin").option("--datafinder-project-id <id>", "DataFinder project ID").option("--datafinder-app-id <id>", "DataFinder application ID").option("--datafinder-dashboard-id <id>", "default DataFinder dashboard ID").option("--datafinder-path-mode <mode>", "DataFinder API path mode: project|app").action((options) => runOnboard(options));
|
|
2619
|
+
};
|
|
2620
|
+
|
|
2621
|
+
// src/constants/ppe-lane-repos.ts
|
|
2622
|
+
var ppeLaneRepos = [
|
|
2623
|
+
{
|
|
2624
|
+
key: "tutti-server",
|
|
2625
|
+
label: "tutti-server",
|
|
2626
|
+
repoFullName: "tutti-lab/tutti-server",
|
|
2627
|
+
deployWorkflowFile: "deploy-lane.yml",
|
|
2628
|
+
destroyWorkflowFile: "destroy-lane.yml",
|
|
2629
|
+
defaultRef: "main",
|
|
2630
|
+
defaultDestroySelection: false,
|
|
2631
|
+
cloudMapServiceId: "srv-izinli73rwxpf2dr",
|
|
2632
|
+
serviceName: "tutti-server"
|
|
2633
|
+
},
|
|
2634
|
+
{
|
|
2635
|
+
key: "zk-admin-server",
|
|
2636
|
+
label: "zk-admin-server",
|
|
2637
|
+
repoFullName: "zk-org-test/zk-admin-server",
|
|
2638
|
+
deployWorkflowFile: "deploy-lane.yml",
|
|
2639
|
+
destroyWorkflowFile: "destroy-lane.yml",
|
|
2640
|
+
defaultRef: "main",
|
|
2641
|
+
cloudMapServiceId: "srv-s4fn35h7nen4u5ci",
|
|
2642
|
+
serviceName: "admin"
|
|
2643
|
+
},
|
|
2644
|
+
{
|
|
2645
|
+
key: "account",
|
|
2646
|
+
label: "account",
|
|
2647
|
+
repoFullName: "zk-org-test/account",
|
|
2648
|
+
deployWorkflowFile: "deploy-lane.yml",
|
|
2649
|
+
destroyWorkflowFile: "destroy-lane.yml",
|
|
2650
|
+
defaultRef: "main",
|
|
2651
|
+
cloudMapServiceId: "srv-j2newpni247wsn7h",
|
|
2652
|
+
serviceName: "account"
|
|
2653
|
+
},
|
|
2654
|
+
{
|
|
2655
|
+
key: "zk-websocket",
|
|
2656
|
+
label: "zk-websocket",
|
|
2657
|
+
repoFullName: "zk-org-test/zk-websocket",
|
|
2658
|
+
deployWorkflowFile: "deploy-lane.yml",
|
|
2659
|
+
destroyWorkflowFile: "destroy-lane.yml",
|
|
2660
|
+
defaultRef: "main",
|
|
2661
|
+
cloudMapServiceId: "srv-34fi45edgao7tp2r",
|
|
2662
|
+
serviceName: "zk-websocket"
|
|
2663
|
+
}
|
|
2664
|
+
];
|
|
2665
|
+
|
|
2666
|
+
// src/lib/aws.ts
|
|
2667
|
+
var parseJson = (raw) => JSON.parse(raw);
|
|
2668
|
+
var isSsoAuthFailure = (stderr) => /(token has expired|token.*expired|sso|authorization pending|login)/i.test(stderr);
|
|
2669
|
+
var runAws = async (args) => {
|
|
2670
|
+
const result = await runCommand("aws", args);
|
|
2671
|
+
if (result.exitCode !== 0) {
|
|
2672
|
+
throw new CliError(result.stderr || `AWS command failed: aws ${args.join(" ")}`, 1);
|
|
2673
|
+
}
|
|
2674
|
+
return result.stdout;
|
|
2675
|
+
};
|
|
2676
|
+
var ensureAwsAuthenticated = async (profile) => {
|
|
2677
|
+
const identityArgs = ["sts", "get-caller-identity", "--profile", profile, "--output", "json"];
|
|
2678
|
+
const firstAttempt = await runCommand("aws", identityArgs);
|
|
2679
|
+
if (firstAttempt.exitCode === 0) {
|
|
2680
|
+
return;
|
|
2681
|
+
}
|
|
2682
|
+
if (!isSsoAuthFailure(firstAttempt.stderr)) {
|
|
2683
|
+
throw new CliError(firstAttempt.stderr || `AWS authentication failed for profile ${profile}`, 1);
|
|
2684
|
+
}
|
|
2685
|
+
const loginResult = await runCommand("aws", ["sso", "login", "--profile", profile], {
|
|
2686
|
+
stdio: "inherit"
|
|
2687
|
+
});
|
|
2688
|
+
if (loginResult.exitCode !== 0) {
|
|
2689
|
+
throw new CliError(`AWS SSO login failed for profile ${profile}`, 1);
|
|
2690
|
+
}
|
|
2691
|
+
const secondAttempt = await runCommand("aws", identityArgs);
|
|
2692
|
+
if (secondAttempt.exitCode !== 0) {
|
|
2693
|
+
throw new CliError(`AWS authentication is still invalid for profile ${profile}`, 1);
|
|
2694
|
+
}
|
|
2695
|
+
};
|
|
2696
|
+
var listLaneInstances = async (input2) => {
|
|
2697
|
+
const service = parseJson(
|
|
2698
|
+
await runAws([
|
|
2699
|
+
"servicediscovery",
|
|
2700
|
+
"get-service",
|
|
2701
|
+
"--id",
|
|
2702
|
+
input2.serviceId,
|
|
2703
|
+
"--region",
|
|
2704
|
+
input2.region,
|
|
2705
|
+
"--profile",
|
|
2706
|
+
input2.profile,
|
|
2707
|
+
"--output",
|
|
2708
|
+
"json"
|
|
2709
|
+
])
|
|
2710
|
+
).Service;
|
|
2711
|
+
const namespace = parseJson(
|
|
2712
|
+
await runAws([
|
|
2713
|
+
"servicediscovery",
|
|
2714
|
+
"get-namespace",
|
|
2715
|
+
"--id",
|
|
2716
|
+
service.NamespaceId,
|
|
2717
|
+
"--region",
|
|
2718
|
+
input2.region,
|
|
2719
|
+
"--profile",
|
|
2720
|
+
input2.profile,
|
|
2694
2721
|
"--output",
|
|
2695
2722
|
"json"
|
|
2696
2723
|
])
|
|
@@ -2722,11 +2749,6 @@ var listLaneInstances = async (input2) => {
|
|
|
2722
2749
|
};
|
|
2723
2750
|
};
|
|
2724
2751
|
|
|
2725
|
-
// src/lib/github.ts
|
|
2726
|
-
import { mkdtemp as mkdtemp2, readFile as readFile2, rm } from "fs/promises";
|
|
2727
|
-
import os from "os";
|
|
2728
|
-
import path from "path";
|
|
2729
|
-
|
|
2730
2752
|
// src/constants/release-targets.ts
|
|
2731
2753
|
var CONTROL_REPO_FULL_NAME = "zk-org-test/github-action";
|
|
2732
2754
|
var CONTROL_RELEASE_PREPARE_WORKFLOW_FILE = "controlled-release.yml";
|
|
@@ -2740,30 +2762,6 @@ var releaseTargets = [
|
|
|
2740
2762
|
repoFullName: "zk-org-test/account",
|
|
2741
2763
|
defaultReleaseProfile: "production"
|
|
2742
2764
|
},
|
|
2743
|
-
{
|
|
2744
|
-
key: "design-project",
|
|
2745
|
-
label: "design-project",
|
|
2746
|
-
repoFullName: "zk-org-test/design-project",
|
|
2747
|
-
defaultReleaseProfile: "production"
|
|
2748
|
-
},
|
|
2749
|
-
{
|
|
2750
|
-
key: "zk-web",
|
|
2751
|
-
label: "zk-web",
|
|
2752
|
-
repoFullName: "zk-org-test/zk-web",
|
|
2753
|
-
defaultReleaseProfile: "production"
|
|
2754
|
-
},
|
|
2755
|
-
{
|
|
2756
|
-
key: "zk-agent-engine",
|
|
2757
|
-
label: "zk-agent-engine",
|
|
2758
|
-
repoFullName: "zk-org-test/zk-agent-engine",
|
|
2759
|
-
defaultReleaseProfile: "production"
|
|
2760
|
-
},
|
|
2761
|
-
{
|
|
2762
|
-
key: "zk-commerce",
|
|
2763
|
-
label: "zk-commerce",
|
|
2764
|
-
repoFullName: "zk-org-test/zk-commerce",
|
|
2765
|
-
defaultReleaseProfile: "production"
|
|
2766
|
-
},
|
|
2767
2765
|
{
|
|
2768
2766
|
key: "zk-admin-server",
|
|
2769
2767
|
label: "zk-admin-server",
|
|
@@ -2946,17 +2944,6 @@ var ensureGhAuthenticated = async () => {
|
|
|
2946
2944
|
throw new CliError("GitHub CLI authentication is required", 1);
|
|
2947
2945
|
}
|
|
2948
2946
|
};
|
|
2949
|
-
var getCurrentGitHubLogin = async () => {
|
|
2950
|
-
const result = await runCommand("gh", ["api", "user", "--jq", ".login"]);
|
|
2951
|
-
if (result.exitCode !== 0) {
|
|
2952
|
-
throw new CliError("Failed to resolve current GitHub login", 1);
|
|
2953
|
-
}
|
|
2954
|
-
const login = result.stdout.trim();
|
|
2955
|
-
if (!login) {
|
|
2956
|
-
throw new CliError("Failed to resolve current GitHub login", 1);
|
|
2957
|
-
}
|
|
2958
|
-
return login;
|
|
2959
|
-
};
|
|
2960
2947
|
var resolveBranchForRepo = async (input2) => {
|
|
2961
2948
|
const result = await runCommand("gh", [
|
|
2962
2949
|
"api",
|
|
@@ -3003,32 +2990,6 @@ var dispatchControlWorkflow = async (input2) => {
|
|
|
3003
2990
|
throw new CliError(`Failed to dispatch ${input2.workflowFile} for ${CONTROL_REPO_FULL_NAME}`, 1);
|
|
3004
2991
|
}
|
|
3005
2992
|
};
|
|
3006
|
-
var dispatchCanvasStagingVersionWorkflow = async (input2) => {
|
|
3007
|
-
const args = [
|
|
3008
|
-
"api",
|
|
3009
|
-
`repos/${input2.repoFullName}/actions/workflows/${input2.workflowFile}/dispatches`,
|
|
3010
|
-
"--method",
|
|
3011
|
-
"POST",
|
|
3012
|
-
"-f",
|
|
3013
|
-
`ref=${input2.workflowRef}`,
|
|
3014
|
-
"-f",
|
|
3015
|
-
`inputs[target_ref]=${input2.targetRef}`
|
|
3016
|
-
];
|
|
3017
|
-
if (input2.versionMessage) {
|
|
3018
|
-
args.push("-f", `inputs[version_message]=${input2.versionMessage}`);
|
|
3019
|
-
}
|
|
3020
|
-
if (input2.versionTag) {
|
|
3021
|
-
args.push("-f", `inputs[version_tag]=${input2.versionTag}`);
|
|
3022
|
-
}
|
|
3023
|
-
const result = await runCommand("gh", args);
|
|
3024
|
-
if (result.exitCode !== 0) {
|
|
3025
|
-
const detail = getCommandFailureDetail(result.stderr, result.stdout);
|
|
3026
|
-
throw new CliError(
|
|
3027
|
-
detail ? `Failed to dispatch ${input2.workflowFile} for ${input2.repoFullName}: ${detail}` : `Failed to dispatch ${input2.workflowFile} for ${input2.repoFullName}`,
|
|
3028
|
-
1
|
|
3029
|
-
);
|
|
3030
|
-
}
|
|
3031
|
-
};
|
|
3032
2993
|
var dispatchWorkflow = async (input2) => {
|
|
3033
2994
|
const result = await runCommand("gh", [
|
|
3034
2995
|
"api",
|
|
@@ -3066,50 +3027,6 @@ var listOpenIssueIds = async (repoFullName) => {
|
|
|
3066
3027
|
}
|
|
3067
3028
|
return JSON.parse(result.stdout).map((issue) => issue.id);
|
|
3068
3029
|
};
|
|
3069
|
-
var parseCanvasStagingVersionArtifact = (payload) => {
|
|
3070
|
-
const parsed = JSON.parse(payload);
|
|
3071
|
-
if (!parsed.deployment_id || !parsed.version_id || !parsed.override_header || !parsed.worker_name || !parsed.workflow_run_url) {
|
|
3072
|
-
throw new CliError("Failed to parse canvas staging version artifact metadata", 1);
|
|
3073
|
-
}
|
|
3074
|
-
return {
|
|
3075
|
-
deploymentId: String(parsed.deployment_id),
|
|
3076
|
-
overrideHeader: String(parsed.override_header),
|
|
3077
|
-
previewAliasUrl: parsed.preview_alias_url ? String(parsed.preview_alias_url) : null,
|
|
3078
|
-
previewUrl: parsed.preview_url ? String(parsed.preview_url) : null,
|
|
3079
|
-
versionId: String(parsed.version_id),
|
|
3080
|
-
versionMessage: String(parsed.version_message ?? ""),
|
|
3081
|
-
versionTag: String(parsed.version_tag ?? ""),
|
|
3082
|
-
workerName: String(parsed.worker_name),
|
|
3083
|
-
workflowRunUrl: String(parsed.workflow_run_url)
|
|
3084
|
-
};
|
|
3085
|
-
};
|
|
3086
|
-
var fetchCanvasStagingVersionArtifact = async (input2) => {
|
|
3087
|
-
const tempDir = await mkdtemp2(path.join(os.tmpdir(), "nexight-devops-canvas-"));
|
|
3088
|
-
try {
|
|
3089
|
-
const downloadResult = await runCommand("gh", [
|
|
3090
|
-
"run",
|
|
3091
|
-
"download",
|
|
3092
|
-
String(input2.runId),
|
|
3093
|
-
"--repo",
|
|
3094
|
-
input2.repoFullName,
|
|
3095
|
-
"--name",
|
|
3096
|
-
input2.artifactName,
|
|
3097
|
-
"--dir",
|
|
3098
|
-
tempDir
|
|
3099
|
-
]);
|
|
3100
|
-
if (downloadResult.exitCode !== 0) {
|
|
3101
|
-
const detail = getCommandFailureDetail(downloadResult.stderr, downloadResult.stdout);
|
|
3102
|
-
throw new CliError(
|
|
3103
|
-
detail ? `Failed to download artifact ${input2.artifactName} for run ${input2.runId}: ${detail}` : `Failed to download artifact ${input2.artifactName} for run ${input2.runId}`,
|
|
3104
|
-
1
|
|
3105
|
-
);
|
|
3106
|
-
}
|
|
3107
|
-
const payload = await readFile2(path.join(tempDir, input2.artifactFileName), "utf8");
|
|
3108
|
-
return parseCanvasStagingVersionArtifact(payload);
|
|
3109
|
-
} finally {
|
|
3110
|
-
await rm(tempDir, { recursive: true, force: true });
|
|
3111
|
-
}
|
|
3112
|
-
};
|
|
3113
3030
|
var listControlledReleaseIssues = async (state) => {
|
|
3114
3031
|
const result = await runCommand("gh", [
|
|
3115
3032
|
"api",
|
|
@@ -3306,780 +3223,63 @@ var pollWorkflowRun = async (input2) => {
|
|
|
3306
3223
|
try {
|
|
3307
3224
|
runs = await listWorkflowRuns(input2);
|
|
3308
3225
|
break;
|
|
3309
|
-
} catch (error) {
|
|
3310
|
-
if (!(error instanceof CliError) || !isTransientWorkflowRunReadFailure(error.message) || readAttempt === TRANSIENT_WORKFLOW_RUN_READ_ATTEMPTS - 1) {
|
|
3311
|
-
throw error;
|
|
3312
|
-
}
|
|
3313
|
-
input2.onUpdate?.(trackedRunId === null ? { status: "pending" } : { runId: trackedRunId, status: "pending" });
|
|
3314
|
-
await sleep(intervalMs);
|
|
3315
|
-
}
|
|
3316
|
-
}
|
|
3317
|
-
if (!runs) {
|
|
3318
|
-
throw new CliError(`Timed out waiting for workflow run in ${input2.repoFullName}`, 1);
|
|
3319
|
-
}
|
|
3320
|
-
if (trackedRunId === null) {
|
|
3321
|
-
const candidates = runs.filter((run) => !knownRunIds.has(run.id)).filter((run) => matchesWorkflowRunCorrelation(run, input2.correlation));
|
|
3322
|
-
if (candidates.length > 1) {
|
|
3323
|
-
throw new CliError(`Ambiguous workflow run selection for ${input2.repoFullName}`, 1);
|
|
3324
|
-
}
|
|
3325
|
-
if (candidates.length === 0) {
|
|
3326
|
-
input2.onUpdate?.({ status: "pending" });
|
|
3327
|
-
await sleep(intervalMs);
|
|
3328
|
-
continue;
|
|
3329
|
-
}
|
|
3330
|
-
trackedRunId = candidates[0]?.id ?? null;
|
|
3331
|
-
}
|
|
3332
|
-
const matchedRun = runs.find((run) => run.id === trackedRunId);
|
|
3333
|
-
if (!matchedRun) {
|
|
3334
|
-
input2.onUpdate?.({ runId: trackedRunId, status: "pending" });
|
|
3335
|
-
await sleep(intervalMs);
|
|
3336
|
-
continue;
|
|
3337
|
-
}
|
|
3338
|
-
input2.onUpdate?.({
|
|
3339
|
-
runId: trackedRunId,
|
|
3340
|
-
htmlUrl: matchedRun.html_url,
|
|
3341
|
-
status: matchedRun.status === "completed" ? "completed" : "in_progress"
|
|
3342
|
-
});
|
|
3343
|
-
if (matchedRun.status === "completed") {
|
|
3344
|
-
const completion = {
|
|
3345
|
-
conclusion: matchedRun.conclusion ?? "unknown",
|
|
3346
|
-
htmlUrl: matchedRun.html_url,
|
|
3347
|
-
runId: matchedRun.id,
|
|
3348
|
-
status: matchedRun.status
|
|
3349
|
-
};
|
|
3350
|
-
if (completion.conclusion !== "success") {
|
|
3351
|
-
const failureMessage = await fetchWorkflowRunFailureMessage({
|
|
3352
|
-
repoFullName: input2.repoFullName,
|
|
3353
|
-
runId: matchedRun.id
|
|
3354
|
-
});
|
|
3355
|
-
if (failureMessage) {
|
|
3356
|
-
completion.failureMessage = failureMessage;
|
|
3357
|
-
}
|
|
3358
|
-
}
|
|
3359
|
-
return completion;
|
|
3360
|
-
}
|
|
3361
|
-
await sleep(intervalMs);
|
|
3362
|
-
}
|
|
3363
|
-
throw new CliError(`Timed out waiting for workflow run in ${input2.repoFullName}`, 1);
|
|
3364
|
-
};
|
|
3365
|
-
|
|
3366
|
-
// src/lib/prerequisites.ts
|
|
3367
|
-
var ensureCommandInstalled = async (command, installHint) => {
|
|
3368
|
-
const result = await runCommand(command, ["--version"]);
|
|
3369
|
-
if (result.exitCode !== 0) {
|
|
3370
|
-
throw new CliError(installHint, 1);
|
|
3371
|
-
}
|
|
3372
|
-
};
|
|
3373
|
-
var ensurePrerequisites = async (input2) => {
|
|
3374
|
-
const config = await loadConfig();
|
|
3375
|
-
if (input2.needsGh) {
|
|
3376
|
-
await ensureCommandInstalled("gh", "gh is required. Run nexight-devops onboard --set-mode gh");
|
|
3377
|
-
await ensureGhAuthenticated();
|
|
3378
|
-
}
|
|
3379
|
-
if (input2.needsAws) {
|
|
3380
|
-
await ensureCommandInstalled("aws", "aws is required. Run nexight-devops onboard --set-mode aws");
|
|
3381
|
-
await ensureAwsAuthenticated(config.defaultAwsProfile);
|
|
3382
|
-
}
|
|
3383
|
-
return config;
|
|
3384
|
-
};
|
|
3385
|
-
|
|
3386
|
-
// src/commands/diagnose/shared-agent.ts
|
|
3387
|
-
var EXPECTED_PRODUCTION_ACCOUNT = "250509935467";
|
|
3388
|
-
var DEFAULT_REGION = "us-east-1";
|
|
3389
|
-
var parseWindowMinutes = (value) => {
|
|
3390
|
-
if (!/^\d+$/.test(value.trim())) {
|
|
3391
|
-
throw new CliError("--window-minutes must be an integer", 1);
|
|
3392
|
-
}
|
|
3393
|
-
return Number(value);
|
|
3394
|
-
};
|
|
3395
|
-
var formatBytes = (bytes) => {
|
|
3396
|
-
if (bytes < 1024) {
|
|
3397
|
-
return `${bytes} B`;
|
|
3398
|
-
}
|
|
3399
|
-
if (bytes < 1024 * 1024) {
|
|
3400
|
-
return `${(bytes / 1024).toFixed(1)} KiB`;
|
|
3401
|
-
}
|
|
3402
|
-
return `${(bytes / (1024 * 1024)).toFixed(1)} MiB`;
|
|
3403
|
-
};
|
|
3404
|
-
var printSharedAgentDiagnosis = (result) => {
|
|
3405
|
-
logger.info(`\u7ED3\u8BBA [${result.verdict.code}/${result.verdict.confidence}] ${result.verdict.summary}`);
|
|
3406
|
-
logger.step(`\u65F6\u95F4\u7A97 ${result.window.from} \u2192 ${result.window.to}`);
|
|
3407
|
-
const ownerParts = [
|
|
3408
|
-
result.owner.userId ? `user=${result.owner.userId}` : void 0,
|
|
3409
|
-
result.owner.deviceId ? `device=${result.owner.deviceId}` : void 0,
|
|
3410
|
-
result.owner.clientVersion ? `version=${result.owner.clientVersion}` : void 0
|
|
3411
|
-
].filter(Boolean);
|
|
3412
|
-
logger.step(`Owner ${ownerParts.length > 0 ? ownerParts.join(" | ") : "\u672A\u8BC6\u522B"}`);
|
|
3413
|
-
const descriptor = result.descriptor.latest;
|
|
3414
|
-
logger.step(
|
|
3415
|
-
descriptor ? `Descriptor ${descriptor.timestamp} | ${descriptor.channel}/${descriptor.outcome}${descriptor.reason ? ` | ${descriptor.reason}` : ""}` : "Descriptor \u5F53\u524D\u65F6\u95F4\u7A97\u65E0\u8BC1\u636E"
|
|
3416
|
-
);
|
|
3417
|
-
const lease = result.ownerLease.latest;
|
|
3418
|
-
logger.step(
|
|
3419
|
-
lease ? `Owner lease ${lease.timestamp} | ${lease.outcome}/${lease.reasonCode} | version=${lease.clientVersion}` : "Owner lease \u5F53\u524D\u65F6\u95F4\u7A97\u65E0\u53EF\u5173\u8054\u89C2\u6D4B"
|
|
3420
|
-
);
|
|
3421
|
-
logger.step(`HTTP failures ${result.httpFailures.count}`);
|
|
3422
|
-
result.queryReports.forEach((report) => {
|
|
3423
|
-
if (report.status === "complete" && report.stats) {
|
|
3424
|
-
logger.step(
|
|
3425
|
-
`${report.name} query | matched=${report.stats.recordsMatched} scanned=${report.stats.recordsScanned} bytes=${formatBytes(
|
|
3426
|
-
report.stats.bytesScanned
|
|
3427
|
-
)}`
|
|
3428
|
-
);
|
|
3429
|
-
}
|
|
3430
|
-
});
|
|
3431
|
-
result.warnings.forEach((warning) => logger.warn(warning));
|
|
3432
|
-
result.missingEvidence.forEach((missing) => logger.warn(`\u7F3A\u5931\u8BC1\u636E: ${missing}`));
|
|
3433
|
-
};
|
|
3434
|
-
var runSharedAgentDiagnosis = async (options) => {
|
|
3435
|
-
const config = await loadConfig();
|
|
3436
|
-
const profile = options.profile?.trim() || config.defaultAwsProfile;
|
|
3437
|
-
await ensureCommandInstalled("aws", "aws is required. Run nexight-devops onboard --set-mode aws");
|
|
3438
|
-
await ensureAwsAuthenticated(profile);
|
|
3439
|
-
const identity = await getAwsCallerIdentity(profile);
|
|
3440
|
-
if (identity.account !== EXPECTED_PRODUCTION_ACCOUNT) {
|
|
3441
|
-
throw new CliError(
|
|
3442
|
-
`AWS profile ${profile} is authenticated to account ${identity.account}; expected ${EXPECTED_PRODUCTION_ACCOUNT}`,
|
|
3443
|
-
1
|
|
3444
|
-
);
|
|
3445
|
-
}
|
|
3446
|
-
const run = () => diagnoseSharedAgent({
|
|
3447
|
-
account: identity.account,
|
|
3448
|
-
profile,
|
|
3449
|
-
region: options.region,
|
|
3450
|
-
logGroup: options.logGroup,
|
|
3451
|
-
namespace: options.namespace,
|
|
3452
|
-
roomId: options.roomId,
|
|
3453
|
-
sharedAgentId: options.sharedAgentId,
|
|
3454
|
-
ownerDeviceId: options.ownerDeviceId,
|
|
3455
|
-
authorityId: options.authorityId,
|
|
3456
|
-
anchor: parseDiagnosisAnchor(options.at),
|
|
3457
|
-
windowMinutes: parseWindowMinutes(options.windowMinutes)
|
|
3458
|
-
});
|
|
3459
|
-
const result = options.json ? await run() : await withProgressTask(
|
|
3460
|
-
{
|
|
3461
|
-
initialText: "\u6B63\u5728\u67E5\u8BE2\u5171\u4EAB Agent \u63A7\u5236\u9762\u8BC1\u636E",
|
|
3462
|
-
heartbeatText: (elapsedMs) => `\u6B63\u5728\u67E5\u8BE2\u5171\u4EAB Agent \u63A7\u5236\u9762\u8BC1\u636E\uFF08${Math.floor(elapsedMs / 1e3)}s\uFF09`
|
|
3463
|
-
},
|
|
3464
|
-
run
|
|
3465
|
-
);
|
|
3466
|
-
if (options.json) {
|
|
3467
|
-
console.log(JSON.stringify(result, null, 2));
|
|
3468
|
-
} else {
|
|
3469
|
-
printSharedAgentDiagnosis(result);
|
|
3470
|
-
}
|
|
3471
|
-
return result;
|
|
3472
|
-
};
|
|
3473
|
-
var registerSharedAgentDiagnosisCommand = (program) => {
|
|
3474
|
-
program.command("shared-agent").description("Diagnose Shared Agent connectivity from bounded production CloudWatch evidence").requiredOption("--room-id <roomId>", "room ID from the user report or requester diagnostics").requiredOption("--at <timestamp>", "incident timestamp with timezone, for example 2026-08-12T12:00:00+08:00").option("--shared-agent-id <sharedAgentId>", "narrow descriptor evidence to one Shared Agent").option("--owner-device-id <ownerDeviceId>", "known Owner device ID, used to join lease evidence").option("--authority-id <authorityId>", "known Device Authority ID, used to join lease evidence").option("--window-minutes <minutes>", "bounded window centered on --at", String(DEFAULT_DIAGNOSIS_WINDOW_MINUTES)).option("--profile <profile>", "AWS CLI profile; defaults to the configured nexight-devops profile").option("--region <region>", "AWS region", DEFAULT_REGION).option("--log-group <logGroup>", "CloudWatch application log group", DEFAULT_TSH_LOG_GROUP).option("--namespace <namespace>", "Kubernetes namespace", DEFAULT_TSH_NAMESPACE).option("--json", "print stable machine-readable JSON without CLI decorations").action(async (options) => {
|
|
3475
|
-
await runSharedAgentDiagnosis(options);
|
|
3476
|
-
});
|
|
3477
|
-
};
|
|
3478
|
-
|
|
3479
|
-
// src/commands/diagnose/index.ts
|
|
3480
|
-
var registerDiagnoseCommand = (program) => {
|
|
3481
|
-
const diagnose = program.command("diagnose").description("Run deterministic incident diagnosis playbooks");
|
|
3482
|
-
registerSharedAgentDiagnosisCommand(diagnose);
|
|
3483
|
-
};
|
|
3484
|
-
|
|
3485
|
-
// src/commands/doctor.ts
|
|
3486
|
-
var registerDoctorCommand = (program) => {
|
|
3487
|
-
program.command("doctor").description("Run a local readiness check for the CLI shell").action(() => {
|
|
3488
|
-
logger.step("Running basic readiness checks");
|
|
3489
|
-
logger.success("nexight-devops is ready");
|
|
3490
|
-
});
|
|
3491
|
-
};
|
|
3492
|
-
|
|
3493
|
-
// src/lib/datafinder/types.ts
|
|
3494
|
-
var DEFAULT_DATAFINDER_BASE_URL = "https://analytics.volcengineapi.com";
|
|
3495
|
-
var DEFAULT_DATAFINDER_TIMEZONE = "Asia/Shanghai";
|
|
3496
|
-
|
|
3497
|
-
// src/locales/zh-CN.ts
|
|
3498
|
-
var zhCN = {
|
|
3499
|
-
common: {
|
|
3500
|
-
continue: "\u7EE7\u7EED",
|
|
3501
|
-
skip: "\u8DF3\u8FC7"
|
|
3502
|
-
},
|
|
3503
|
-
errors: {
|
|
3504
|
-
missingAws: "\u672A\u68C0\u6D4B\u5230 aws\uFF0C\u8BF7\u5148\u5B8C\u6210 AWS CLI \u5B89\u88C5\u3002",
|
|
3505
|
-
missingGh: "\u672A\u68C0\u6D4B\u5230 gh\uFF0C\u8BF7\u5148\u5B8C\u6210 GitHub CLI \u5B89\u88C5\u3002"
|
|
3506
|
-
},
|
|
3507
|
-
onboard: {
|
|
3508
|
-
adminLoginSucceeded: "\u7BA1\u7406\u540E\u53F0\u767B\u5F55\u6210\u529F",
|
|
3509
|
-
adminSection: "\u7BA1\u7406\u540E\u53F0\u767B\u5F55\u8BBE\u7F6E",
|
|
3510
|
-
adminTokenRequired: "\u7BA1\u7406\u540E\u53F0 Token \u4E0D\u80FD\u4E3A\u7A7A",
|
|
3511
|
-
awsSection: "AWS CLI \u8BBE\u7F6E",
|
|
3512
|
-
complete: "Onboard \u5B8C\u6210",
|
|
3513
|
-
chooseAwsProfile: "\u8BF7\u9009\u62E9\u9ED8\u8BA4 AWS Profile",
|
|
3514
|
-
chooseLocale: "\u8BF7\u9009\u62E9\u547D\u4EE4\u884C\u8BED\u8A00",
|
|
3515
|
-
datafinderAppPathMode: "\u5E94\u7528\u8DEF\u5F84\uFF08\u4F20\u7EDF SaaS / \u79C1\u6709\u5316\uFF09",
|
|
3516
|
-
datafinderCredentialAction: "\u5DF2\u914D\u7F6E DataFinder \u51ED\u8BC1\uFF0C\u8BF7\u9009\u62E9\u5904\u7406\u65B9\u5F0F",
|
|
3517
|
-
datafinderKeepCredentials: "\u4FDD\u7559\u73B0\u6709\u51ED\u8BC1",
|
|
3518
|
-
datafinderProjectPathMode: "\u9879\u76EE\u8DEF\u5F84\uFF08SaaS \u4E91\u539F\u751F\uFF0C\u63A8\u8350\uFF09",
|
|
3519
|
-
datafinderReplaceCredentials: "\u91CD\u65B0\u8F93\u5165\u51ED\u8BC1",
|
|
3520
|
-
datafinderSection: "DataFinder OpenAPI \u8BBE\u7F6E",
|
|
3521
|
-
datafinderSucceeded: "DataFinder \u8FDE\u63A5\u6210\u529F\uFF1A{dashboardName}\uFF0C\u5171 {reportCount} \u4E2A\u62A5\u8868",
|
|
3522
|
-
enterGhToken: "\u8BF7\u8F93\u5165 GitHub Token",
|
|
3523
|
-
enterAdminToken: "\u8BF7\u8F93\u5165 Tutti \u7BA1\u7406\u540E\u53F0 Token",
|
|
3524
|
-
enterDataFinderAk: "\u8BF7\u8F93\u5165 DataFinder AccessKeyId",
|
|
3525
|
-
enterDataFinderAppId: "\u8BF7\u8F93\u5165 DataFinder \u5E94\u7528 ID",
|
|
3526
|
-
enterDataFinderDashboardId: "\u8BF7\u8F93\u5165\u9ED8\u8BA4 DataFinder \u770B\u677F ID",
|
|
3527
|
-
enterDataFinderProjectId: "\u8BF7\u8F93\u5165 DataFinder \u9879\u76EE ID",
|
|
3528
|
-
enterDataFinderSk: "\u8BF7\u8F93\u5165 DataFinder SecretAccessKey",
|
|
3529
|
-
enterDataFinderTimezone: "\u8BF7\u8F93\u5165 DataFinder \u67E5\u8BE2\u65F6\u533A",
|
|
3530
|
-
enterDataFinderUrl: "\u8BF7\u8F93\u5165 DataFinder OpenAPI \u5730\u5740",
|
|
3531
|
-
ghInstallDeferred: "\u672A\u5B8C\u6210 gh \u5B89\u88C5\u3002\u4F60\u53EF\u4EE5\u7A0D\u540E\u8FD0\u884C nexight-devops onboard --set-mode gh",
|
|
3532
|
-
ghLoginToken: "\u4F7F\u7528 Token",
|
|
3533
|
-
ghLoginWeb: "\u6D4F\u89C8\u5668\u767B\u5F55\uFF08\u63A8\u8350\uFF09",
|
|
3534
|
-
ghSection: "GitHub CLI \u8BBE\u7F6E",
|
|
3535
|
-
installAwsPrompt: "\u672A\u68C0\u6D4B\u5230 aws\uFF0C\u662F\u5426\u73B0\u5728\u5C1D\u8BD5\u5B89\u88C5\uFF1F",
|
|
3536
|
-
installBrewFailed: "Homebrew \u5B89\u88C5\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u624B\u52A8\u91CD\u8BD5\u3002",
|
|
3537
|
-
installBrewPrompt: "\u672A\u68C0\u6D4B\u5230 Homebrew\uFF0C\u662F\u5426\u73B0\u5728\u5C1D\u8BD5\u5B89\u88C5\uFF1F",
|
|
3538
|
-
installGhPrompt: "\u672A\u68C0\u6D4B\u5230 gh\uFF0C\u662F\u5426\u73B0\u5728\u5C1D\u8BD5\u5B89\u88C5\uFF1F",
|
|
3539
|
-
installManualPrefix: "\u8BF7\u5148\u5B89\u88C5 Homebrew\uFF0C\u518D\u6267\u884C",
|
|
3540
|
-
selectDataFinderPathMode: "\u8BF7\u9009\u62E9 DataFinder OpenAPI \u8DEF\u5F84\u6A21\u5F0F",
|
|
3541
|
-
selectGhLoginMode: "\u8BF7\u9009\u62E9 GitHub \u767B\u5F55\u65B9\u5F0F",
|
|
3542
|
-
skipGhSetup: "\u8DF3\u8FC7 GitHub CLI \u8BBE\u7F6E",
|
|
3543
|
-
skipAdminSetup: "\u8DF3\u8FC7\u7BA1\u7406\u540E\u53F0\u767B\u5F55\u8BBE\u7F6E",
|
|
3544
|
-
skipAwsSetup: "\u8DF3\u8FC7 AWS CLI \u8BBE\u7F6E",
|
|
3545
|
-
skipDataFinderSetup: "\u8DF3\u8FC7 DataFinder OpenAPI \u8BBE\u7F6E",
|
|
3546
|
-
awsInstallDeferred: "\u672A\u5B8C\u6210 AWS CLI \u5B89\u88C5\u3002\u4F60\u53EF\u4EE5\u7A0D\u540E\u8FD0\u884C nexight-devops onboard --set-mode aws",
|
|
3547
|
-
title: "\u5F00\u59CB\u521D\u59CB\u5316 nexight-devops"
|
|
3548
|
-
},
|
|
3549
|
-
release: {
|
|
3550
|
-
common: {
|
|
3551
|
-
approvalIssueUrl: "\u5BA1\u6279\u5355\u5730\u5740: {approvalIssueUrl}",
|
|
3552
|
-
candidateTag: "\u5019\u9009\u7248\u672C: {candidateTag}",
|
|
3553
|
-
controlRunUrl: "\u63A7\u5236\u9762\u8FD0\u884C\u5730\u5740: {controlRunUrl}",
|
|
3554
|
-
releaseBranch: "\u53D1\u5E03\u5206\u652F: {releaseBranch}",
|
|
3555
|
-
releaseProfile: "\u53D1\u5E03 Profile: {releaseProfile}",
|
|
3556
|
-
targetRepo: "\u76EE\u6807\u4ED3\u5E93: {targetRepo}"
|
|
3557
|
-
},
|
|
3558
|
-
approve: {
|
|
3559
|
-
approvalIssueMissing: "\u672A\u627E\u5230\u53EF\u5BA1\u6279\u7684\u53D1\u5E03\u5355\uFF0C\u8BF7\u91CD\u65B0\u9009\u62E9\u76EE\u6807\u4ED3\u5E93\u6216\u68C0\u67E5\u63A7\u5236\u9762\u72B6\u6001\u3002",
|
|
3560
|
-
approvalWorkflowRunUrl: "\u5BA1\u6279\u5DE5\u4F5C\u6D41\u5730\u5740: {approvalWorkflowRunUrl}",
|
|
3561
|
-
cancelled: "\u5DF2\u53D6\u6D88 release approve\u3002",
|
|
3562
|
-
commenting: "\u6B63\u5728\u5BF9\u53D1\u5E03\u5355 #{issueNumber} \u6267\u884C /release-approve",
|
|
3563
|
-
closeIssueWarning: "\u5BA1\u6279\u5355 #{issueNumber} \u81EA\u52A8\u5173\u95ED\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u624B\u52A8\u5173\u95ED\u3002",
|
|
3564
|
-
confirm: "\u786E\u8BA4\u5BF9\u53D1\u5E03\u5355 #{issueNumber}\uFF08{candidateTag} / {releaseBranch}\uFF09\u6267\u884C /release-approve\uFF1F",
|
|
3565
|
-
feishuReminder: "\u98DE\u4E66\u63D0\u9192\uFF1A\u6B63\u5F0F\u53D1\u7248\u5DF2\u5F00\u59CB\uFF0C\u8BF7\u7ACB\u5373\u5173\u6CE8\u98DE\u4E66\u7FA4\uFF0C\u6301\u7EED\u8DDF\u8FDB\u53D1\u5E03\u72B6\u6001\u3002",
|
|
3566
|
-
noPendingIssues: "\u5F53\u524D\u6CA1\u6709\u5F85\u5BA1\u6279\u7684\u53D1\u5E03\u5355\uFF0C\u76EE\u6807\u4ED3\u5E93 {targetRepo} \u6682\u65E0\u53EF\u5904\u7406\u9879\u3002",
|
|
3567
|
-
selectApprovalIssue: "\u8BF7\u9009\u62E9\u8981\u5BA1\u6279\u7684\u53D1\u5E03\u5355",
|
|
3568
|
-
selectTarget: "\u8BF7\u9009\u62E9\u8981\u5BA1\u6279\u7684\u4ED3\u5E93",
|
|
3569
|
-
success: "release approve \u5DF2\u5B8C\u6210\uFF0C\u6B63\u5F0F\u53D1\u7248\u5DF2\u5F00\u59CB",
|
|
3570
|
-
workflowFailed: "\u5BA1\u6279\u5DE5\u4F5C\u6D41\u6267\u884C\u5931\u8D25\uFF0C\u8BF7\u6253\u5F00\u5BA1\u6279\u5DE5\u4F5C\u6D41\u8FD0\u884C\u67E5\u770B\u8BE6\u60C5: {approvalWorkflowRunUrl}"
|
|
3571
|
-
},
|
|
3572
|
-
hotfix: {
|
|
3573
|
-
allowUnmergedHotfix: "\u662F\u5426\u5141\u8BB8 release \u5206\u652F\u4E0A\u7684\u8865\u4E01\u5C1A\u672A\u88AB main \u5438\u6536\u65F6\u76F4\u63A5\u53D1\u8D77 hotfix\uFF1F",
|
|
3574
|
-
approvalIssueAmbiguous: "\u68C0\u6D4B\u5230\u591A\u4E2A\u65B0\u7684 hotfix \u5BA1\u6279\u5355\uFF0C\u8BF7\u68C0\u67E5\u4EE5\u4E0B\u5019\u9009\u9879: {candidateIssueUrls}\uFF1B\u63A7\u5236\u9762\u8FD0\u884C: {controlRunUrl}",
|
|
3575
|
-
approvalIssueMissing: "\u672A\u627E\u5230\u65B0\u521B\u5EFA\u7684 hotfix \u5BA1\u6279\u5355\uFF0C\u8BF7\u6253\u5F00\u63A7\u5236\u9762\u8FD0\u884C\u67E5\u770B\u8BE6\u60C5: {controlRunUrl}",
|
|
3576
|
-
cancelled: "\u5DF2\u53D6\u6D88 release hotfix\u3002",
|
|
3577
|
-
confirm: "\u786E\u8BA4\u5BF9 {repoName} \u53D1\u8D77 hotfix\uFF0C\u5206\u652F {releaseBranch}\uFF1F",
|
|
3578
|
-
dispatching: "\u6B63\u5728\u51C6\u5907 hotfix: {repoName}",
|
|
3579
|
-
enterReleaseBranch: "\u8BF7\u8F93\u5165 hotfix \u7684 release \u5206\u652F\uFF08\u5FC5\u987B\u4EE5 release/ \u5F00\u5934\uFF09",
|
|
3580
|
-
feishuReminder: "\u98DE\u4E66\u63D0\u9192\uFF1A\u81EA\u6D4B\u53D1\u7248\u901A\u77E5\u5DF2\u53D1\u9001\u5230\u98DE\u4E66\u7FA4\uFF0C\u8BF7\u5173\u6CE8\u98DE\u4E66\u7FA4\u5E76\u5B8C\u6210\u9A8C\u8BC1\uFF0C\u968F\u540E\u6267\u884C nexight-devops release approve\u3002",
|
|
3581
|
-
invalidReleaseBranch: "release \u5206\u652F\u5FC5\u987B\u662F release/<name> \u683C\u5F0F\u3002",
|
|
3582
|
-
nextSteps: "\u4E0B\u4E00\u6B65\uFF1A\u5B8C\u6210\u9A8C\u8BC1\u540E\uFF0C\u6267\u884C nexight-devops release approve\u3002",
|
|
3583
|
-
overrideMode: "Override \u6A21\u5F0F: {overrideMode}",
|
|
3584
|
-
overrideModeDisabled: "\u672A\u5F00\u542F",
|
|
3585
|
-
overrideModeEnabled: "\u5DF2\u5F00\u542F",
|
|
3586
|
-
overrideReason: "\u8BF7\u8F93\u5165 release \u5206\u652F\u4E0A\u7684\u8865\u4E01\u5C1A\u672A\u88AB main \u5438\u6536\u65F6\u7EE7\u7EED hotfix \u7684\u539F\u56E0",
|
|
3587
|
-
overrideReasonRequired: "\u5F00\u542F release \u5206\u652F\u8865\u4E01 override \u65F6\u5FC5\u987B\u586B\u5199\u539F\u56E0\u3002",
|
|
3588
|
-
workflowFailureReason: "\u5931\u8D25\u539F\u56E0: {failureMessage}",
|
|
3589
|
-
selectTarget: "\u8BF7\u9009\u62E9\u8981\u6267\u884C hotfix \u7684\u4ED3\u5E93",
|
|
3590
|
-
success: "release hotfix \u5DF2\u5B8C\u6210",
|
|
3591
|
-
waiting: "\u63A7\u5236\u9762 hotfix \u5DE5\u4F5C\u6D41\u5DF2\u89E6\u53D1\uFF0C\u6B63\u5728\u7B49\u5F85\u5B8C\u6210: {repoName}",
|
|
3592
|
-
workflowFailed: "controlled-hotfix-release \u5DE5\u4F5C\u6D41\u6267\u884C\u5931\u8D25\uFF0C\u8BF7\u6253\u5F00\u63A7\u5236\u9762\u8FD0\u884C\u67E5\u770B\u8BE6\u60C5: {controlRunUrl}"
|
|
3593
|
-
},
|
|
3594
|
-
prepare: {
|
|
3595
|
-
approvalIssueAmbiguous: "\u68C0\u6D4B\u5230\u591A\u4E2A\u65B0\u7684\u53D1\u5E03\u5BA1\u6279\u5355\uFF0C\u8BF7\u68C0\u67E5\u4EE5\u4E0B\u5019\u9009\u9879: {candidateIssueUrls}\uFF1B\u63A7\u5236\u9762\u8FD0\u884C: {controlRunUrl}",
|
|
3596
|
-
approvalIssueMissing: "\u672A\u627E\u5230\u65B0\u521B\u5EFA\u7684\u53D1\u5E03\u5BA1\u6279\u5355\uFF0C\u8BF7\u6253\u5F00\u63A7\u5236\u9762\u8FD0\u884C\u67E5\u770B\u8BE6\u60C5: {controlRunUrl}",
|
|
3597
|
-
cancelled: "\u5DF2\u53D6\u6D88 release prepare\u3002",
|
|
3598
|
-
confirm: "\u786E\u8BA4\u5BF9 {repoName} \u6267\u884C release prepare\uFF1F",
|
|
3599
|
-
dispatching: "\u6B63\u5728\u51C6\u5907\u53D1\u5E03: {repoName}",
|
|
3600
|
-
feishuReminder: "\u98DE\u4E66\u63D0\u9192\uFF1A\u81EA\u6D4B\u53D1\u7248\u901A\u77E5\u5DF2\u53D1\u9001\u5230\u98DE\u4E66\u7FA4\uFF0C\u8BF7\u5173\u6CE8\u98DE\u4E66\u7FA4\u5E76\u5B8C\u6210\u9A8C\u8BC1\uFF0C\u968F\u540E\u6267\u884C nexight-devops release approve\u3002",
|
|
3601
|
-
nextSteps: "\u4E0B\u4E00\u6B65\uFF1A\u5B8C\u6210\u9A8C\u8BC1\u540E\uFF0C\u6267\u884C nexight-devops release approve\u3002",
|
|
3602
|
-
selectTarget: "\u8BF7\u9009\u62E9\u8981\u51C6\u5907\u53D1\u5E03\u7684\u4ED3\u5E93",
|
|
3603
|
-
success: "release prepare \u5DF2\u5B8C\u6210",
|
|
3604
|
-
waiting: "\u63A7\u5236\u9762\u53D1\u5E03\u5DE5\u4F5C\u6D41\u5DF2\u89E6\u53D1\uFF0C\u6B63\u5728\u7B49\u5F85\u5B8C\u6210: {repoName}",
|
|
3605
|
-
workflowFailed: "controlled-release \u5DE5\u4F5C\u6D41\u6267\u884C\u5931\u8D25\uFF0C\u8BF7\u6253\u5F00\u63A7\u5236\u9762\u8FD0\u884C\u67E5\u770B\u8BE6\u60C5: {controlRunUrl}"
|
|
3606
|
-
}
|
|
3607
|
-
},
|
|
3608
|
-
ppeLane: {
|
|
3609
|
-
destroy: {
|
|
3610
|
-
atLeastOneRepo: "\u81F3\u5C11\u9009\u62E9\u4E00\u4E2A\u4ED3\u5E93",
|
|
3611
|
-
blankLane: "PPE lane \u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A",
|
|
3612
|
-
cancelled: "\u5DF2\u53D6\u6D88\u9500\u6BC1",
|
|
3613
|
-
confirmLane: "\u8FD9\u662F\u9500\u6BC1\u64CD\u4F5C\uFF0C\u8BF7\u518D\u6B21\u8F93\u5165 {lane} \u786E\u8BA4",
|
|
3614
|
-
enterLane: "\u8BF7\u8F93\u5165\u9700\u8981\u79FB\u9664\u7684 PPE lane \u540D\u79F0",
|
|
3615
|
-
failed: "\u4E00\u4E2A\u6216\u591A\u4E2A PPE lane \u9500\u6BC1\u5DE5\u4F5C\u6D41\u6267\u884C\u5931\u8D25",
|
|
3616
|
-
noRecords: "\u8BE5 ppe-lane \u6CA1\u67E5\u8BE2\u5230\u4EFB\u4F55\u90E8\u7F72\u8BB0\u5F55",
|
|
3617
|
-
running: "\u6B63\u5728\u9500\u6BC1 lane {lane}",
|
|
3618
|
-
selectRepos: "\u8BF7\u9009\u62E9\u9700\u8981\u9500\u6BC1\u7684\u4ED3\u5E93",
|
|
3619
|
-
skippedReason: "Cloud Map \u4E2D\u672A\u627E\u5230\u90E8\u7F72\u8BB0\u5F55",
|
|
3620
|
-
warning: "\u5C06\u9500\u6BC1\u4EE5\u4E0B\u4ED3\u5E93: {repos}"
|
|
3621
|
-
}
|
|
3622
|
-
},
|
|
3623
|
-
versionUpdate: {
|
|
3624
|
-
alreadyLatest: "\u5F53\u524D\u5DF2\u662F\u6700\u65B0\u7248\u672C {currentVersion}\u3002",
|
|
3625
|
-
available: "\u68C0\u6D4B\u5230\u65B0\u7248\u672C {latestVersion}\uFF0C\u5F53\u524D\u7248\u672C\u4E3A {currentVersion}\u3002",
|
|
3626
|
-
checkingRegistry: "\u6B63\u5728\u68C0\u67E5\u7248\u672C\u4FE1\u606F",
|
|
3627
|
-
detectingInstallSource: "\u6B63\u5728\u68C0\u6D4B\u5B89\u88C5\u6765\u6E90",
|
|
3628
|
-
majorRequired: "\u68C0\u6D4B\u5230\u5927\u7248\u672C\u66F4\u65B0 {latestVersion}\uFF0C\u5F53\u524D\u7248\u672C {currentVersion} \u5FC5\u987B\u5148\u5347\u7EA7\u540E\u518D\u7EE7\u7EED\u3002",
|
|
3629
|
-
manualUpgrade: "\u65E0\u6CD5\u8BC6\u522B\u5F53\u524D\u5B89\u88C5\u6765\u6E90\uFF0C\u8BF7\u624B\u52A8\u6267\u884C\u4EE5\u4E0B\u4EFB\u4E00\u5347\u7EA7\u547D\u4EE4\uFF1A\n{commands}",
|
|
3630
|
-
optionalUpgradeFailed: "\u81EA\u52A8\u5347\u7EA7\u5931\u8D25\uFF0C\u672C\u6B21\u7EE7\u7EED\u4F7F\u7528\u5F53\u524D\u7248\u672C\u3002\u4F60\u53EF\u4EE5\u7A0D\u540E\u624B\u52A8\u6267\u884C\uFF1A{command}",
|
|
3631
|
-
registryCheckFailed: "\u7248\u672C\u68C0\u67E5\u5931\u8D25\uFF0C\u5DF2\u7EE7\u7EED\u6267\u884C\u5F53\u524D\u547D\u4EE4\u3002",
|
|
3632
|
-
registryCheckFailedManual: "\u7248\u672C\u68C0\u67E5\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002",
|
|
3633
|
-
rerun: "\u5347\u7EA7\u5B8C\u6210\u540E\u8BF7\u91CD\u65B0\u6267\u884C\u521A\u624D\u7684\u547D\u4EE4\u3002",
|
|
3634
|
-
selectAction: "\u68C0\u6D4B\u5230\u53EF\u7528\u66F4\u65B0\uFF0C\u662F\u5426\u73B0\u5728\u5347\u7EA7\uFF1F",
|
|
3635
|
-
skipThisRun: "\u8DF3\u8FC7\u672C\u6B21",
|
|
3636
|
-
skippedForRun: "\u5DF2\u8DF3\u8FC7\u672C\u6B21\u5347\u7EA7\uFF0C\u7EE7\u7EED\u6267\u884C\u5F53\u524D\u547D\u4EE4\u3002",
|
|
3637
|
-
upgradeFailed: "\u81EA\u52A8\u5347\u7EA7\u5931\u8D25\uFF0C\u8BF7\u5148\u624B\u52A8\u6267\u884C\uFF1A{command}",
|
|
3638
|
-
upgradeNow: "\u7ACB\u5373\u5347\u7EA7",
|
|
3639
|
-
upgradeSucceeded: "CLI \u5DF2\u66F4\u65B0\u5230\u6700\u65B0\u7248\u672C {latestVersion}\u3002",
|
|
3640
|
-
upgrading: "\u6B63\u5728\u6267\u884C\u5347\u7EA7\u547D\u4EE4\uFF1A{command}"
|
|
3641
|
-
}
|
|
3642
|
-
};
|
|
3643
|
-
|
|
3644
|
-
// src/lib/i18n.ts
|
|
3645
|
-
var locales = {
|
|
3646
|
-
"zh-CN": zhCN
|
|
3647
|
-
};
|
|
3648
|
-
var getNestedValue = (source, key) => {
|
|
3649
|
-
const value = key.split(".").reduce((current, part) => {
|
|
3650
|
-
if (current && typeof current === "object" && part in current) {
|
|
3651
|
-
return current[part];
|
|
3652
|
-
}
|
|
3653
|
-
return void 0;
|
|
3654
|
-
}, source);
|
|
3655
|
-
return typeof value === "string" ? value : key;
|
|
3656
|
-
};
|
|
3657
|
-
var interpolate = (message, values) => {
|
|
3658
|
-
if (!values) {
|
|
3659
|
-
return message;
|
|
3660
|
-
}
|
|
3661
|
-
return message.replace(/\{(\w+)\}/g, (placeholder, key) => {
|
|
3662
|
-
const value = values[key];
|
|
3663
|
-
if (value === void 0) {
|
|
3664
|
-
return placeholder;
|
|
3665
|
-
}
|
|
3666
|
-
return String(value);
|
|
3667
|
-
});
|
|
3668
|
-
};
|
|
3669
|
-
var createI18n = (locale) => {
|
|
3670
|
-
const messages = locales[locale];
|
|
3671
|
-
return (key, values) => {
|
|
3672
|
-
const messageFromLocale = messages ? getNestedValue(messages, key) : key;
|
|
3673
|
-
const message = messageFromLocale === key ? getNestedValue(zhCN, key) : messageFromLocale;
|
|
3674
|
-
return interpolate(message, values);
|
|
3675
|
-
};
|
|
3676
|
-
};
|
|
3677
|
-
|
|
3678
|
-
// src/commands/onboard.ts
|
|
3679
|
-
var onboardModes = ["all", "admin", "aws", "datafinder", "gh", "locale"];
|
|
3680
|
-
var resolveBrewCommand = async () => {
|
|
3681
|
-
const result = await runCommand("/bin/bash", [
|
|
3682
|
-
"-lc",
|
|
3683
|
-
"command -v brew || { [ -x /opt/homebrew/bin/brew ] && printf /opt/homebrew/bin/brew; } || { [ -x /usr/local/bin/brew ] && printf /usr/local/bin/brew; }"
|
|
3684
|
-
]);
|
|
3685
|
-
const brewPath = result.stdout.trim();
|
|
3686
|
-
return brewPath ? brewPath : null;
|
|
3687
|
-
};
|
|
3688
|
-
var installHomebrew = async () => {
|
|
3689
|
-
const result = await runCommand(
|
|
3690
|
-
"/bin/bash",
|
|
3691
|
-
[
|
|
3692
|
-
"-lc",
|
|
3693
|
-
'/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"'
|
|
3694
|
-
],
|
|
3695
|
-
{ stdio: "inherit" }
|
|
3696
|
-
);
|
|
3697
|
-
return result.exitCode === 0;
|
|
3698
|
-
};
|
|
3699
|
-
var ensureBrewPackage = async (packageName, t) => {
|
|
3700
|
-
let brewCommand = await resolveBrewCommand();
|
|
3701
|
-
if (!brewCommand) {
|
|
3702
|
-
const shouldInstallBrew = await ui.confirm({
|
|
3703
|
-
message: t("onboard.installBrewPrompt"),
|
|
3704
|
-
default: true
|
|
3705
|
-
});
|
|
3706
|
-
if (!shouldInstallBrew) {
|
|
3707
|
-
logger.warn(`${t("onboard.installManualPrefix")} brew install ${packageName}`);
|
|
3708
|
-
return false;
|
|
3709
|
-
}
|
|
3710
|
-
if (!await installHomebrew()) {
|
|
3711
|
-
logger.warn(t("onboard.installBrewFailed"));
|
|
3712
|
-
return false;
|
|
3713
|
-
}
|
|
3714
|
-
brewCommand = await resolveBrewCommand();
|
|
3715
|
-
if (!brewCommand) {
|
|
3716
|
-
logger.warn(t("onboard.installBrewFailed"));
|
|
3717
|
-
return false;
|
|
3718
|
-
}
|
|
3719
|
-
}
|
|
3720
|
-
const installResult = await runCommand(brewCommand, ["install", packageName], { stdio: "inherit" });
|
|
3721
|
-
return installResult.exitCode === 0;
|
|
3722
|
-
};
|
|
3723
|
-
var ensureGhInstalled = async (t) => {
|
|
3724
|
-
const ghVersion = await runCommand("gh", ["--version"]);
|
|
3725
|
-
if (ghVersion.exitCode === 0) {
|
|
3726
|
-
return true;
|
|
3727
|
-
}
|
|
3728
|
-
if (process.platform === "darwin") {
|
|
3729
|
-
const shouldInstall = await ui.confirm({ message: t("onboard.installGhPrompt"), default: true });
|
|
3730
|
-
if (shouldInstall) {
|
|
3731
|
-
return ensureBrewPackage("gh", t);
|
|
3732
|
-
}
|
|
3733
|
-
}
|
|
3734
|
-
logger.warn(t("onboard.ghInstallDeferred"));
|
|
3735
|
-
return false;
|
|
3736
|
-
};
|
|
3737
|
-
var ensureAwsInstalled = async (t) => {
|
|
3738
|
-
const awsVersion = await runCommand("aws", ["--version"]);
|
|
3739
|
-
if (awsVersion.exitCode === 0) {
|
|
3740
|
-
return true;
|
|
3741
|
-
}
|
|
3742
|
-
if (process.platform === "darwin") {
|
|
3743
|
-
const shouldInstall = await ui.confirm({ message: t("onboard.installAwsPrompt"), default: true });
|
|
3744
|
-
if (shouldInstall) {
|
|
3745
|
-
return ensureBrewPackage("awscli", t);
|
|
3746
|
-
}
|
|
3747
|
-
}
|
|
3748
|
-
logger.warn(t("onboard.awsInstallDeferred"));
|
|
3749
|
-
return false;
|
|
3750
|
-
};
|
|
3751
|
-
var runGhSetup = async (t) => {
|
|
3752
|
-
if (!await ensureGhInstalled(t)) {
|
|
3753
|
-
return false;
|
|
3754
|
-
}
|
|
3755
|
-
const authStatus = await runCommand("gh", ["auth", "status"]);
|
|
3756
|
-
if (authStatus.exitCode === 0) {
|
|
3757
|
-
return true;
|
|
3758
|
-
}
|
|
3759
|
-
const loginMode = await ui.select({
|
|
3760
|
-
message: t("onboard.selectGhLoginMode"),
|
|
3761
|
-
choices: [
|
|
3762
|
-
{ value: "web", name: t("onboard.ghLoginWeb") },
|
|
3763
|
-
{ value: "token", name: t("onboard.ghLoginToken") }
|
|
3764
|
-
]
|
|
3765
|
-
});
|
|
3766
|
-
if (loginMode === "web") {
|
|
3767
|
-
await runCommand("gh", ["auth", "login", "--web"], { stdio: "inherit" });
|
|
3768
|
-
} else {
|
|
3769
|
-
const token = await ui.password({ message: t("onboard.enterGhToken") });
|
|
3770
|
-
await runCommand("gh", ["auth", "login", "--with-token"], {
|
|
3771
|
-
input: token
|
|
3772
|
-
});
|
|
3773
|
-
}
|
|
3774
|
-
return (await runCommand("gh", ["auth", "status"])).exitCode === 0;
|
|
3775
|
-
};
|
|
3776
|
-
var runAwsSetup = async (defaultProfile, t) => {
|
|
3777
|
-
if (!await ensureAwsInstalled(t)) {
|
|
3778
|
-
return {
|
|
3779
|
-
profile: defaultProfile,
|
|
3780
|
-
configured: false
|
|
3781
|
-
};
|
|
3782
|
-
}
|
|
3783
|
-
const profile = await ui.input({
|
|
3784
|
-
message: t("onboard.chooseAwsProfile"),
|
|
3785
|
-
default: defaultProfile
|
|
3786
|
-
});
|
|
3787
|
-
const listProfiles = await runCommand("aws", ["configure", "list-profiles"]);
|
|
3788
|
-
const profiles = listProfiles.stdout.split("\n").map((item) => item.trim()).filter(Boolean);
|
|
3789
|
-
if (!profiles.includes(profile)) {
|
|
3790
|
-
await runCommand("aws", ["configure", "sso", "--profile", profile], { stdio: "inherit" });
|
|
3791
|
-
}
|
|
3792
|
-
const loginResult = await runCommand("aws", ["sso", "login", "--profile", profile], { stdio: "inherit" });
|
|
3793
|
-
if (loginResult.exitCode !== 0) {
|
|
3794
|
-
return {
|
|
3795
|
-
profile,
|
|
3796
|
-
configured: false
|
|
3797
|
-
};
|
|
3798
|
-
}
|
|
3799
|
-
const identityResult = await runCommand("aws", [
|
|
3800
|
-
"sts",
|
|
3801
|
-
"get-caller-identity",
|
|
3802
|
-
"--profile",
|
|
3803
|
-
profile,
|
|
3804
|
-
"--output",
|
|
3805
|
-
"json"
|
|
3806
|
-
]);
|
|
3807
|
-
return {
|
|
3808
|
-
profile,
|
|
3809
|
-
configured: identityResult.exitCode === 0
|
|
3810
|
-
};
|
|
3811
|
-
};
|
|
3812
|
-
var runAdminSetup = async (config, options, t) => {
|
|
3813
|
-
const apiOrigin = normalizeAdminApiOrigin(options.adminUrl ?? config.admin?.apiOrigin);
|
|
3814
|
-
const token = (await ui.password({ message: t("onboard.enterAdminToken") })).trim();
|
|
3815
|
-
if (!token) {
|
|
3816
|
-
throw new CliError(t("onboard.adminTokenRequired"), 1);
|
|
3817
|
-
}
|
|
3818
|
-
await new AdminApi({ apiOrigin, token }).inspectToken(token);
|
|
3819
|
-
config.admin = {
|
|
3820
|
-
apiOrigin,
|
|
3821
|
-
token
|
|
3822
|
-
};
|
|
3823
|
-
logger.success(t("onboard.adminLoginSucceeded"));
|
|
3824
|
-
};
|
|
3825
|
-
var inputWithDefault = async (message, value) => (await ui.input({
|
|
3826
|
-
message,
|
|
3827
|
-
...value ? { default: value } : {}
|
|
3828
|
-
})).trim();
|
|
3829
|
-
var requiredDataFinderValue = (value, label) => {
|
|
3830
|
-
if (!value) {
|
|
3831
|
-
throw new CliError(`DataFinder ${label} \u4E0D\u80FD\u4E3A\u7A7A`, 1);
|
|
3832
|
-
}
|
|
3833
|
-
return value;
|
|
3834
|
-
};
|
|
3835
|
-
var runDataFinderSetup = async (config, options, t) => {
|
|
3836
|
-
const previous = config.datafinder;
|
|
3837
|
-
let accessKeyId = previous?.accessKeyId ?? "";
|
|
3838
|
-
let secretAccessKey = previous?.secretAccessKey ?? "";
|
|
3839
|
-
if (previous?.accessKeyId && previous.secretAccessKey) {
|
|
3840
|
-
const credentialAction = await ui.select({
|
|
3841
|
-
message: t("onboard.datafinderCredentialAction"),
|
|
3842
|
-
choices: [
|
|
3843
|
-
{ value: "keep", name: t("onboard.datafinderKeepCredentials") },
|
|
3844
|
-
{ value: "replace", name: t("onboard.datafinderReplaceCredentials") }
|
|
3845
|
-
]
|
|
3846
|
-
});
|
|
3847
|
-
if (credentialAction === "replace") {
|
|
3848
|
-
accessKeyId = "";
|
|
3849
|
-
secretAccessKey = "";
|
|
3850
|
-
}
|
|
3851
|
-
}
|
|
3852
|
-
if (!accessKeyId || !secretAccessKey) {
|
|
3853
|
-
accessKeyId = (await ui.password({ message: t("onboard.enterDataFinderAk") })).trim();
|
|
3854
|
-
secretAccessKey = (await ui.password({ message: t("onboard.enterDataFinderSk") })).trim();
|
|
3855
|
-
}
|
|
3856
|
-
const pathMode = parseDataFinderPathMode(
|
|
3857
|
-
options.datafinderPathMode ?? await ui.select({
|
|
3858
|
-
message: t("onboard.selectDataFinderPathMode"),
|
|
3859
|
-
choices: [
|
|
3860
|
-
{ value: "project", name: t("onboard.datafinderProjectPathMode") },
|
|
3861
|
-
{ value: "app", name: t("onboard.datafinderAppPathMode") }
|
|
3862
|
-
],
|
|
3863
|
-
default: previous?.pathMode ?? "project"
|
|
3864
|
-
})
|
|
3865
|
-
);
|
|
3866
|
-
const candidate = {
|
|
3867
|
-
accessKeyId: requiredDataFinderValue(accessKeyId, "AccessKeyId"),
|
|
3868
|
-
appId: requiredDataFinderValue(
|
|
3869
|
-
await inputWithDefault(
|
|
3870
|
-
t("onboard.enterDataFinderAppId"),
|
|
3871
|
-
options.datafinderAppId ?? previous?.appId
|
|
3872
|
-
),
|
|
3873
|
-
"appId"
|
|
3874
|
-
),
|
|
3875
|
-
baseUrl: normalizeDataFinderBaseUrl(
|
|
3876
|
-
requiredDataFinderValue(
|
|
3877
|
-
await inputWithDefault(
|
|
3878
|
-
t("onboard.enterDataFinderUrl"),
|
|
3879
|
-
options.datafinderUrl ?? previous?.baseUrl ?? DEFAULT_DATAFINDER_BASE_URL
|
|
3880
|
-
),
|
|
3881
|
-
"baseUrl"
|
|
3882
|
-
)
|
|
3883
|
-
),
|
|
3884
|
-
defaultDashboardId: requiredDataFinderValue(
|
|
3885
|
-
await inputWithDefault(
|
|
3886
|
-
t("onboard.enterDataFinderDashboardId"),
|
|
3887
|
-
options.datafinderDashboardId ?? previous?.defaultDashboardId
|
|
3888
|
-
),
|
|
3889
|
-
"defaultDashboardId"
|
|
3890
|
-
),
|
|
3891
|
-
pathMode,
|
|
3892
|
-
projectId: requiredDataFinderValue(
|
|
3893
|
-
await inputWithDefault(
|
|
3894
|
-
t("onboard.enterDataFinderProjectId"),
|
|
3895
|
-
options.datafinderProjectId ?? previous?.projectId
|
|
3896
|
-
),
|
|
3897
|
-
"projectId"
|
|
3898
|
-
),
|
|
3899
|
-
secretAccessKey: requiredDataFinderValue(secretAccessKey, "SecretAccessKey"),
|
|
3900
|
-
timezone: requiredDataFinderValue(
|
|
3901
|
-
await inputWithDefault(
|
|
3902
|
-
t("onboard.enterDataFinderTimezone"),
|
|
3903
|
-
previous?.timezone ?? DEFAULT_DATAFINDER_TIMEZONE
|
|
3904
|
-
),
|
|
3905
|
-
"timezone"
|
|
3906
|
-
)
|
|
3907
|
-
};
|
|
3908
|
-
const summary = await new DataFinderClient(candidate).checkConnection();
|
|
3909
|
-
config.datafinder = toStoredDataFinderConfig(candidate);
|
|
3910
|
-
config.setup.datafinderConfigured = true;
|
|
3911
|
-
logger.success(
|
|
3912
|
-
t("onboard.datafinderSucceeded", {
|
|
3913
|
-
dashboardName: summary.dashboardName ?? summary.dashboardId,
|
|
3914
|
-
reportCount: summary.reportCount
|
|
3915
|
-
})
|
|
3916
|
-
);
|
|
3917
|
-
};
|
|
3918
|
-
var runOnboard = async (options) => {
|
|
3919
|
-
const config = await loadConfig();
|
|
3920
|
-
const t = createI18n(config.locale);
|
|
3921
|
-
const mode = options.setMode ?? "all";
|
|
3922
|
-
if (!onboardModes.includes(mode)) {
|
|
3923
|
-
throw new CliError(`Invalid onboard mode: ${mode}`, 1);
|
|
3924
|
-
}
|
|
3925
|
-
if (mode === "all" || mode === "locale") {
|
|
3926
|
-
const locale = await ui.select({
|
|
3927
|
-
message: t("onboard.chooseLocale"),
|
|
3928
|
-
choices: [
|
|
3929
|
-
{ value: "zh-CN", name: "\u4E2D\u6587" },
|
|
3930
|
-
{ value: "skip", name: t("common.skip") }
|
|
3931
|
-
]
|
|
3932
|
-
});
|
|
3933
|
-
if (locale !== "skip") {
|
|
3934
|
-
config.locale = locale;
|
|
3226
|
+
} catch (error) {
|
|
3227
|
+
if (!(error instanceof CliError) || !isTransientWorkflowRunReadFailure(error.message) || readAttempt === TRANSIENT_WORKFLOW_RUN_READ_ATTEMPTS - 1) {
|
|
3228
|
+
throw error;
|
|
3229
|
+
}
|
|
3230
|
+
input2.onUpdate?.(trackedRunId === null ? { status: "pending" } : { runId: trackedRunId, status: "pending" });
|
|
3231
|
+
await sleep(intervalMs);
|
|
3232
|
+
}
|
|
3935
3233
|
}
|
|
3936
|
-
|
|
3937
|
-
|
|
3938
|
-
if (mode === "all" || mode === "admin") {
|
|
3939
|
-
const action = mode === "admin" ? "continue" : await ui.select({
|
|
3940
|
-
message: t("onboard.adminSection"),
|
|
3941
|
-
choices: [
|
|
3942
|
-
{ value: "continue", name: t("common.continue") },
|
|
3943
|
-
{ value: "skip", name: t("common.skip") }
|
|
3944
|
-
]
|
|
3945
|
-
});
|
|
3946
|
-
if (action === "continue") {
|
|
3947
|
-
await runAdminSetup(config, options, t);
|
|
3948
|
-
} else {
|
|
3949
|
-
logger.info(t("onboard.skipAdminSetup"));
|
|
3234
|
+
if (!runs) {
|
|
3235
|
+
throw new CliError(`Timed out waiting for workflow run in ${input2.repoFullName}`, 1);
|
|
3950
3236
|
}
|
|
3951
|
-
|
|
3952
|
-
|
|
3953
|
-
|
|
3954
|
-
|
|
3955
|
-
|
|
3956
|
-
|
|
3957
|
-
{
|
|
3958
|
-
|
|
3959
|
-
|
|
3960
|
-
|
|
3961
|
-
|
|
3962
|
-
} else {
|
|
3963
|
-
logger.info(t("onboard.skipDataFinderSetup"));
|
|
3237
|
+
if (trackedRunId === null) {
|
|
3238
|
+
const candidates = runs.filter((run) => !knownRunIds.has(run.id)).filter((run) => matchesWorkflowRunCorrelation(run, input2.correlation));
|
|
3239
|
+
if (candidates.length > 1) {
|
|
3240
|
+
throw new CliError(`Ambiguous workflow run selection for ${input2.repoFullName}`, 1);
|
|
3241
|
+
}
|
|
3242
|
+
if (candidates.length === 0) {
|
|
3243
|
+
input2.onUpdate?.({ status: "pending" });
|
|
3244
|
+
await sleep(intervalMs);
|
|
3245
|
+
continue;
|
|
3246
|
+
}
|
|
3247
|
+
trackedRunId = candidates[0]?.id ?? null;
|
|
3964
3248
|
}
|
|
3965
|
-
|
|
3966
|
-
|
|
3967
|
-
|
|
3968
|
-
|
|
3969
|
-
|
|
3970
|
-
{ value: "continue", name: t("common.continue") },
|
|
3971
|
-
{ value: "skip", name: t("common.skip") }
|
|
3972
|
-
]
|
|
3973
|
-
});
|
|
3974
|
-
if (action === "continue") {
|
|
3975
|
-
config.setup.ghConfigured = await runGhSetup(t);
|
|
3976
|
-
} else {
|
|
3977
|
-
logger.info(t("onboard.skipGhSetup"));
|
|
3978
|
-
config.setup.ghConfigured = true;
|
|
3249
|
+
const matchedRun = runs.find((run) => run.id === trackedRunId);
|
|
3250
|
+
if (!matchedRun) {
|
|
3251
|
+
input2.onUpdate?.({ runId: trackedRunId, status: "pending" });
|
|
3252
|
+
await sleep(intervalMs);
|
|
3253
|
+
continue;
|
|
3979
3254
|
}
|
|
3980
|
-
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
choices: [
|
|
3985
|
-
{ value: "continue", name: t("common.continue") },
|
|
3986
|
-
{ value: "skip", name: t("common.skip") }
|
|
3987
|
-
]
|
|
3255
|
+
input2.onUpdate?.({
|
|
3256
|
+
runId: trackedRunId,
|
|
3257
|
+
htmlUrl: matchedRun.html_url,
|
|
3258
|
+
status: matchedRun.status === "completed" ? "completed" : "in_progress"
|
|
3988
3259
|
});
|
|
3989
|
-
if (
|
|
3990
|
-
const
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
|
|
3260
|
+
if (matchedRun.status === "completed") {
|
|
3261
|
+
const completion = {
|
|
3262
|
+
conclusion: matchedRun.conclusion ?? "unknown",
|
|
3263
|
+
htmlUrl: matchedRun.html_url,
|
|
3264
|
+
runId: matchedRun.id,
|
|
3265
|
+
status: matchedRun.status
|
|
3266
|
+
};
|
|
3267
|
+
if (completion.conclusion !== "success") {
|
|
3268
|
+
const failureMessage = await fetchWorkflowRunFailureMessage({
|
|
3269
|
+
repoFullName: input2.repoFullName,
|
|
3270
|
+
runId: matchedRun.id
|
|
3271
|
+
});
|
|
3272
|
+
if (failureMessage) {
|
|
3273
|
+
completion.failureMessage = failureMessage;
|
|
3274
|
+
}
|
|
3994
3275
|
}
|
|
3995
|
-
|
|
3996
|
-
logger.info(t("onboard.skipAwsSetup"));
|
|
3997
|
-
config.setup.awsConfigured = true;
|
|
3276
|
+
return completion;
|
|
3998
3277
|
}
|
|
3278
|
+
await sleep(intervalMs);
|
|
3999
3279
|
}
|
|
4000
|
-
|
|
4001
|
-
logger.success(t("onboard.complete"));
|
|
4002
|
-
};
|
|
4003
|
-
var registerOnboardCommand = (program) => {
|
|
4004
|
-
program.command("onboard").description("Install or configure local prerequisites").option("--set-mode <mode>", "setup only one flow: all|admin|aws|datafinder|gh|locale").option("--admin-url <url>", "management API URL or admin.tutti.sh page URL").option("--datafinder-url <url>", "DataFinder OpenAPI origin").option("--datafinder-project-id <id>", "DataFinder project ID").option("--datafinder-app-id <id>", "DataFinder application ID").option("--datafinder-dashboard-id <id>", "default DataFinder dashboard ID").option("--datafinder-path-mode <mode>", "DataFinder API path mode: project|app").action((options) => runOnboard(options));
|
|
4005
|
-
};
|
|
4006
|
-
|
|
4007
|
-
// src/constants/ppe-lane-frontend-targets.ts
|
|
4008
|
-
var ppeLaneCanvasTarget = {
|
|
4009
|
-
key: "zk-web/canvas",
|
|
4010
|
-
label: "zk-web/canvas",
|
|
4011
|
-
repoFullName: "zk-org-test/zk-web",
|
|
4012
|
-
workflowFile: "upload-canvas-staging-version.yml",
|
|
4013
|
-
defaultWorkflowRef: "main",
|
|
4014
|
-
artifactName: "canvas-staging-version",
|
|
4015
|
-
artifactFileName: "canvas-staging-version.json",
|
|
4016
|
-
workerName: "zk-design-web-staging"
|
|
3280
|
+
throw new CliError(`Timed out waiting for workflow run in ${input2.repoFullName}`, 1);
|
|
4017
3281
|
};
|
|
4018
3282
|
|
|
4019
|
-
// src/constants/ppe-lane-repos.ts
|
|
4020
|
-
var ppeLaneRepos = [
|
|
4021
|
-
{
|
|
4022
|
-
key: "zk-agent-engine",
|
|
4023
|
-
label: "zk-agent-engine",
|
|
4024
|
-
repoFullName: "zk-org-test/zk-agent-engine",
|
|
4025
|
-
deployWorkflowFile: "deploy-lane.yml",
|
|
4026
|
-
destroyWorkflowFile: "destroy-lane.yml",
|
|
4027
|
-
defaultRef: "main",
|
|
4028
|
-
cloudMapServiceId: "srv-gg2qso5pnqgykn7h",
|
|
4029
|
-
serviceName: "agent-engine"
|
|
4030
|
-
},
|
|
4031
|
-
{
|
|
4032
|
-
key: "zk-commerce",
|
|
4033
|
-
label: "zk-commerce",
|
|
4034
|
-
repoFullName: "zk-org-test/zk-commerce",
|
|
4035
|
-
deployWorkflowFile: "deploy-lane.yml",
|
|
4036
|
-
destroyWorkflowFile: "destroy-lane.yml",
|
|
4037
|
-
defaultRef: "main",
|
|
4038
|
-
cloudMapServiceId: "srv-tfl3t6duyoehwedi",
|
|
4039
|
-
serviceName: "zk-commerce"
|
|
4040
|
-
},
|
|
4041
|
-
{
|
|
4042
|
-
key: "zk-admin-server",
|
|
4043
|
-
label: "zk-admin-server",
|
|
4044
|
-
repoFullName: "zk-org-test/zk-admin-server",
|
|
4045
|
-
deployWorkflowFile: "deploy-lane.yml",
|
|
4046
|
-
destroyWorkflowFile: "destroy-lane.yml",
|
|
4047
|
-
defaultRef: "main",
|
|
4048
|
-
cloudMapServiceId: "srv-s4fn35h7nen4u5ci",
|
|
4049
|
-
serviceName: "admin"
|
|
4050
|
-
},
|
|
4051
|
-
{
|
|
4052
|
-
key: "design-project",
|
|
4053
|
-
label: "design-project",
|
|
4054
|
-
repoFullName: "zk-org-test/design-project",
|
|
4055
|
-
deployWorkflowFile: "deploy-lane.yml",
|
|
4056
|
-
destroyWorkflowFile: "destroy-lane.yml",
|
|
4057
|
-
defaultRef: "main",
|
|
4058
|
-
cloudMapServiceId: "srv-bjtdvtcxg2clgs6m",
|
|
4059
|
-
serviceName: "design-project"
|
|
4060
|
-
},
|
|
4061
|
-
{
|
|
4062
|
-
key: "account",
|
|
4063
|
-
label: "account",
|
|
4064
|
-
repoFullName: "zk-org-test/account",
|
|
4065
|
-
deployWorkflowFile: "deploy-lane.yml",
|
|
4066
|
-
destroyWorkflowFile: "destroy-lane.yml",
|
|
4067
|
-
defaultRef: "main",
|
|
4068
|
-
cloudMapServiceId: "srv-j2newpni247wsn7h",
|
|
4069
|
-
serviceName: "account"
|
|
4070
|
-
},
|
|
4071
|
-
{
|
|
4072
|
-
key: "zk-websocket",
|
|
4073
|
-
label: "zk-websocket",
|
|
4074
|
-
repoFullName: "zk-org-test/zk-websocket",
|
|
4075
|
-
deployWorkflowFile: "deploy-lane.yml",
|
|
4076
|
-
destroyWorkflowFile: "destroy-lane.yml",
|
|
4077
|
-
defaultRef: "main",
|
|
4078
|
-
cloudMapServiceId: "srv-34fi45edgao7tp2r",
|
|
4079
|
-
serviceName: "zk-websocket"
|
|
4080
|
-
}
|
|
4081
|
-
];
|
|
4082
|
-
|
|
4083
3283
|
// src/lib/ppe-lane.ts
|
|
4084
3284
|
var PPE_LANE_MAX_LENGTH = 38;
|
|
4085
3285
|
var PPE_LANE_PATTERN = /^[a-z0-9](?:[-a-z0-9]*[a-z0-9])?$/;
|
|
@@ -4100,48 +3300,50 @@ var validatePpeLaneName = (value) => {
|
|
|
4100
3300
|
return lane;
|
|
4101
3301
|
};
|
|
4102
3302
|
|
|
3303
|
+
// src/lib/prerequisites.ts
|
|
3304
|
+
var ensureCommandInstalled = async (command, installHint) => {
|
|
3305
|
+
const result = await runCommand(command, ["--version"]);
|
|
3306
|
+
if (result.exitCode !== 0) {
|
|
3307
|
+
throw new CliError(installHint, 1);
|
|
3308
|
+
}
|
|
3309
|
+
};
|
|
3310
|
+
var ensurePrerequisites = async (input2) => {
|
|
3311
|
+
const config = await loadConfig();
|
|
3312
|
+
if (input2.needsGh) {
|
|
3313
|
+
await ensureCommandInstalled("gh", "gh is required. Run nexight-devops onboard --set-mode gh");
|
|
3314
|
+
await ensureGhAuthenticated();
|
|
3315
|
+
}
|
|
3316
|
+
if (input2.needsAws) {
|
|
3317
|
+
await ensureCommandInstalled("aws", "aws is required. Run nexight-devops onboard --set-mode aws");
|
|
3318
|
+
await ensureAwsAuthenticated(config.defaultAwsProfile);
|
|
3319
|
+
}
|
|
3320
|
+
return config;
|
|
3321
|
+
};
|
|
3322
|
+
|
|
4103
3323
|
// src/commands/ppe-lane/deploy.ts
|
|
4104
3324
|
var getErrorMessage = (error) => error instanceof Error ? error.message : String(error);
|
|
4105
3325
|
var renderAggregateProgress = (lane, selectedTargets, repoStates) => renderWorkflowProgressPanel({
|
|
4106
3326
|
items: selectedTargets,
|
|
4107
3327
|
states: repoStates,
|
|
4108
|
-
title:
|
|
3328
|
+
title: `\u6B63\u5728\u53D1\u5E03 lane ${lane}`
|
|
4109
3329
|
});
|
|
4110
3330
|
var runPpeLaneDeploy = async () => {
|
|
4111
3331
|
const requestedRef = await ui.input({ message: "\u8BF7\u8F93\u5165\u76EE\u6807\u5206\u652F", default: "main" });
|
|
4112
|
-
const deployTargetChoices = [
|
|
4113
|
-
...ppeLaneRepos.map((repo) => ({ value: repo.key, name: repo.label })),
|
|
4114
|
-
{ value: ppeLaneCanvasTarget.key, name: ppeLaneCanvasTarget.label }
|
|
4115
|
-
];
|
|
4116
3332
|
const selectedKeys = await ui.checkbox({
|
|
4117
|
-
message: "\u8BF7\u9009\u62E9\u9700\u8981\u53D1\u5E03\u7684\
|
|
4118
|
-
choices:
|
|
3333
|
+
message: "\u8BF7\u9009\u62E9\u9700\u8981\u53D1\u5E03\u7684\u4ED3\u5E93",
|
|
3334
|
+
choices: ppeLaneRepos.map((repo) => ({ value: repo.key, name: repo.label }))
|
|
4119
3335
|
});
|
|
4120
|
-
if (selectedKeys.length === 0) {
|
|
4121
|
-
throw new CliError("At least one deploy target must be selected", 1);
|
|
4122
|
-
}
|
|
4123
3336
|
const selectedRepos = ppeLaneRepos.filter((repo) => selectedKeys.includes(repo.key));
|
|
4124
|
-
|
|
4125
|
-
|
|
4126
|
-
|
|
4127
|
-
|
|
4128
|
-
|
|
4129
|
-
|
|
4130
|
-
|
|
4131
|
-
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
...shouldDeployCanvas ? [
|
|
4135
|
-
{
|
|
4136
|
-
defaultRef: ppeLaneCanvasTarget.defaultWorkflowRef,
|
|
4137
|
-
key: ppeLaneCanvasTarget.key,
|
|
4138
|
-
kind: "canvas",
|
|
4139
|
-
repoFullName: ppeLaneCanvasTarget.repoFullName,
|
|
4140
|
-
workflowFile: ppeLaneCanvasTarget.workflowFile
|
|
4141
|
-
}
|
|
4142
|
-
] : []
|
|
4143
|
-
];
|
|
4144
|
-
const config = await ensurePrerequisites({ needsAws: selectedRepos.length > 0, needsGh: true });
|
|
3337
|
+
if (selectedRepos.length === 0) {
|
|
3338
|
+
throw new CliError("At least one deploy repository must be selected", 1);
|
|
3339
|
+
}
|
|
3340
|
+
const lane = validatePpeLaneName(await ui.input({ message: "\u8BF7\u8F93\u5165 PPE lane \u540D\u79F0" }));
|
|
3341
|
+
const selectedTargets = selectedRepos.map((repo) => ({
|
|
3342
|
+
key: repo.key,
|
|
3343
|
+
repoFullName: repo.repoFullName,
|
|
3344
|
+
workflowFile: repo.deployWorkflowFile
|
|
3345
|
+
}));
|
|
3346
|
+
const config = await ensurePrerequisites({ needsAws: true, needsGh: true });
|
|
4145
3347
|
const branchResults = await Promise.all(
|
|
4146
3348
|
selectedRepos.map(
|
|
4147
3349
|
(repo) => resolveBranchForRepo({
|
|
@@ -4166,12 +3368,11 @@ var runPpeLaneDeploy = async () => {
|
|
|
4166
3368
|
const confirmationLines = [
|
|
4167
3369
|
"\u786E\u8BA4\u5F00\u59CB\u90E8\u7F72\uFF1F",
|
|
4168
3370
|
` \u5206\u652F: ${requestedRef}`,
|
|
3371
|
+
` lane: ${lane}`,
|
|
3372
|
+
"",
|
|
4169
3373
|
" \u5DF2\u9009\u62E9\u76EE\u6807:",
|
|
4170
|
-
...
|
|
3374
|
+
...selectedRepos.map((repo) => ` - ${repo.key}`)
|
|
4171
3375
|
];
|
|
4172
|
-
if (lane) {
|
|
4173
|
-
confirmationLines.splice(2, 0, ` lane: ${lane}`, "");
|
|
4174
|
-
}
|
|
4175
3376
|
const confirmed = await ui.confirm({
|
|
4176
3377
|
message: confirmationLines.join("\n"),
|
|
4177
3378
|
default: true
|
|
@@ -4180,11 +3381,10 @@ var runPpeLaneDeploy = async () => {
|
|
|
4180
3381
|
logger.warn("\u5DF2\u53D6\u6D88\u53D1\u5E03");
|
|
4181
3382
|
return;
|
|
4182
3383
|
}
|
|
4183
|
-
const githubLogin = shouldDeployCanvas ? await getCurrentGitHubLogin() : void 0;
|
|
4184
3384
|
const repoStates = /* @__PURE__ */ new Map();
|
|
4185
3385
|
const results = await withProgressTask(
|
|
4186
3386
|
{
|
|
4187
|
-
initialText:
|
|
3387
|
+
initialText: `\u6B63\u5728\u53D1\u5E03 lane ${lane}`,
|
|
4188
3388
|
heartbeatMs: 1e3,
|
|
4189
3389
|
heartbeatText: () => renderAggregateProgress(lane, selectedTargets, repoStates)
|
|
4190
3390
|
},
|
|
@@ -4193,7 +3393,6 @@ var runPpeLaneDeploy = async () => {
|
|
|
4193
3393
|
selectedTargets.map(async (target) => {
|
|
4194
3394
|
const samples = await Promise.resolve(
|
|
4195
3395
|
listSuccessfulWorkflowRunDurations({
|
|
4196
|
-
...target.kind === "canvas" ? { ref: target.defaultRef } : {},
|
|
4197
3396
|
repoFullName: target.repoFullName,
|
|
4198
3397
|
workflowFile: target.workflowFile
|
|
4199
3398
|
})
|
|
@@ -4285,130 +3484,7 @@ var runPpeLaneDeploy = async () => {
|
|
|
4285
3484
|
};
|
|
4286
3485
|
}
|
|
4287
3486
|
};
|
|
4288
|
-
|
|
4289
|
-
const currentState = repoStates.get(ppeLaneCanvasTarget.key) ?? { estimateMs: null, status: "idle" };
|
|
4290
|
-
const targetRef = requestedRef;
|
|
4291
|
-
const dispatchCreatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4292
|
-
try {
|
|
4293
|
-
repoStates.set(ppeLaneCanvasTarget.key, {
|
|
4294
|
-
...currentState,
|
|
4295
|
-
conclusion: void 0,
|
|
4296
|
-
startedAt: Date.now(),
|
|
4297
|
-
status: "queued"
|
|
4298
|
-
});
|
|
4299
|
-
setText(renderAggregateProgress(lane, selectedTargets, repoStates));
|
|
4300
|
-
const knownRunIds = await listWorkflowRunIds({
|
|
4301
|
-
ref: ppeLaneCanvasTarget.defaultWorkflowRef,
|
|
4302
|
-
repoFullName: ppeLaneCanvasTarget.repoFullName,
|
|
4303
|
-
workflowFile: ppeLaneCanvasTarget.workflowFile
|
|
4304
|
-
});
|
|
4305
|
-
await dispatchCanvasStagingVersionWorkflow({
|
|
4306
|
-
repoFullName: ppeLaneCanvasTarget.repoFullName,
|
|
4307
|
-
targetRef,
|
|
4308
|
-
workflowFile: ppeLaneCanvasTarget.workflowFile,
|
|
4309
|
-
workflowRef: ppeLaneCanvasTarget.defaultWorkflowRef
|
|
4310
|
-
});
|
|
4311
|
-
logger.step(`${ppeLaneCanvasTarget.key} -> queued`);
|
|
4312
|
-
const run = await pollWorkflowRun({
|
|
4313
|
-
correlation: {
|
|
4314
|
-
actorLogin: githubLogin,
|
|
4315
|
-
createdAt: dispatchCreatedAt
|
|
4316
|
-
},
|
|
4317
|
-
knownRunIds,
|
|
4318
|
-
ref: ppeLaneCanvasTarget.defaultWorkflowRef,
|
|
4319
|
-
repoFullName: ppeLaneCanvasTarget.repoFullName,
|
|
4320
|
-
workflowFile: ppeLaneCanvasTarget.workflowFile,
|
|
4321
|
-
onUpdate: (update) => {
|
|
4322
|
-
const nextState = repoStates.get(ppeLaneCanvasTarget.key) ?? currentState;
|
|
4323
|
-
const runUrl = "htmlUrl" in update ? update.htmlUrl : nextState.runUrl;
|
|
4324
|
-
repoStates.set(ppeLaneCanvasTarget.key, {
|
|
4325
|
-
...nextState,
|
|
4326
|
-
conclusion: void 0,
|
|
4327
|
-
detail: update.status === "completed" ? "downloading artifact" : update.status === "pending" ? "waiting for workflow" : void 0,
|
|
4328
|
-
runUrl,
|
|
4329
|
-
startedAt: nextState.startedAt ?? Date.now(),
|
|
4330
|
-
status: update.status === "completed" ? "running" : update.status === "pending" ? "queued" : "running"
|
|
4331
|
-
});
|
|
4332
|
-
setText(renderAggregateProgress(lane, selectedTargets, repoStates));
|
|
4333
|
-
}
|
|
4334
|
-
});
|
|
4335
|
-
logger.step(`${ppeLaneCanvasTarget.key} -> ${run.conclusion}`);
|
|
4336
|
-
if (run.conclusion !== "success") {
|
|
4337
|
-
repoStates.set(ppeLaneCanvasTarget.key, {
|
|
4338
|
-
...repoStates.get(ppeLaneCanvasTarget.key) ?? currentState,
|
|
4339
|
-
conclusion: "failure",
|
|
4340
|
-
detail: run.failureMessage ?? "workflow failed",
|
|
4341
|
-
runUrl: run.htmlUrl,
|
|
4342
|
-
startedAt: repoStates.get(ppeLaneCanvasTarget.key)?.startedAt ?? Date.now(),
|
|
4343
|
-
status: "completed"
|
|
4344
|
-
});
|
|
4345
|
-
setText(renderAggregateProgress(lane, selectedTargets, repoStates));
|
|
4346
|
-
return {
|
|
4347
|
-
key: ppeLaneCanvasTarget.key,
|
|
4348
|
-
ref: targetRef,
|
|
4349
|
-
runUrl: run.htmlUrl,
|
|
4350
|
-
status: run.status,
|
|
4351
|
-
conclusion: run.conclusion,
|
|
4352
|
-
failureDetail: run.failureMessage ?? "workflow failed"
|
|
4353
|
-
};
|
|
4354
|
-
}
|
|
4355
|
-
repoStates.set(ppeLaneCanvasTarget.key, {
|
|
4356
|
-
...repoStates.get(ppeLaneCanvasTarget.key) ?? currentState,
|
|
4357
|
-
conclusion: void 0,
|
|
4358
|
-
detail: "downloading artifact",
|
|
4359
|
-
runUrl: run.htmlUrl,
|
|
4360
|
-
startedAt: repoStates.get(ppeLaneCanvasTarget.key)?.startedAt ?? Date.now(),
|
|
4361
|
-
status: "running"
|
|
4362
|
-
});
|
|
4363
|
-
setText(renderAggregateProgress(lane, selectedTargets, repoStates));
|
|
4364
|
-
const artifact = await fetchCanvasStagingVersionArtifact({
|
|
4365
|
-
artifactFileName: ppeLaneCanvasTarget.artifactFileName,
|
|
4366
|
-
artifactName: ppeLaneCanvasTarget.artifactName,
|
|
4367
|
-
repoFullName: ppeLaneCanvasTarget.repoFullName,
|
|
4368
|
-
runId: run.runId
|
|
4369
|
-
});
|
|
4370
|
-
repoStates.set(ppeLaneCanvasTarget.key, {
|
|
4371
|
-
...repoStates.get(ppeLaneCanvasTarget.key) ?? currentState,
|
|
4372
|
-
conclusion: "success",
|
|
4373
|
-
detail: void 0,
|
|
4374
|
-
runUrl: artifact.workflowRunUrl,
|
|
4375
|
-
startedAt: repoStates.get(ppeLaneCanvasTarget.key)?.startedAt ?? Date.now(),
|
|
4376
|
-
status: "completed"
|
|
4377
|
-
});
|
|
4378
|
-
setText(renderAggregateProgress(lane, selectedTargets, repoStates));
|
|
4379
|
-
return {
|
|
4380
|
-
key: ppeLaneCanvasTarget.key,
|
|
4381
|
-
ref: targetRef,
|
|
4382
|
-
artifact,
|
|
4383
|
-
runUrl: artifact.workflowRunUrl,
|
|
4384
|
-
status: run.status,
|
|
4385
|
-
conclusion: "success"
|
|
4386
|
-
};
|
|
4387
|
-
} catch (error) {
|
|
4388
|
-
const failureState = repoStates.get(ppeLaneCanvasTarget.key) ?? currentState;
|
|
4389
|
-
repoStates.set(ppeLaneCanvasTarget.key, {
|
|
4390
|
-
...failureState,
|
|
4391
|
-
conclusion: "failure",
|
|
4392
|
-
detail: getErrorMessage(error),
|
|
4393
|
-
runUrl: failureState.runUrl,
|
|
4394
|
-
startedAt: failureState.startedAt ?? Date.now(),
|
|
4395
|
-
status: "completed"
|
|
4396
|
-
});
|
|
4397
|
-
setText(renderAggregateProgress(lane, selectedTargets, repoStates));
|
|
4398
|
-
return {
|
|
4399
|
-
key: ppeLaneCanvasTarget.key,
|
|
4400
|
-
ref: targetRef,
|
|
4401
|
-
runUrl: failureState.runUrl ?? "",
|
|
4402
|
-
status: "completed",
|
|
4403
|
-
conclusion: "failure",
|
|
4404
|
-
failureDetail: getErrorMessage(error)
|
|
4405
|
-
};
|
|
4406
|
-
}
|
|
4407
|
-
};
|
|
4408
|
-
return Promise.all([
|
|
4409
|
-
...selectedRepos.map((repo, index) => deployBackendTarget(repo, index)),
|
|
4410
|
-
...shouldDeployCanvas ? [deployCanvasTarget()] : []
|
|
4411
|
-
]);
|
|
3487
|
+
return Promise.all(selectedRepos.map((repo, index) => deployBackendTarget(repo, index)));
|
|
4412
3488
|
}
|
|
4413
3489
|
);
|
|
4414
3490
|
results.forEach((result) => {
|
|
@@ -4416,28 +3492,14 @@ var runPpeLaneDeploy = async () => {
|
|
|
4416
3492
|
`${result.key} | ${result.ref} | ${result.conclusion} | ${result.runUrl}${result.failureDetail ? ` | ${result.failureDetail}` : ""}`
|
|
4417
3493
|
);
|
|
4418
3494
|
});
|
|
4419
|
-
const canvasResult = results.find((result) => result.key === ppeLaneCanvasTarget.key && result.conclusion === "success");
|
|
4420
|
-
if (canvasResult?.artifact) {
|
|
4421
|
-
logger.info(`canvas version id: ${canvasResult.artifact.versionId}`);
|
|
4422
|
-
logger.info(`canvas workflow: ${canvasResult.artifact.workflowRunUrl}`);
|
|
4423
|
-
logger.info(`override header: ${canvasResult.artifact.overrideHeader}`);
|
|
4424
|
-
if (canvasResult.artifact.previewUrl) {
|
|
4425
|
-
logger.info(`preview url: ${canvasResult.artifact.previewUrl}`);
|
|
4426
|
-
}
|
|
4427
|
-
if (canvasResult.artifact.previewAliasUrl) {
|
|
4428
|
-
logger.info(`preview alias url: ${canvasResult.artifact.previewAliasUrl}`);
|
|
4429
|
-
}
|
|
4430
|
-
}
|
|
4431
3495
|
if (results.some((result) => result.conclusion !== "success")) {
|
|
4432
3496
|
throw new Error("One or more PPE lane deployments failed");
|
|
4433
3497
|
}
|
|
4434
|
-
logger.success(
|
|
4435
|
-
|
|
4436
|
-
|
|
4437
|
-
|
|
4438
|
-
|
|
4439
|
-
logger.info("=================================================");
|
|
4440
|
-
}
|
|
3498
|
+
logger.success(`PPE lane ${lane} \u53D1\u5E03\u5B8C\u6210`);
|
|
3499
|
+
logger.info("==================== \u91CD\u8981\u63D0\u793A ====================");
|
|
3500
|
+
logger.info(`\u540E\u7EED\u8BF7\u6C42\u5982\u9700\u547D\u4E2D lane ${lane}\uFF0C\u8BF7\u6DFB\u52A0\u8BF7\u6C42\u5934\uFF1A`);
|
|
3501
|
+
logger.info(`x-zk-ppe-lane: ${lane}`);
|
|
3502
|
+
logger.info("=================================================");
|
|
4441
3503
|
};
|
|
4442
3504
|
var registerPpeLaneDeployCommand = (program) => {
|
|
4443
3505
|
program.command("deploy").description("Deploy selected repositories to a PPE lane; route traffic with x-zk-ppe-lane").addHelpText(
|
|
@@ -4463,7 +3525,11 @@ var runPpeLaneDestroy = async () => {
|
|
|
4463
3525
|
const lane = validatePpeLaneName(await ui.input({ message: t("ppeLane.destroy.enterLane") }));
|
|
4464
3526
|
const selectedKeys = await ui.checkbox({
|
|
4465
3527
|
message: t("ppeLane.destroy.selectRepos"),
|
|
4466
|
-
choices: ppeLaneRepos.map((repo) => ({
|
|
3528
|
+
choices: ppeLaneRepos.map((repo) => ({
|
|
3529
|
+
value: repo.key,
|
|
3530
|
+
name: repo.label,
|
|
3531
|
+
checked: repo.defaultDestroySelection !== false
|
|
3532
|
+
}))
|
|
4467
3533
|
});
|
|
4468
3534
|
if (selectedKeys.length === 0) {
|
|
4469
3535
|
throw new CliError(t("ppeLane.destroy.atLeastOneRepo"), 1);
|
|
@@ -5082,10 +4148,18 @@ var registerReleasePrepareCommand = (program) => {
|
|
|
5082
4148
|
};
|
|
5083
4149
|
|
|
5084
4150
|
// src/commands/release/rollback.ts
|
|
5085
|
-
import { mkdtemp as
|
|
4151
|
+
import { mkdtemp as mkdtemp2, readFile as readFile2, rm } from "fs/promises";
|
|
5086
4152
|
import { tmpdir } from "os";
|
|
5087
4153
|
import { join as join5 } from "path";
|
|
5088
4154
|
|
|
4155
|
+
// src/constants/rollback-targets.ts
|
|
4156
|
+
var tuttiServerRollbackTarget = {
|
|
4157
|
+
key: "tutti-server",
|
|
4158
|
+
label: "tutti-server",
|
|
4159
|
+
repoFullName: "tutti-lab/tutti-server"
|
|
4160
|
+
};
|
|
4161
|
+
var rollbackTargets = [tuttiServerRollbackTarget];
|
|
4162
|
+
|
|
5089
4163
|
// src/commands/release/rollback-local.ts
|
|
5090
4164
|
var CLUSTER = "arn:aws:eks:us-east-1:250509935467:cluster/nexight-ojo-us-east-1";
|
|
5091
4165
|
var REPOSITORY = "250509935467.dkr.ecr.us-east-1.amazonaws.com/nexight/tutti/tutti-server";
|
|
@@ -5173,7 +4247,7 @@ var fetchLocalRollbackCatalog = async () => {
|
|
|
5173
4247
|
};
|
|
5174
4248
|
|
|
5175
4249
|
// src/commands/release/rollback.ts
|
|
5176
|
-
var REPO =
|
|
4250
|
+
var REPO = tuttiServerRollbackTarget.repoFullName;
|
|
5177
4251
|
var WORKFLOW = "rollback.yml";
|
|
5178
4252
|
var SHA256 = /^sha256:[0-9a-f]{64}$/;
|
|
5179
4253
|
var SINGAPORE_TIME = new Intl.DateTimeFormat("sv-SE", {
|
|
@@ -5254,15 +4328,15 @@ var dispatch = async (operation, inputs = {}) => {
|
|
|
5254
4328
|
if (run.conclusion !== "success") {
|
|
5255
4329
|
let state = "";
|
|
5256
4330
|
if (operation === "execute") {
|
|
5257
|
-
const dir = await
|
|
4331
|
+
const dir = await mkdtemp2(join5(tmpdir(), "tutti-rollback-state-"));
|
|
5258
4332
|
try {
|
|
5259
4333
|
await gh(["run", "download", String(run.runId), "--repo", REPO, "--name", "rollback-state", "--dir", dir]);
|
|
5260
4334
|
state = `
|
|
5261
|
-
${await
|
|
4335
|
+
${await readFile2(join5(dir, "rollback-state.txt"), "utf8")}`;
|
|
5262
4336
|
} catch {
|
|
5263
4337
|
state = "\nActual production state is available in the workflow logs.";
|
|
5264
4338
|
} finally {
|
|
5265
|
-
await
|
|
4339
|
+
await rm(dir, { recursive: true, force: true });
|
|
5266
4340
|
}
|
|
5267
4341
|
}
|
|
5268
4342
|
throw new CliError(`Workflow failed: ${run.failureMessage ?? run.conclusion}
|
|
@@ -5274,12 +4348,12 @@ ${run.htmlUrl}${state}`, 1);
|
|
|
5274
4348
|
}
|
|
5275
4349
|
};
|
|
5276
4350
|
var fetchCatalog = async (runId) => {
|
|
5277
|
-
const dir = await
|
|
4351
|
+
const dir = await mkdtemp2(join5(tmpdir(), "tutti-rollback-"));
|
|
5278
4352
|
try {
|
|
5279
4353
|
await gh(["run", "download", String(runId), "--repo", REPO, "--name", "rollback-catalog", "--dir", dir]);
|
|
5280
|
-
return parseRollbackCatalog(await
|
|
4354
|
+
return parseRollbackCatalog(await readFile2(join5(dir, "rollback-catalog.json"), "utf8"));
|
|
5281
4355
|
} finally {
|
|
5282
|
-
await
|
|
4356
|
+
await rm(dir, { recursive: true, force: true });
|
|
5283
4357
|
}
|
|
5284
4358
|
};
|
|
5285
4359
|
var readCatalog = async () => {
|
|
@@ -5341,8 +4415,12 @@ var runTuttiRollback = async (options) => {
|
|
|
5341
4415
|
logger.info(run.htmlUrl);
|
|
5342
4416
|
};
|
|
5343
4417
|
var registerReleaseRollbackCommand = (program) => {
|
|
5344
|
-
program.command("rollback").description("Restore a recent production image").argument("
|
|
5345
|
-
|
|
4418
|
+
program.command("rollback").description("Restore a recent production image").argument("[repository]", "Rollback target; omit to choose interactively").option("--revision <number>", "Select a recent Helm revision without the chooser").option("--yes", "Skip the final confirmation").action(async (repository, options) => {
|
|
4419
|
+
const selected = repository ?? await ui.select({
|
|
4420
|
+
message: "Select a repository to roll back",
|
|
4421
|
+
choices: rollbackTargets.map((target) => ({ name: target.label, value: target.key }))
|
|
4422
|
+
});
|
|
4423
|
+
if (selected !== tuttiServerRollbackTarget.key) {
|
|
5346
4424
|
throw new CliError("Only tutti-server is currently supported", 1);
|
|
5347
4425
|
}
|
|
5348
4426
|
await runTuttiRollback(options);
|
|
@@ -5756,7 +4834,6 @@ var registerUpdateVersionCommand = (program) => {
|
|
|
5756
4834
|
var registerCommands = (program) => {
|
|
5757
4835
|
registerAdminCommand(program);
|
|
5758
4836
|
registerDataFinderCommand(program);
|
|
5759
|
-
registerDiagnoseCommand(program);
|
|
5760
4837
|
registerDoctorCommand(program);
|
|
5761
4838
|
registerOnboardCommand(program);
|
|
5762
4839
|
registerPpeLaneCommand(program);
|