bdy 1.25.6-dev → 1.25.8-dev
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/distTs/package.json +1 -1
- package/distTs/src/cliCommand.js +43 -0
- package/distTs/src/cliIndex.js +10 -0
- package/distTs/src/command/agent/uninstall.js +1 -1
- package/distTs/src/command/artifact/delete.js +1 -2
- package/distTs/src/command/artifact/version/delete.js +1 -2
- package/distTs/src/command/context/add.js +38 -0
- package/distTs/src/command/context/list.js +33 -0
- package/distTs/src/command/context/remove.js +35 -0
- package/distTs/src/command/context/show.js +35 -0
- package/distTs/src/command/context/use.js +25 -0
- package/distTs/src/command/context.js +22 -0
- package/distTs/src/command/distro/delete.js +1 -2
- package/distTs/src/command/distro/route/delete.js +1 -2
- package/distTs/src/command/environment/delete.js +1 -2
- package/distTs/src/command/login.js +1 -0
- package/distTs/src/command/logout.js +1 -0
- package/distTs/src/command/pipeline/delete.js +1 -2
- package/distTs/src/command/pipeline/run/approve.js +3 -2
- package/distTs/src/command/pipeline/run/cancel.js +3 -2
- package/distTs/src/command/pipeline/run/find.js +87 -0
- package/distTs/src/command/pipeline/run/findFilters.js +68 -0
- package/distTs/src/command/pipeline/run/retry.js +5 -2
- package/distTs/src/command/pipeline/run/start.js +3 -2
- package/distTs/src/command/pipeline/run/status.js +3 -2
- package/distTs/src/command/pipeline/run.js +3 -0
- package/distTs/src/command/project/link.js +1 -0
- package/distTs/src/command/register.js +1 -0
- package/distTs/src/command/sandbox/destroy.js +1 -2
- package/distTs/src/command/sandbox/endpoint/delete.js +1 -2
- package/distTs/src/command/sandbox/exec/kill.js +1 -2
- package/distTs/src/command/sandbox/snapshot/delete.js +1 -2
- package/distTs/src/command/target/delete.js +1 -1
- package/distTs/src/command/target/exec/kill.js +2 -3
- package/distTs/src/command/whoami.js +7 -10
- package/distTs/src/command/workspace/get.js +2 -2
- package/distTs/src/command/workspace/set.js +1 -0
- package/distTs/src/input.js +240 -26
- package/distTs/src/output/pipeline.js +84 -13
- package/distTs/src/output.js +24 -5
- package/distTs/src/project/context.js +97 -0
- package/distTs/src/texts.js +275 -29
- package/distTs/src/tunnel/cfg.js +43 -4
- package/distTs/src/types/context.js +2 -0
- package/distTs/src/types/pipelineRunFind.js +2 -0
- package/package.json +1 -1
|
@@ -65,15 +65,34 @@ class OutputPipeline {
|
|
|
65
65
|
}
|
|
66
66
|
return await this._runLogsNonInteractive(client, workspace, project, identifier, pipelineId, runId, runActionId, noWait, isJson, semanticExitCode);
|
|
67
67
|
}
|
|
68
|
-
static
|
|
68
|
+
static findRuns(result) {
|
|
69
|
+
if (result.executions.length) {
|
|
70
|
+
const rows = result.executions.map((run) => [
|
|
71
|
+
String(run.id),
|
|
72
|
+
this.runStatusText((run.status || '-')),
|
|
73
|
+
run.start_date || '-',
|
|
74
|
+
[run.branch?.name, run.tag?.name, run.pull_request?.name]
|
|
75
|
+
.filter(Boolean)
|
|
76
|
+
.join(', ') || '-',
|
|
77
|
+
run.to_revision?.revision?.slice(0, 7) || '-',
|
|
78
|
+
run.html_url || '-',
|
|
79
|
+
]);
|
|
80
|
+
output_1.default.table([[...texts_1.TXT_PIPELINE_RUN_FIND_COLUMNS], ...rows]);
|
|
81
|
+
}
|
|
82
|
+
output_1.default.normal((0, texts_1.TXT_PIPELINE_RUN_FIND_SUMMARY)(result.executions.length, result.scanned));
|
|
83
|
+
if (result.has_more)
|
|
84
|
+
output_1.default.normal(texts_1.TXT_PIPELINE_RUN_FIND_MORE);
|
|
85
|
+
}
|
|
86
|
+
static async runStatus(client, workspace, project, identifier, pipelineId, runId, noWait, noFollow, isJson, semanticExitCode = false, failedLogs = false) {
|
|
69
87
|
if (output_1.default.isTTY() &&
|
|
70
88
|
!noWait &&
|
|
71
89
|
!noFollow &&
|
|
72
90
|
!isJson &&
|
|
73
|
-
!semanticExitCode
|
|
91
|
+
!semanticExitCode &&
|
|
92
|
+
!failedLogs) {
|
|
74
93
|
return await this._runStatusInteractive(client, workspace, project, pipelineId, runId);
|
|
75
94
|
}
|
|
76
|
-
return await this._runStatusNonInteractive(client, workspace, project, identifier, pipelineId, runId, noWait, noFollow, isJson, semanticExitCode);
|
|
95
|
+
return await this._runStatusNonInteractive(client, workspace, project, identifier, pipelineId, runId, noWait, noFollow, isJson, semanticExitCode, failedLogs);
|
|
77
96
|
}
|
|
78
97
|
static _canCancel(status) {
|
|
79
98
|
return ![
|
|
@@ -1327,7 +1346,12 @@ class OutputPipeline {
|
|
|
1327
1346
|
}
|
|
1328
1347
|
}, false, semanticExitCode);
|
|
1329
1348
|
}
|
|
1330
|
-
static async _runStatusNonInteractive(client, workspace, project, identifier, pipelineId, runId, noWait, noFollow, isJson, semanticExitCode = false) {
|
|
1349
|
+
static async _runStatusNonInteractive(client, workspace, project, identifier, pipelineId, runId, noWait, noFollow, isJson, semanticExitCode = false, failedLogs = false) {
|
|
1350
|
+
const finishRun = async (run) => {
|
|
1351
|
+
if (failedLogs)
|
|
1352
|
+
await this.failedActionLogs(client, workspace, project, pipelineId, run, isJson);
|
|
1353
|
+
this._exitByStatus(isJson, run.status, semanticExitCode);
|
|
1354
|
+
};
|
|
1331
1355
|
let run = null;
|
|
1332
1356
|
let totalFetchesCount = 0;
|
|
1333
1357
|
const tabs = 40;
|
|
@@ -1395,7 +1419,7 @@ class OutputPipeline {
|
|
|
1395
1419
|
});
|
|
1396
1420
|
run = r;
|
|
1397
1421
|
};
|
|
1398
|
-
run = await this._runStatusLoop(client, workspace, project, pipelineId, runId, (run) => {
|
|
1422
|
+
run = await this._runStatusLoop(client, workspace, project, pipelineId, runId, async (run) => {
|
|
1399
1423
|
totalFetchesCount += 1;
|
|
1400
1424
|
if (!noFollow || noWait) {
|
|
1401
1425
|
if (isJson)
|
|
@@ -1418,9 +1442,8 @@ class OutputPipeline {
|
|
|
1418
1442
|
throw error;
|
|
1419
1443
|
}
|
|
1420
1444
|
}
|
|
1421
|
-
if (noWait)
|
|
1422
|
-
|
|
1423
|
-
}
|
|
1445
|
+
if (noWait)
|
|
1446
|
+
await finishRun(run);
|
|
1424
1447
|
}, false, semanticExitCode);
|
|
1425
1448
|
if (isJson) {
|
|
1426
1449
|
this.jsonRun(identifier, run);
|
|
@@ -1465,7 +1488,55 @@ class OutputPipeline {
|
|
|
1465
1488
|
output_1.default.normal(this._dimLine());
|
|
1466
1489
|
output_1.default.normal(this._runActionsLogsHelp(identifier, runId));
|
|
1467
1490
|
}
|
|
1468
|
-
|
|
1491
|
+
await finishRun(run);
|
|
1492
|
+
}
|
|
1493
|
+
static async failedActionLogs(client, workspace, project, pipelineId, run, isJson) {
|
|
1494
|
+
for (const action of run.actions) {
|
|
1495
|
+
if (action.status !== pipeline_1.PIPELINE_RUN_STATUS.FAILED)
|
|
1496
|
+
continue;
|
|
1497
|
+
const identity = {
|
|
1498
|
+
action_run_id: action.runId,
|
|
1499
|
+
action_name: action.name,
|
|
1500
|
+
};
|
|
1501
|
+
try {
|
|
1502
|
+
let page = await client.getPipelineRunActionLogs(workspace, project, pipelineId, run.runId, action.runId, 0, texts_1.FAILED_LOGS_LIMIT);
|
|
1503
|
+
if (typeof page.total_element_count !== 'number' ||
|
|
1504
|
+
!Number.isInteger(page.total_element_count) ||
|
|
1505
|
+
page.total_element_count < 0) {
|
|
1506
|
+
throw new cliError_1.default('command_failed', texts_1.ERR_PIPELINE_FAILED_LOGS_COUNT);
|
|
1507
|
+
}
|
|
1508
|
+
// The count lets us jump to the tail without downloading the full log.
|
|
1509
|
+
if (page.total_element_count > texts_1.FAILED_LOGS_LIMIT) {
|
|
1510
|
+
page = await client.getPipelineRunActionLogs(workspace, project, pipelineId, run.runId, action.runId, page.total_element_count - texts_1.FAILED_LOGS_LIMIT, texts_1.FAILED_LOGS_LIMIT);
|
|
1511
|
+
}
|
|
1512
|
+
if (!isJson)
|
|
1513
|
+
output_1.default.data((0, texts_1.TXT_PIPELINE_FAILED_LOGS_HEADER)(action.name, action.runId));
|
|
1514
|
+
const logs = page.logs.slice(-texts_1.FAILED_LOGS_LIMIT);
|
|
1515
|
+
if (!logs.length) {
|
|
1516
|
+
if (isJson)
|
|
1517
|
+
output_1.default.json({
|
|
1518
|
+
type: 'info',
|
|
1519
|
+
...identity,
|
|
1520
|
+
message: texts_1.TXT_PIPELINE_FAILED_LOGS_EMPTY,
|
|
1521
|
+
});
|
|
1522
|
+
else
|
|
1523
|
+
output_1.default.data(texts_1.TXT_PIPELINE_FAILED_LOGS_EMPTY);
|
|
1524
|
+
}
|
|
1525
|
+
for (const log of logs) {
|
|
1526
|
+
if (isJson)
|
|
1527
|
+
output_1.default.json({ type: 'log', ...identity, log });
|
|
1528
|
+
else
|
|
1529
|
+
output_1.default.data(log);
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
catch (error) {
|
|
1533
|
+
const details = cliError_1.default.details(error);
|
|
1534
|
+
if (isJson)
|
|
1535
|
+
output_1.default.json({ type: 'warning', ...identity, error: details });
|
|
1536
|
+
else
|
|
1537
|
+
output_1.default.noteWarning((0, texts_1.TXT_PIPELINE_FAILED_LOGS_ERROR)(action.name, action.runId, details.message));
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1469
1540
|
}
|
|
1470
1541
|
static async _actionLogsLoop(client, workspace, project, pipelineId, runId, actionExecutionId, onChange) {
|
|
1471
1542
|
let logs = [];
|
|
@@ -1664,11 +1735,11 @@ class OutputPipeline {
|
|
|
1664
1735
|
context = {};
|
|
1665
1736
|
context['Environment'] = run.environment.identifier;
|
|
1666
1737
|
}
|
|
1667
|
-
if (run.
|
|
1738
|
+
if (run.artifact && run.artifact_version) {
|
|
1668
1739
|
if (!context)
|
|
1669
1740
|
context = {};
|
|
1670
|
-
context['
|
|
1671
|
-
`${run.
|
|
1741
|
+
context['Artifact'] =
|
|
1742
|
+
`${run.artifact.identifier}:${run.artifact_version.version}`;
|
|
1672
1743
|
}
|
|
1673
1744
|
if (run.loop_details) {
|
|
1674
1745
|
if (!context)
|
|
@@ -1768,7 +1839,7 @@ class OutputPipeline {
|
|
|
1768
1839
|
context,
|
|
1769
1840
|
actions,
|
|
1770
1841
|
};
|
|
1771
|
-
onChange(r, forceFetchRun);
|
|
1842
|
+
await onChange(r, forceFetchRun);
|
|
1772
1843
|
return r;
|
|
1773
1844
|
};
|
|
1774
1845
|
lastRun = await forceFetchRun();
|
package/distTs/src/output.js
CHANGED
|
@@ -187,6 +187,15 @@ class Output {
|
|
|
187
187
|
static data(txt, newLine = true) {
|
|
188
188
|
process.stdout.write(newLine ? `${txt}\n` : txt);
|
|
189
189
|
}
|
|
190
|
+
static examples(command, examples, format) {
|
|
191
|
+
if (format === 'json')
|
|
192
|
+
return this.json({ command, examples });
|
|
193
|
+
if (format === 'jsonl')
|
|
194
|
+
return this.json({ type: 'examples', command, examples });
|
|
195
|
+
if (!examples.length)
|
|
196
|
+
return this.data(`No examples for ${command}.`);
|
|
197
|
+
this.data(`EXAMPLES:\n${examples.map((example) => ` ### ${example.description}\n ${example.command}`).join('\n')}`);
|
|
198
|
+
}
|
|
190
199
|
// For payload embedded in terminal-kit markup (e.g. a dimmed log line):
|
|
191
200
|
// neutralizes the ^-markup and %-formatting the payload would otherwise
|
|
192
201
|
// trigger, so it renders verbatim while the surrounding markup still
|
|
@@ -375,7 +384,15 @@ class Output {
|
|
|
375
384
|
}
|
|
376
385
|
});
|
|
377
386
|
}
|
|
378
|
-
static async
|
|
387
|
+
static async confirmDestructive(force, message, quiet = false) {
|
|
388
|
+
if (force)
|
|
389
|
+
return true;
|
|
390
|
+
if (!this.isStdInTTY()) {
|
|
391
|
+
throw new cliError_1.default('usage_error', texts_1.ERR_CONFIRM_REQUIRED, texts_1.TXT_CONFIRM_FORCE_HINT);
|
|
392
|
+
}
|
|
393
|
+
return await this.confirm(message, quiet);
|
|
394
|
+
}
|
|
395
|
+
static async confirm(message = 'Are you sure?', quiet = false) {
|
|
379
396
|
if (this.isStdInTTY()) {
|
|
380
397
|
const prompts = require('@inquirer/prompts');
|
|
381
398
|
try {
|
|
@@ -383,15 +400,17 @@ class Output {
|
|
|
383
400
|
message,
|
|
384
401
|
theme: this.getInquirerTheme(),
|
|
385
402
|
default: false,
|
|
386
|
-
});
|
|
403
|
+
}, quiet ? { output: process.stderr } : undefined);
|
|
387
404
|
}
|
|
388
405
|
catch {
|
|
389
406
|
this.exitError(texts_1.ERR_CANCELLED);
|
|
390
407
|
}
|
|
391
408
|
}
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
409
|
+
if (!quiet) {
|
|
410
|
+
this.cyan('? ', false);
|
|
411
|
+
this.label(message, false);
|
|
412
|
+
this.cyan(` No`);
|
|
413
|
+
}
|
|
395
414
|
return false;
|
|
396
415
|
}
|
|
397
416
|
static async inputString(message, def, required, signal) {
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.parseBuddyRemote = parseBuddyRemote;
|
|
7
|
+
exports.resolveContextScope = resolveContextScope;
|
|
8
|
+
const node_child_process_1 = require("node:child_process");
|
|
9
|
+
const node_fs_1 = require("node:fs");
|
|
10
|
+
const node_path_1 = require("node:path");
|
|
11
|
+
const cliError_1 = __importDefault(require("../cliError"));
|
|
12
|
+
const utils_1 = require("../utils");
|
|
13
|
+
const texts_1 = require("../texts");
|
|
14
|
+
function parseBuddyRemote(remote) {
|
|
15
|
+
try {
|
|
16
|
+
// Accept both standard URLs and Git's scp-like SSH syntax.
|
|
17
|
+
const value = remote.includes('://')
|
|
18
|
+
? remote
|
|
19
|
+
: remote.replace(/^(?:[^@/:]+@)?([^/:]+):(.+)$/, 'ssh://$1/$2');
|
|
20
|
+
const url = new URL(value);
|
|
21
|
+
if (!['https:', 'ssh:'].includes(url.protocol) ||
|
|
22
|
+
!Object.values(utils_1.REST_API_ENDPOINT).some((endpoint) => (0, utils_1.getAppHostByApiBaseUrl)(new URL(`https://${endpoint}`)) ===
|
|
23
|
+
url.hostname) ||
|
|
24
|
+
url.search ||
|
|
25
|
+
url.hash)
|
|
26
|
+
throw new Error();
|
|
27
|
+
const parts = url.pathname
|
|
28
|
+
.replace(/\/$/, '')
|
|
29
|
+
.replace(/\.git$/, '')
|
|
30
|
+
.split('/')
|
|
31
|
+
.slice(1);
|
|
32
|
+
if (parts.length !== 2 ||
|
|
33
|
+
parts.some((part) => !/^[a-zA-Z0-9_-]+$/.test(part)))
|
|
34
|
+
throw new Error();
|
|
35
|
+
return { workspace: parts[0], project: parts[1] };
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
// Do not include the raw URL: remotes can contain credentials.
|
|
39
|
+
throw new cliError_1.default('git_remote_unsupported', texts_1.ERR_CONTEXT_GIT_URL, texts_1.TXT_CONTEXT_GIT_HINT);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function resolveContextScope(context, cwd) {
|
|
43
|
+
if (context.source === 'explicit')
|
|
44
|
+
return {
|
|
45
|
+
workspace: context.workspace || null,
|
|
46
|
+
project: context.project || null,
|
|
47
|
+
remote: null,
|
|
48
|
+
};
|
|
49
|
+
if (context.source === 'link') {
|
|
50
|
+
let data;
|
|
51
|
+
try {
|
|
52
|
+
data = JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(cwd, '.bdy/project.json'), 'utf8'));
|
|
53
|
+
}
|
|
54
|
+
catch (err) {
|
|
55
|
+
const missing = err.code === 'ENOENT';
|
|
56
|
+
throw new cliError_1.default(missing ? 'project_link_not_found' : 'project_link_invalid', missing ? texts_1.ERR_CONTEXT_LINK_MISSING : texts_1.ERR_CONTEXT_LINK_INVALID, texts_1.TXT_CONTEXT_LINK_HINT);
|
|
57
|
+
}
|
|
58
|
+
if (typeof data?.workspace !== 'string' ||
|
|
59
|
+
!data.workspace.trim() ||
|
|
60
|
+
typeof data?.project !== 'string' ||
|
|
61
|
+
!data.project.trim())
|
|
62
|
+
throw new cliError_1.default('project_link_invalid', texts_1.ERR_CONTEXT_LINK_INVALID, texts_1.TXT_CONTEXT_LINK_HINT);
|
|
63
|
+
return {
|
|
64
|
+
workspace: data.workspace,
|
|
65
|
+
project: data.project,
|
|
66
|
+
remote: null,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
const remote = context.remote || 'origin';
|
|
70
|
+
let url;
|
|
71
|
+
try {
|
|
72
|
+
url = (0, node_child_process_1.execFileSync)('git', ['remote', 'get-url', '--', remote], {
|
|
73
|
+
cwd,
|
|
74
|
+
encoding: 'utf8',
|
|
75
|
+
timeout: 3000,
|
|
76
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
77
|
+
// Git diagnostics are classified below; keep their language deterministic.
|
|
78
|
+
env: { ...process.env, LC_ALL: 'C', LANG: 'C' },
|
|
79
|
+
}).trim();
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
const failure = err;
|
|
83
|
+
if (failure.code === 'ENOENT')
|
|
84
|
+
throw new cliError_1.default('git_not_found', texts_1.ERR_CONTEXT_GIT_MISSING, texts_1.TXT_CONTEXT_GIT_HINT);
|
|
85
|
+
if (failure.code === 'ETIMEDOUT')
|
|
86
|
+
throw new cliError_1.default('timeout', texts_1.ERR_CONTEXT_GIT_TIMEOUT, texts_1.TXT_CONTEXT_GIT_HINT);
|
|
87
|
+
const stderr = String(failure.stderr || '');
|
|
88
|
+
if (failure.status === 128 && /fatal: not a git repository/i.test(stderr))
|
|
89
|
+
throw new cliError_1.default('git_repository_not_found', texts_1.ERR_CONTEXT_GIT_REPO, texts_1.TXT_CONTEXT_GIT_HINT);
|
|
90
|
+
if (failure.status === 2 && /error: No such remote /i.test(stderr))
|
|
91
|
+
throw new cliError_1.default('git_remote_not_found', (0, texts_1.ERR_CONTEXT_GIT_REMOTE)(remote), texts_1.TXT_CONTEXT_GIT_HINT);
|
|
92
|
+
// Neither permission errors nor an interrupted Git process imply a missing repo.
|
|
93
|
+
// Never forward raw diagnostics, which may contain remote credentials.
|
|
94
|
+
throw new cliError_1.default('command_failed', texts_1.ERR_CONTEXT_GIT_FAILED, texts_1.TXT_CONTEXT_GIT_HINT);
|
|
95
|
+
}
|
|
96
|
+
return { ...parseBuddyRemote(url), remote };
|
|
97
|
+
}
|