bdy 1.23.13 → 1.23.14-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 +3 -1
- package/distTs/src/api/client.js +259 -24
- package/distTs/src/cliIndex.js +4 -0
- package/distTs/src/command/api/request.js +5 -4
- package/distTs/src/command/artifact/list.js +2 -6
- package/distTs/src/command/distro/list.js +1 -0
- package/distTs/src/command/distro/route/update.js +1 -1
- package/distTs/src/command/distro/update.js +9 -9
- package/distTs/src/command/environment/create.js +70 -0
- package/distTs/src/command/environment/delete.js +30 -0
- package/distTs/src/command/environment/get.js +48 -0
- package/distTs/src/command/environment/list.js +45 -0
- package/distTs/src/command/environment/resolve.js +39 -0
- package/distTs/src/command/environment/update.js +60 -0
- package/distTs/src/command/environment.js +20 -0
- package/distTs/src/command/project/git/credential.js +6 -4
- package/distTs/src/command/sandbox/exec/command.js +1 -1
- package/distTs/src/command/sandbox/exec/logs.js +1 -1
- package/distTs/src/command/target/create.js +33 -0
- package/distTs/src/command/target/delete.js +31 -0
- package/distTs/src/command/target/exec/command.js +101 -0
- package/distTs/src/command/target/exec/kill.js +31 -0
- package/distTs/src/command/target/exec/list.js +53 -0
- package/distTs/src/command/target/exec/logs.js +27 -0
- package/distTs/src/command/target/exec/status.js +44 -0
- package/distTs/src/command/target/exec.js +24 -0
- package/distTs/src/command/target/get.js +63 -0
- package/distTs/src/command/target/list.js +49 -0
- package/distTs/src/command/target/scope.js +138 -0
- package/distTs/src/command/target/update.js +32 -0
- package/distTs/src/command/target.js +22 -0
- package/distTs/src/index.js +14 -0
- package/distTs/src/input.js +65 -0
- package/distTs/src/output/pipeline.js +5 -3
- package/distTs/src/output.js +30 -2
- package/distTs/src/texts.js +251 -27
- package/distTs/src/utils.js +18 -2
- package/package.json +3 -1
|
@@ -0,0 +1,49 @@
|
|
|
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
|
+
const utils_1 = require("../../utils");
|
|
7
|
+
const texts_1 = require("../../texts");
|
|
8
|
+
const output_1 = __importDefault(require("../../output"));
|
|
9
|
+
const input_1 = __importDefault(require("../../input"));
|
|
10
|
+
const scope_1 = require("./scope");
|
|
11
|
+
const commandTargetList = (0, utils_1.newCommand)('list', texts_1.DESC_COMMAND_TARGET_LIST);
|
|
12
|
+
commandTargetList.alias('ls');
|
|
13
|
+
commandTargetList.option('-w, --workspace <domain>', texts_1.OPTION_REST_API_WORKSPACE);
|
|
14
|
+
commandTargetList.option('-p, --project <name>', texts_1.OPTION_REST_API_PROJECT);
|
|
15
|
+
commandTargetList.option('--pipeline <identifier>', texts_1.OPTION_TARGET_PIPELINE);
|
|
16
|
+
commandTargetList.option('--environment <identifier>', texts_1.OPTION_TARGET_ENVIRONMENT);
|
|
17
|
+
commandTargetList.option('--scope <scope>', texts_1.OPT_COMMAND_TARGET_SCOPE);
|
|
18
|
+
commandTargetList.option('--format <text|json>', texts_1.OPTION_FORMAT);
|
|
19
|
+
commandTargetList.addHelpText('after', `\nEXAMPLES:${texts_1.EXAMPLE_TARGET_LIST}`);
|
|
20
|
+
commandTargetList.action(async (options) => {
|
|
21
|
+
const workspace = input_1.default.restApiWorkspace(options.workspace);
|
|
22
|
+
const project = input_1.default.restApiProject(options.project, true);
|
|
23
|
+
const scope = input_1.default.targetScope(project, options.pipeline, options.environment, options.scope);
|
|
24
|
+
input_1.default.projectScopeNote(options, project, scope, 'targets');
|
|
25
|
+
const client = input_1.default.restApiTokenClient();
|
|
26
|
+
const filters = await (0, scope_1.resolveTargetScope)(client, workspace, project, scope, options);
|
|
27
|
+
const result = await client.getTargets(workspace, filters);
|
|
28
|
+
const targets = result.targets || [];
|
|
29
|
+
if (options.format === 'json') {
|
|
30
|
+
output_1.default.json(targets);
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
if (targets.length === 0) {
|
|
34
|
+
output_1.default.exitNormal(texts_1.TXT_TARGET_NOT_FOUND);
|
|
35
|
+
}
|
|
36
|
+
const data = [['NAME', 'IDENTIFIER', 'TYPE', 'SCOPE']];
|
|
37
|
+
for (const target of targets) {
|
|
38
|
+
data.push([
|
|
39
|
+
target.name || '-',
|
|
40
|
+
target.identifier || '-',
|
|
41
|
+
target.type || '-',
|
|
42
|
+
(0, scope_1.targetScopeOf)(target),
|
|
43
|
+
]);
|
|
44
|
+
}
|
|
45
|
+
output_1.default.table(data);
|
|
46
|
+
}
|
|
47
|
+
output_1.default.exitNormal();
|
|
48
|
+
});
|
|
49
|
+
exports.default = commandTargetList;
|
|
@@ -0,0 +1,138 @@
|
|
|
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.resolveTargetScope = exports.targetScopeOf = exports.targetAddressOf = exports.forwardExecLog = exports.resolveTargetId = void 0;
|
|
7
|
+
const utils_1 = require("../../utils");
|
|
8
|
+
const texts_1 = require("../../texts");
|
|
9
|
+
const output_1 = __importDefault(require("../../output"));
|
|
10
|
+
const input_1 = __importDefault(require("../../input"));
|
|
11
|
+
// Shared resolve preamble for commands taking a target identifier: line
|
|
12
|
+
// context from flags/env -> /identifiers -> hash id, exiting on a miss.
|
|
13
|
+
//
|
|
14
|
+
// strictLine is for mutating commands (update/delete/exec command): an
|
|
15
|
+
// explicit line flag is a statement "the target of this project/pipeline/
|
|
16
|
+
// environment", yet /identifiers falls back to the workspace target of the
|
|
17
|
+
// same identifier when the requested line exists but does not hold it - the
|
|
18
|
+
// operation would silently land on a workspace-wide resource. Reads keep
|
|
19
|
+
// the fallback (workspace targets stay addressable from a line context).
|
|
20
|
+
const resolveTargetId = async (client, workspace, identifier, options, strictLine = false) => {
|
|
21
|
+
const project = input_1.default.restApiProject(options.project, true);
|
|
22
|
+
const line = input_1.default.targetLine(project, options.pipeline, options.environment);
|
|
23
|
+
const resolved = await client.getTargetByIdentifier(workspace, identifier, line);
|
|
24
|
+
if (!resolved.target_id) {
|
|
25
|
+
output_1.default.exitError(texts_1.ERR_TARGET_NOT_FOUND);
|
|
26
|
+
}
|
|
27
|
+
if (strictLine) {
|
|
28
|
+
const requested = options.pipeline
|
|
29
|
+
? utils_1.TARGET_SCOPE.PIPELINE
|
|
30
|
+
: options.environment
|
|
31
|
+
? utils_1.TARGET_SCOPE.ENVIRONMENT
|
|
32
|
+
: options.project
|
|
33
|
+
? utils_1.TARGET_SCOPE.PROJECT
|
|
34
|
+
: null;
|
|
35
|
+
if (requested) {
|
|
36
|
+
const target = await client.getTarget(workspace, resolved.target_id);
|
|
37
|
+
const matches = requested === utils_1.TARGET_SCOPE.PIPELINE
|
|
38
|
+
? String(target.pipeline?.id) === String(resolved.pipeline_id)
|
|
39
|
+
: requested === utils_1.TARGET_SCOPE.ENVIRONMENT
|
|
40
|
+
? target.environment?.id === resolved.environment_id
|
|
41
|
+
: (0, exports.targetScopeOf)(target) === utils_1.TARGET_SCOPE.PROJECT &&
|
|
42
|
+
target.project?.name === resolved.project_identifier;
|
|
43
|
+
if (!matches) {
|
|
44
|
+
output_1.default.exitError(texts_1.ERR_TARGET_NOT_FOUND);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return resolved.target_id;
|
|
49
|
+
};
|
|
50
|
+
exports.resolveTargetId = resolveTargetId;
|
|
51
|
+
// Lines the CLI requests per log fetch.
|
|
52
|
+
const EXEC_LOG_PAGE = 1000;
|
|
53
|
+
// The exec log endpoint pages by line offset; its `truncated` field
|
|
54
|
+
// describes server-side output truncation, not pagination. `lines` may
|
|
55
|
+
// additionally include a trailing line that is still being written -
|
|
56
|
+
// `next_offset` only advances past committed lines - so exactly
|
|
57
|
+
// next_offset - offset lines are emitted to avoid re-printing the partial
|
|
58
|
+
// line on the next pass. A page shorter than requested means the committed
|
|
59
|
+
// log is drained, so the common case costs a single request.
|
|
60
|
+
const forwardExecLog = async (client, workspace, execId, startOffset, emitPage = (lines) => output_1.default.data(lines.join('\n')), maxLines) => {
|
|
61
|
+
let offset = startOffset;
|
|
62
|
+
let emitted = 0;
|
|
63
|
+
let truncated = false;
|
|
64
|
+
for (;;) {
|
|
65
|
+
const pageLimit = maxLines === undefined
|
|
66
|
+
? EXEC_LOG_PAGE
|
|
67
|
+
: Math.min(EXEC_LOG_PAGE, maxLines - emitted);
|
|
68
|
+
if (pageLimit <= 0)
|
|
69
|
+
break;
|
|
70
|
+
const log = await client.getTargetExecLog(workspace, execId, offset, pageLimit);
|
|
71
|
+
truncated = truncated || !!log.truncated;
|
|
72
|
+
const next = log.next_offset ?? offset;
|
|
73
|
+
if (next <= offset)
|
|
74
|
+
break;
|
|
75
|
+
const lines = (log.lines || []).slice(0, next - offset);
|
|
76
|
+
if (lines.length)
|
|
77
|
+
emitPage(lines);
|
|
78
|
+
emitted += lines.length;
|
|
79
|
+
offset = next;
|
|
80
|
+
if (lines.length < pageLimit)
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
return { offset, truncated };
|
|
84
|
+
};
|
|
85
|
+
exports.forwardExecLog = forwardExecLog;
|
|
86
|
+
// Connection info condensed to one host:port/database cell (auth is left
|
|
87
|
+
// out on purpose - the text view is a summary, credentials stay in json).
|
|
88
|
+
const targetAddressOf = (target) => {
|
|
89
|
+
if (!target.host)
|
|
90
|
+
return '-';
|
|
91
|
+
let address = target.host;
|
|
92
|
+
if (target.port)
|
|
93
|
+
address += `:${target.port}`;
|
|
94
|
+
if (target.database)
|
|
95
|
+
address += `/${target.database}`;
|
|
96
|
+
return address;
|
|
97
|
+
};
|
|
98
|
+
exports.targetAddressOf = targetAddressOf;
|
|
99
|
+
const targetScopeOf = (target) => {
|
|
100
|
+
if (target.pipeline)
|
|
101
|
+
return utils_1.TARGET_SCOPE.PIPELINE;
|
|
102
|
+
if (target.environment)
|
|
103
|
+
return utils_1.TARGET_SCOPE.ENVIRONMENT;
|
|
104
|
+
if (target.project)
|
|
105
|
+
return utils_1.TARGET_SCOPE.PROJECT;
|
|
106
|
+
return utils_1.TARGET_SCOPE.WORKSPACE;
|
|
107
|
+
};
|
|
108
|
+
exports.targetScopeOf = targetScopeOf;
|
|
109
|
+
// Input.targetScope guarantees project is set for the PIPELINE scope.
|
|
110
|
+
// Environments exist on both workspace and project level, so for the
|
|
111
|
+
// ENVIRONMENT scope project is optional and narrows the lookup when given.
|
|
112
|
+
const resolveTargetScope = async (client, workspace, project, scope, options) => {
|
|
113
|
+
const params = {};
|
|
114
|
+
if (scope === utils_1.TARGET_SCOPE.PIPELINE) {
|
|
115
|
+
const { pipeline_id } = await client.getPipelineByIdentifier(workspace, project, options.pipeline);
|
|
116
|
+
if (!pipeline_id)
|
|
117
|
+
output_1.default.exitError(texts_1.ERR_PIPELINE_NOT_FOUND);
|
|
118
|
+
params.pipelineId = pipeline_id;
|
|
119
|
+
}
|
|
120
|
+
else if (scope === utils_1.TARGET_SCOPE.ENVIRONMENT) {
|
|
121
|
+
const query = {
|
|
122
|
+
environment: options.environment,
|
|
123
|
+
};
|
|
124
|
+
if (project)
|
|
125
|
+
query.project = project;
|
|
126
|
+
const resolved = await client.getResourceByIdentifier(workspace, query);
|
|
127
|
+
// a nonexistent project is rejected by getResourceByIdentifier itself
|
|
128
|
+
if (!resolved.environment_id) {
|
|
129
|
+
output_1.default.exitError(texts_1.ERR_TARGET_ENVIRONMENT_NOT_FOUND);
|
|
130
|
+
}
|
|
131
|
+
params.environmentId = resolved.environment_id;
|
|
132
|
+
}
|
|
133
|
+
else if (scope === utils_1.TARGET_SCOPE.PROJECT) {
|
|
134
|
+
params.project = project;
|
|
135
|
+
}
|
|
136
|
+
return params;
|
|
137
|
+
};
|
|
138
|
+
exports.resolveTargetScope = resolveTargetScope;
|
|
@@ -0,0 +1,32 @@
|
|
|
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
|
+
const utils_1 = require("../../utils");
|
|
7
|
+
const texts_1 = require("../../texts");
|
|
8
|
+
const output_1 = __importDefault(require("../../output"));
|
|
9
|
+
const input_1 = __importDefault(require("../../input"));
|
|
10
|
+
const scope_1 = require("./scope");
|
|
11
|
+
const commandTargetUpdate = (0, utils_1.newCommand)('update', texts_1.DESC_COMMAND_TARGET_UPDATE);
|
|
12
|
+
commandTargetUpdate.alias('edit');
|
|
13
|
+
commandTargetUpdate.option('-w, --workspace <domain>', texts_1.OPTION_REST_API_WORKSPACE);
|
|
14
|
+
commandTargetUpdate.option('-p, --project <name>', texts_1.OPTION_REST_API_PROJECT);
|
|
15
|
+
commandTargetUpdate.option('--pipeline <identifier>', texts_1.OPTION_TARGET_PIPELINE);
|
|
16
|
+
commandTargetUpdate.option('--environment <identifier>', texts_1.OPTION_TARGET_ENVIRONMENT);
|
|
17
|
+
commandTargetUpdate.option('--yaml <content|@path>', texts_1.OPTION_TARGET_YAML);
|
|
18
|
+
commandTargetUpdate.argument('<identifier>', texts_1.OPTION_TARGET_IDENTIFIER);
|
|
19
|
+
commandTargetUpdate.addHelpText('after', `\nEXAMPLES:${texts_1.EXAMPLE_TARGET_UPDATE}`);
|
|
20
|
+
commandTargetUpdate.action(async (identifier, options) => {
|
|
21
|
+
const yaml = input_1.default.restApiYaml(options.yaml);
|
|
22
|
+
const workspace = input_1.default.restApiWorkspace(options.workspace);
|
|
23
|
+
const client = input_1.default.restApiTokenClient();
|
|
24
|
+
const target_id = await (0, scope_1.resolveTargetId)(client, workspace, identifier, options,
|
|
25
|
+
/* strictLine */ true);
|
|
26
|
+
const body = {
|
|
27
|
+
yaml: Buffer.from(yaml, 'utf8').toString('base64'),
|
|
28
|
+
};
|
|
29
|
+
const result = await client.updateTargetByYaml(workspace, target_id, body);
|
|
30
|
+
output_1.default.exitSuccess((0, texts_1.TXT_TARGET_UPDATED)(result.identifier, result.html_url));
|
|
31
|
+
});
|
|
32
|
+
exports.default = commandTargetUpdate;
|
|
@@ -0,0 +1,22 @@
|
|
|
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
|
+
const utils_1 = require("../utils");
|
|
7
|
+
const texts_1 = require("../texts");
|
|
8
|
+
const list_1 = __importDefault(require("./target/list"));
|
|
9
|
+
const get_1 = __importDefault(require("./target/get"));
|
|
10
|
+
const create_1 = __importDefault(require("./target/create"));
|
|
11
|
+
const update_1 = __importDefault(require("./target/update"));
|
|
12
|
+
const delete_1 = __importDefault(require("./target/delete"));
|
|
13
|
+
const exec_1 = __importDefault(require("./target/exec"));
|
|
14
|
+
const commandTarget = (0, utils_1.newCommand)('target', texts_1.DESC_COMMAND_TARGET);
|
|
15
|
+
commandTarget.alias('tg');
|
|
16
|
+
commandTarget.addCommand(list_1.default);
|
|
17
|
+
commandTarget.addCommand(get_1.default);
|
|
18
|
+
commandTarget.addCommand(create_1.default);
|
|
19
|
+
commandTarget.addCommand(update_1.default);
|
|
20
|
+
commandTarget.addCommand(delete_1.default);
|
|
21
|
+
commandTarget.addCommand(exec_1.default);
|
|
22
|
+
exports.default = commandTarget;
|
package/distTs/src/index.js
CHANGED
|
@@ -9,7 +9,21 @@ const stream_1 = __importDefault(require("stream"));
|
|
|
9
9
|
output_1.default.muteNodeWarnings();
|
|
10
10
|
stream_1.default.setDefaultHighWaterMark(false, 65536);
|
|
11
11
|
process.title = 'bdy';
|
|
12
|
+
// Node writes to a pipe asynchronously and process.exit() does not wait for
|
|
13
|
+
// the buffer to drain, so large output piped to another process was cut at a
|
|
14
|
+
// pipe-buffer boundary (files and POSIX TTYs are synchronous already).
|
|
15
|
+
// Blocking mode makes every write land before exit, on all Output paths.
|
|
16
|
+
for (const s of [process.stdout, process.stderr]) {
|
|
17
|
+
s._handle?.setBlocking?.(true);
|
|
18
|
+
}
|
|
19
|
+
// The cost of blocking mode: a reader that closes early (`bdy ... | head`)
|
|
20
|
+
// turns further writes into EPIPE errors, which surface here as uncaught
|
|
21
|
+
// exceptions. That is the reader saying "got enough", not a failure - end
|
|
22
|
+
// quietly like any well-behaved pipe writer.
|
|
23
|
+
const isEpipe = (err) => err?.code === 'EPIPE' || err?.cause?.code === 'EPIPE';
|
|
12
24
|
process.on('uncaughtException', (err) => {
|
|
25
|
+
if (isEpipe(err))
|
|
26
|
+
process.exit(0);
|
|
13
27
|
logger_1.default.fatal(err);
|
|
14
28
|
output_1.default.exitError(err);
|
|
15
29
|
});
|
package/distTs/src/input.js
CHANGED
|
@@ -368,8 +368,19 @@ class Input {
|
|
|
368
368
|
const ApiClient = require('./api/client').default;
|
|
369
369
|
const { token: t, refreshToken, clientId, clientToken, clientSecret, } = this.restApiToken(allowNoToken, token);
|
|
370
370
|
const baseUrl = this.restApiBaseUrl(api, region, t);
|
|
371
|
+
ApiClient.onRateLimitLow = (remaining, reset) => output_1.default.noteWarning((0, texts_1.WARN_REST_API_RATE_LIMIT_LOW)(remaining, reset));
|
|
371
372
|
return new ApiClient(baseUrl, t, refreshToken, clientId, clientSecret, clientToken);
|
|
372
373
|
}
|
|
374
|
+
// Stderr note for list commands whose PROJECT scope was inferred (linked
|
|
375
|
+
// project / BUDDY_PROJECT) rather than requested via -p or --scope, so the
|
|
376
|
+
// narrowing is never silent.
|
|
377
|
+
static projectScopeNote(options, project, scope, resource) {
|
|
378
|
+
if (options.format === 'json' || options.scope || options.project)
|
|
379
|
+
return;
|
|
380
|
+
if (scope !== 'PROJECT' || !project)
|
|
381
|
+
return;
|
|
382
|
+
output_1.default.note((0, texts_1.TXT_LIST_PROJECT_SCOPE_NOTE)(resource, project));
|
|
383
|
+
}
|
|
373
384
|
static routeType(type) {
|
|
374
385
|
if (!type)
|
|
375
386
|
return distro_1.ROUTE_TYPE.DIRECT_PROXY;
|
|
@@ -1087,6 +1098,54 @@ class Input {
|
|
|
1087
1098
|
return distro_1.SCOPE.PROJECT;
|
|
1088
1099
|
return distro_1.SCOPE.WORKSPACE;
|
|
1089
1100
|
}
|
|
1101
|
+
static targetScope(project, pipeline, environment, scope) {
|
|
1102
|
+
Input.targetLine(project, pipeline, environment);
|
|
1103
|
+
if (scope) {
|
|
1104
|
+
scope = scope.toUpperCase();
|
|
1105
|
+
if (scope === utils_1.TARGET_SCOPE.WORKSPACE) {
|
|
1106
|
+
return scope;
|
|
1107
|
+
}
|
|
1108
|
+
if (scope === utils_1.TARGET_SCOPE.PROJECT) {
|
|
1109
|
+
if (!project)
|
|
1110
|
+
output_1.default.exitError(texts_1.ERR_COMMAND_SCOPE_NO_PROJECT);
|
|
1111
|
+
return scope;
|
|
1112
|
+
}
|
|
1113
|
+
if (scope === utils_1.TARGET_SCOPE.PIPELINE) {
|
|
1114
|
+
if (!pipeline)
|
|
1115
|
+
output_1.default.exitError(texts_1.ERR_TARGET_SCOPE_NO_PIPELINE);
|
|
1116
|
+
return scope;
|
|
1117
|
+
}
|
|
1118
|
+
if (scope === utils_1.TARGET_SCOPE.ENVIRONMENT) {
|
|
1119
|
+
if (!environment)
|
|
1120
|
+
output_1.default.exitError(texts_1.ERR_TARGET_SCOPE_NO_ENVIRONMENT);
|
|
1121
|
+
return scope;
|
|
1122
|
+
}
|
|
1123
|
+
return output_1.default.exitError(texts_1.ERR_TARGET_SCOPE);
|
|
1124
|
+
}
|
|
1125
|
+
if (pipeline)
|
|
1126
|
+
return utils_1.TARGET_SCOPE.PIPELINE;
|
|
1127
|
+
if (environment)
|
|
1128
|
+
return utils_1.TARGET_SCOPE.ENVIRONMENT;
|
|
1129
|
+
if (project)
|
|
1130
|
+
return utils_1.TARGET_SCOPE.PROJECT;
|
|
1131
|
+
return utils_1.TARGET_SCOPE.WORKSPACE;
|
|
1132
|
+
}
|
|
1133
|
+
// Line context for resolving a target identifier via /identifiers:
|
|
1134
|
+
// params describe one line from workspace down (ws -> project -> pipeline
|
|
1135
|
+
// or environment), searched from the deepest scope up.
|
|
1136
|
+
static targetLine(project, pipeline, environment) {
|
|
1137
|
+
if (pipeline && !project) {
|
|
1138
|
+
output_1.default.exitError(texts_1.ERR_TARGET_PIPELINE_REQUIRES_PROJECT);
|
|
1139
|
+
}
|
|
1140
|
+
if (pipeline && environment) {
|
|
1141
|
+
output_1.default.exitError(texts_1.ERR_TARGET_PIPELINE_ENV_CONFLICT);
|
|
1142
|
+
}
|
|
1143
|
+
return {
|
|
1144
|
+
project: project || undefined,
|
|
1145
|
+
pipeline,
|
|
1146
|
+
environment,
|
|
1147
|
+
};
|
|
1148
|
+
}
|
|
1090
1149
|
static restApiProject(project, allowNull) {
|
|
1091
1150
|
const ProjectCfg = require('./project/cfg').default;
|
|
1092
1151
|
let p = process.env.BUDDY_PROJECT;
|
|
@@ -1107,6 +1166,12 @@ class Input {
|
|
|
1107
1166
|
output_1.default.exitError(texts_1.ERR_SANDBOX_BOOT_SCRIPT_NOT_EXISTS);
|
|
1108
1167
|
return node_fs_1.default.readFileSync(p, 'utf8');
|
|
1109
1168
|
}
|
|
1169
|
+
static tags(tags) {
|
|
1170
|
+
return tags
|
|
1171
|
+
.split(',')
|
|
1172
|
+
.map((t) => t.trim())
|
|
1173
|
+
.filter(Boolean);
|
|
1174
|
+
}
|
|
1110
1175
|
static restApiYaml(yaml) {
|
|
1111
1176
|
if (!yaml)
|
|
1112
1177
|
output_1.default.exitError(texts_1.ERR_REST_API_YAML);
|
|
@@ -1191,7 +1191,9 @@ class OutputPipeline {
|
|
|
1191
1191
|
this.jsonLog(logs[actionLogsStart]);
|
|
1192
1192
|
}
|
|
1193
1193
|
else {
|
|
1194
|
-
|
|
1194
|
+
// log lines are payload inside markup - escape so % and ^
|
|
1195
|
+
// sequences in build output render verbatim
|
|
1196
|
+
output_1.default.normal(output_1.default.getTermKitDimColor(` ${output_1.default.escapeTermKit(logs[actionLogsStart])}`));
|
|
1195
1197
|
}
|
|
1196
1198
|
}
|
|
1197
1199
|
if (action &&
|
|
@@ -1221,9 +1223,9 @@ class OutputPipeline {
|
|
|
1221
1223
|
let line = '';
|
|
1222
1224
|
if (i > 0)
|
|
1223
1225
|
line += ' ';
|
|
1224
|
-
line += output_1.default.getTermKitMutedColor(v.key);
|
|
1226
|
+
line += output_1.default.getTermKitMutedColor(output_1.default.escapeTermKit(v.key));
|
|
1225
1227
|
line += output_1.default.getTermKitDimColor(` = "`);
|
|
1226
|
-
line += output_1.default.getTermKitBlueColor(v.value);
|
|
1228
|
+
line += output_1.default.getTermKitBlueColor(output_1.default.escapeTermKit(v.value));
|
|
1227
1229
|
line += output_1.default.getTermKitDimColor('"');
|
|
1228
1230
|
output_1.default.normal(line);
|
|
1229
1231
|
});
|
package/distTs/src/output.js
CHANGED
|
@@ -134,6 +134,32 @@ class Output {
|
|
|
134
134
|
msg += '\n';
|
|
135
135
|
getTerminal()(msg);
|
|
136
136
|
}
|
|
137
|
+
// Payload output (command results, logs, JSON): bypasses terminal-kit,
|
|
138
|
+
// whose printf-style formatting and ^-markup corrupt data containing
|
|
139
|
+
// % or ^ sequences. newLine=false emits a mid-stream chunk, keeping
|
|
140
|
+
// chunk boundaries out of the payload.
|
|
141
|
+
static data(txt, newLine = true) {
|
|
142
|
+
process.stdout.write(newLine ? `${txt}\n` : txt);
|
|
143
|
+
}
|
|
144
|
+
// For payload embedded in terminal-kit markup (e.g. a dimmed log line):
|
|
145
|
+
// neutralizes the ^-markup and %-formatting the payload would otherwise
|
|
146
|
+
// trigger, so it renders verbatim while the surrounding markup still
|
|
147
|
+
// works. Only for strings printed via terminal() - ScreenBuffer.put does
|
|
148
|
+
// not printf-format, so there %% would render double.
|
|
149
|
+
static escapeTermKit(txt) {
|
|
150
|
+
return txt.replace(/[%^]/g, '$&$&');
|
|
151
|
+
}
|
|
152
|
+
static writeStderr(txt) {
|
|
153
|
+
process.stderr.write(`${txt}\n`);
|
|
154
|
+
}
|
|
155
|
+
// Narration accompanying payload output goes to stderr so stdout stays
|
|
156
|
+
// parseable by scripts.
|
|
157
|
+
static note(txt) {
|
|
158
|
+
this.writeStderr(this.getMutedColor(txt));
|
|
159
|
+
}
|
|
160
|
+
static noteWarning(txt) {
|
|
161
|
+
this.writeStderr(this.getYellowColor(txt));
|
|
162
|
+
}
|
|
137
163
|
static gray(txt, newLine = true) {
|
|
138
164
|
this.normal(this.getMutedColor(txt), newLine);
|
|
139
165
|
}
|
|
@@ -624,7 +650,7 @@ class Output {
|
|
|
624
650
|
_terminal.grabInput(false);
|
|
625
651
|
}
|
|
626
652
|
static json(json) {
|
|
627
|
-
this.
|
|
653
|
+
this.data(JSON.stringify(json));
|
|
628
654
|
}
|
|
629
655
|
static exitError(err) {
|
|
630
656
|
this.clearUndici();
|
|
@@ -642,7 +668,9 @@ class Output {
|
|
|
642
668
|
if (isDebug)
|
|
643
669
|
msg += `\n${e.stack}`;
|
|
644
670
|
}
|
|
645
|
-
|
|
671
|
+
// Raw stderr: error messages carry user/API data (SQL, URLs) that
|
|
672
|
+
// terminal-kit's %-formatting would corrupt.
|
|
673
|
+
this.writeStderr(this.getRedColor(String(msg)));
|
|
646
674
|
process.exit(1);
|
|
647
675
|
}
|
|
648
676
|
}
|