@copado/agentia-cli 0.71.0-alpha.0 → 0.72.0-alpha.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/LICENSE.md +9 -9
- package/README.md +172 -170
- package/dist/commands/cicd/work/done.js +36 -22
- package/dist/commands/cicd/work/done.js.map +1 -1
- package/dist/commands/cicd/work/get.js +19 -10
- package/dist/commands/cicd/work/get.js.map +1 -1
- package/dist/commands/cicd/work/push.js +26 -21
- package/dist/commands/cicd/work/push.js.map +1 -1
- package/dist/commands/cicd/work/set.js +11 -20
- package/dist/commands/cicd/work/set.js.map +1 -1
- package/dist/commands/cicd/work/submit.js +40 -23
- package/dist/commands/cicd/work/submit.js.map +1 -1
- package/dist/commands/cicd/work/test.js +4 -0
- package/dist/commands/cicd/work/test.js.map +1 -1
- package/dist/hooks/init/migrate-user-config.d.ts +3 -0
- package/dist/hooks/init/migrate-user-config.js +17 -0
- package/dist/hooks/init/migrate-user-config.js.map +1 -0
- package/dist/lib/agentia-command.d.ts +3 -0
- package/dist/lib/agentia-command.js +11 -0
- package/dist/lib/agentia-command.js.map +1 -1
- package/dist/lib/defaults-store.d.ts +15 -0
- package/dist/lib/defaults-store.js +128 -21
- package/dist/lib/defaults-store.js.map +1 -1
- package/dist/lib/format.js +73 -26
- package/dist/lib/format.js.map +1 -1
- package/dist/lib/git-worktree.d.ts +18 -0
- package/dist/lib/git-worktree.js +44 -0
- package/dist/lib/git-worktree.js.map +1 -1
- package/dist/lib/mcp/server.js +4 -0
- package/dist/lib/mcp/server.js.map +1 -1
- package/dist/lib/mcp/work-tools.js +25 -57
- package/dist/lib/mcp/work-tools.js.map +1 -1
- package/dist/lib/project-setup.js +59 -7
- package/dist/lib/project-setup.js.map +1 -1
- package/dist/lib/setup-templates.d.ts +3 -1
- package/dist/lib/setup-templates.js +3 -1
- package/dist/lib/setup-templates.js.map +1 -1
- package/dist/lib/var-replacement.d.ts +47 -0
- package/dist/lib/var-replacement.js +150 -0
- package/dist/lib/var-replacement.js.map +1 -0
- package/dist/lib/work-get-selection.d.ts +6 -0
- package/dist/lib/work-get-selection.js +44 -0
- package/dist/lib/work-get-selection.js.map +1 -0
- package/dist/lib/work-local-run-tests.d.ts +3 -0
- package/dist/lib/work-local-run-tests.js +5 -0
- package/dist/lib/work-local-run-tests.js.map +1 -1
- package/dist/lib/work-push.d.ts +21 -1
- package/dist/lib/work-push.js +203 -40
- package/dist/lib/work-push.js.map +1 -1
- package/dist/lib/work-set-selection.d.ts +11 -0
- package/dist/lib/work-set-selection.js +27 -0
- package/dist/lib/work-set-selection.js.map +1 -0
- package/dist/lib/work-types.d.ts +26 -0
- package/dist/lib/work-types.js +16 -0
- package/dist/lib/work-types.js.map +1 -1
- package/dist/lib/yaml-replacement.d.ts +41 -0
- package/dist/lib/yaml-replacement.js +129 -0
- package/dist/lib/yaml-replacement.js.map +1 -0
- package/oclif.manifest.json +483 -483
- package/package.json +7 -4
- package/vendor/java/var-replace-command.jar +0 -0
- package/vendor/java/yaml-replace-command.jar +0 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { access, mkdtemp, rm, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { tmpdir } from 'node:os';
|
|
4
|
+
import { dirname, join, resolve } from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import { logVerboseDebug } from './debug.js';
|
|
7
|
+
const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), '../..');
|
|
8
|
+
export const VAR_REPLACE_COMMAND_JAR_NAME = 'var-replace-command.jar';
|
|
9
|
+
function defaultRunProcess(command, args, options) {
|
|
10
|
+
return new Promise((resolvePromise, reject) => {
|
|
11
|
+
const child = spawn(command, args, {
|
|
12
|
+
cwd: options.cwd,
|
|
13
|
+
env: process.env,
|
|
14
|
+
});
|
|
15
|
+
let stdout = '';
|
|
16
|
+
let stderr = '';
|
|
17
|
+
child.stdout.on('data', (chunk) => {
|
|
18
|
+
stdout += String(chunk);
|
|
19
|
+
});
|
|
20
|
+
child.stderr.on('data', (chunk) => {
|
|
21
|
+
stderr += String(chunk);
|
|
22
|
+
});
|
|
23
|
+
child.on('error', (error) => {
|
|
24
|
+
reject(error);
|
|
25
|
+
});
|
|
26
|
+
child.on('close', (code) => {
|
|
27
|
+
resolvePromise({ exitCode: code, stderr, stdout });
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
export function resolveVarReplaceCommandJar() {
|
|
32
|
+
const override = process.env.AGENTIA_VAR_REPLACE_COMMAND_JAR?.trim();
|
|
33
|
+
if (override !== undefined && override.length > 0) {
|
|
34
|
+
return override;
|
|
35
|
+
}
|
|
36
|
+
return join(packageRoot, 'vendor/java', VAR_REPLACE_COMMAND_JAR_NAME);
|
|
37
|
+
}
|
|
38
|
+
export function resolveVarReplacementJavaCommand() {
|
|
39
|
+
const override = process.env.AGENTIA_JAVA?.trim();
|
|
40
|
+
return override !== undefined && override.length > 0 ? override : 'java';
|
|
41
|
+
}
|
|
42
|
+
export function formatVarReplacementCommand(command, args) {
|
|
43
|
+
return [command, ...args].join(' ');
|
|
44
|
+
}
|
|
45
|
+
/** JSON path list consumed by var-replace-command `-c` / `--include`. */
|
|
46
|
+
export function buildFilesInScopeJson(changedFiles) {
|
|
47
|
+
return `${JSON.stringify(changedFiles, null, 0)}\n`;
|
|
48
|
+
}
|
|
49
|
+
/** JSON consumed by var-replace-command `<replacements>`. */
|
|
50
|
+
export function buildVarReplacementEntries(variables) {
|
|
51
|
+
const entries = [];
|
|
52
|
+
for (const variable of variables) {
|
|
53
|
+
const name = variable.name?.trim();
|
|
54
|
+
if (name === undefined || name.length === 0 || typeof variable.value !== 'string') {
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
const scope = variable.scope?.trim();
|
|
58
|
+
entries.push({
|
|
59
|
+
name,
|
|
60
|
+
scope: scope !== undefined && scope.length > 0 ? scope : '*',
|
|
61
|
+
value: variable.value,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
return entries;
|
|
65
|
+
}
|
|
66
|
+
export function buildVarReplacementsJson(variables) {
|
|
67
|
+
return `${JSON.stringify(buildVarReplacementEntries(variables))}\n`;
|
|
68
|
+
}
|
|
69
|
+
function redactVarReplacementsForDebug(variables) {
|
|
70
|
+
return JSON.stringify(buildVarReplacementEntries(variables).map((entry) => ({
|
|
71
|
+
name: entry.name,
|
|
72
|
+
scope: entry.scope,
|
|
73
|
+
value: '[redacted]',
|
|
74
|
+
})), null, 2);
|
|
75
|
+
}
|
|
76
|
+
function logVarReplacementVerboseInput(options) {
|
|
77
|
+
logVerboseDebug('Var replacement command', formatVarReplacementCommand(options.command, options.args));
|
|
78
|
+
logVerboseDebug('Var replacement rules', redactVarReplacementsForDebug(options.environmentVariables));
|
|
79
|
+
logVerboseDebug('Var replacement files in scope', options.changedFiles.length > 0 ? options.changedFiles.join('\n') : '(empty)');
|
|
80
|
+
}
|
|
81
|
+
/** Emit var-replace-command process streams for normal CLI output. */
|
|
82
|
+
export function emitVarReplacementProcessOutput(result) {
|
|
83
|
+
for (const stream of [result.stdout, result.stderr]) {
|
|
84
|
+
const trimmed = stream.trimEnd();
|
|
85
|
+
if (trimmed.length > 0) {
|
|
86
|
+
process.stderr.write(`${trimmed}\n`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
export async function runVarReplacement(options) {
|
|
91
|
+
const jarPath = resolveVarReplaceCommandJar();
|
|
92
|
+
try {
|
|
93
|
+
await access(jarPath);
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
throw new Error(`Variable replacement JAR not found at ${jarPath}. Run \`npm run build-java-dependencies\` or set AGENTIA_VAR_REPLACE_COMMAND_JAR.`);
|
|
97
|
+
}
|
|
98
|
+
const tempDir = await mkdtemp(join(tmpdir(), 'agentia-var-replace-'));
|
|
99
|
+
const replacementsPath = join(tempDir, 'replacements.json');
|
|
100
|
+
const includePath = join(tempDir, 'files-in-scope.json');
|
|
101
|
+
try {
|
|
102
|
+
await writeFile(replacementsPath, buildVarReplacementsJson(options.environmentVariables), 'utf8');
|
|
103
|
+
await writeFile(includePath, buildFilesInScopeJson(options.changedFiles), 'utf8');
|
|
104
|
+
const javaCommand = resolveVarReplacementJavaCommand();
|
|
105
|
+
// `-v` replaces environment values with `{!Environment.NAME}` placeholders (commit direction).
|
|
106
|
+
// `-d` allows the same variable name to appear for multiple environments.
|
|
107
|
+
const args = [
|
|
108
|
+
'-jar',
|
|
109
|
+
jarPath,
|
|
110
|
+
replacementsPath,
|
|
111
|
+
options.targetFolder,
|
|
112
|
+
'-v',
|
|
113
|
+
'-d',
|
|
114
|
+
'-c',
|
|
115
|
+
includePath,
|
|
116
|
+
];
|
|
117
|
+
logVarReplacementVerboseInput({
|
|
118
|
+
args,
|
|
119
|
+
changedFiles: options.changedFiles,
|
|
120
|
+
command: javaCommand,
|
|
121
|
+
environmentVariables: options.environmentVariables,
|
|
122
|
+
});
|
|
123
|
+
const runProcess = options.runProcess ?? varReplacementProcessForTests() ?? defaultRunProcess;
|
|
124
|
+
const result = await runProcess(javaCommand, args, { cwd: options.cwd });
|
|
125
|
+
emitVarReplacementProcessOutput(result);
|
|
126
|
+
return {
|
|
127
|
+
args,
|
|
128
|
+
command: javaCommand,
|
|
129
|
+
exitCode: result.exitCode,
|
|
130
|
+
stderr: result.stderr,
|
|
131
|
+
stdout: result.stdout,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
finally {
|
|
135
|
+
await rm(tempDir, { force: true, recursive: true });
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
let runProcessForTests;
|
|
139
|
+
/** Test-only hook */
|
|
140
|
+
export function setVarReplacementProcessForTests(runner) {
|
|
141
|
+
runProcessForTests = runner;
|
|
142
|
+
}
|
|
143
|
+
/** Test-only hook */
|
|
144
|
+
export function resetVarReplacementProcessForTests() {
|
|
145
|
+
runProcessForTests = undefined;
|
|
146
|
+
}
|
|
147
|
+
export function varReplacementProcessForTests() {
|
|
148
|
+
return runProcessForTests;
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=var-replacement.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"var-replacement.js","sourceRoot":"","sources":["../../src/lib/var-replacement.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { WorkGetResponse } from './work-types.js';
|
|
2
|
+
/**
|
|
3
|
+
* When headless work/get returns `devOrgBranch` for the active work item,
|
|
4
|
+
* persist it (and refresh base/PR URLs when present) into config.user.json.
|
|
5
|
+
*/
|
|
6
|
+
export declare function persistActiveDevOrgBranchFromGet(getResponse: WorkGetResponse): Promise<void>;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { readLastBaseBranch, readLastPullRequestBaseUrl, readLastWorkItem, readLastWorkItemId, writeLastWorkSelection, } from './defaults-store.js';
|
|
2
|
+
function nonEmpty(value) {
|
|
3
|
+
const trimmed = value?.trim();
|
|
4
|
+
return trimmed !== undefined && trimmed.length > 0 ? trimmed : undefined;
|
|
5
|
+
}
|
|
6
|
+
function isActiveWorkItem(responseId, responseName, lastWorkItemId, lastWorkItem) {
|
|
7
|
+
if (responseId !== undefined && lastWorkItemId !== undefined && responseId === lastWorkItemId) {
|
|
8
|
+
return true;
|
|
9
|
+
}
|
|
10
|
+
return (responseName !== undefined &&
|
|
11
|
+
lastWorkItem !== undefined &&
|
|
12
|
+
responseName.toUpperCase() === lastWorkItem.toUpperCase());
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* When headless work/get returns `devOrgBranch` for the active work item,
|
|
16
|
+
* persist it (and refresh base/PR URLs when present) into config.user.json.
|
|
17
|
+
*/
|
|
18
|
+
export async function persistActiveDevOrgBranchFromGet(getResponse) {
|
|
19
|
+
const devOrgBranch = nonEmpty(getResponse.devOrgBranch);
|
|
20
|
+
if (devOrgBranch === undefined) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const lastWorkItemId = nonEmpty(await readLastWorkItemId());
|
|
24
|
+
const lastWorkItem = nonEmpty(await readLastWorkItem());
|
|
25
|
+
if (lastWorkItem === undefined || lastWorkItemId === undefined) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
if (!isActiveWorkItem(nonEmpty(getResponse.workItem.id), nonEmpty(getResponse.workItem.name), lastWorkItemId, lastWorkItem)) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const baseBranch = nonEmpty(getResponse.baseBranch) ?? nonEmpty(await readLastBaseBranch());
|
|
32
|
+
if (baseBranch === undefined) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const pullRequestBaseUrl = nonEmpty(getResponse.pullRequestBaseUrl) ?? nonEmpty(await readLastPullRequestBaseUrl());
|
|
36
|
+
await writeLastWorkSelection({
|
|
37
|
+
lastBaseBranch: baseBranch,
|
|
38
|
+
lastDevOrgBranch: devOrgBranch,
|
|
39
|
+
...(pullRequestBaseUrl === undefined ? {} : { lastPullRequestBaseUrl: pullRequestBaseUrl }),
|
|
40
|
+
lastWorkItem,
|
|
41
|
+
lastWorkItemId,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=work-get-selection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"work-get-selection.js","sourceRoot":"","sources":["../../src/lib/work-get-selection.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,kBAAkB,EAClB,0BAA0B,EAC1B,gBAAgB,EAChB,kBAAkB,EAClB,sBAAsB,GACvB,MAAM,qBAAqB,CAAA;AAE5B,SAAS,QAAQ,CAAC,KAAgC;IAChD,MAAM,OAAO,GAAG,KAAK,EAAE,IAAI,EAAE,CAAA;IAC7B,OAAO,OAAO,KAAK,SAAS,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAA;AAC1E,CAAC;AAED,SAAS,gBAAgB,CACvB,UAA8B,EAC9B,YAAgC,EAChC,cAAkC,EAClC,YAAgC;IAEhC,IAAI,UAAU,KAAK,SAAS,IAAI,cAAc,KAAK,SAAS,IAAI,UAAU,KAAK,cAAc,EAAE,CAAC;QAC9F,OAAO,IAAI,CAAA;IACb,CAAC;IAED,OAAO,CACL,YAAY,KAAK,SAAS;QAC1B,YAAY,KAAK,SAAS;QAC1B,YAAY,CAAC,WAAW,EAAE,KAAK,YAAY,CAAC,WAAW,EAAE,CAC1D,CAAA;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,gCAAgC,CACpD,WAA4B;IAE5B,MAAM,YAAY,GAAG,QAAQ,CAAC,WAAW,CAAC,YAAY,CAAC,CAAA;IACvD,IAAI,YAAY,KAAK,SAAS,EAAE,CAAC;QAC/B,OAAM;IACR,CAAC;IAED,MAAM,cAAc,GAAG,QAAQ,CAAC,MAAM,kBAAkB,EAAE,CAAC,CAAA;IAC3D,MAAM,YAAY,GAAG,QAAQ,CAAC,MAAM,gBAAgB,EAAE,CAAC,CAAA;IACvD,IAAI,YAAY,KAAK,SAAS,IAAI,cAAc,KAAK,SAAS,EAAE,CAAC;QAC/D,OAAM;IACR,CAAC;IAED,IACE,CAAC,gBAAgB,CACf,QAAQ,CAAC,WAAW,CAAC,QAAQ,CAAC,EAAE,CAAC,EACjC,QAAQ,CAAC,WAAW,CAAC,QAAQ,CAAC,IAAI,CAAC,EACnC,cAAc,EACd,YAAY,CACb,EACD,CAAC;QACD,OAAM;IACR,CAAC;IAED,MAAM,UAAU,GAAG,QAAQ,CAAC,WAAW,CAAC,UAAU,CAAC,IAAI,QAAQ,CAAC,MAAM,kBAAkB,EAAE,CAAC,CAAA;IAC3F,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;QAC7B,OAAM;IACR,CAAC;IAED,MAAM,kBAAkB,GACtB,QAAQ,CAAC,WAAW,CAAC,kBAAkB,CAAC,IAAI,QAAQ,CAAC,MAAM,0BAA0B,EAAE,CAAC,CAAA;IAE1F,MAAM,sBAAsB,CAAC;QAC3B,cAAc,EAAE,UAAU;QAC1B,gBAAgB,EAAE,YAAY;QAC9B,GAAG,CAAC,kBAAkB,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAC,sBAAsB,EAAE,kBAAkB,EAAC,CAAC;QACzF,YAAY;QACZ,cAAc;KACf,CAAC,CAAA;AACJ,CAAC"}
|
|
@@ -2,6 +2,7 @@ import { type LocalQualityGatesError, type LocalQualityGatesRunResult } from './
|
|
|
2
2
|
import { type LocalRunTestsStatus } from './work-types.js';
|
|
3
3
|
export type ReportLocalRunTestsOptions = {
|
|
4
4
|
logs?: string;
|
|
5
|
+
onLog?: (message: string) => void;
|
|
5
6
|
onWarning?: (message: string) => void;
|
|
6
7
|
status: LocalRunTestsStatus;
|
|
7
8
|
workItem: string;
|
|
@@ -10,6 +11,7 @@ export type ReportLocalRunTestsOptions = {
|
|
|
10
11
|
export declare function reportLocalRunTests(options: ReportLocalRunTestsOptions): Promise<void>;
|
|
11
12
|
/** Reports success or skipped when local quality gates were available; no-ops otherwise. */
|
|
12
13
|
export declare function reportLocalQualityGatesOutcome(options: {
|
|
14
|
+
onLog?: (message: string) => void;
|
|
13
15
|
onWarning?: (message: string) => void;
|
|
14
16
|
qualityGates: LocalQualityGatesRunResult;
|
|
15
17
|
workItem: string;
|
|
@@ -17,6 +19,7 @@ export declare function reportLocalQualityGatesOutcome(options: {
|
|
|
17
19
|
/** Reports failure for a local quality-gates script error. */
|
|
18
20
|
export declare function reportLocalQualityGatesFailure(options: {
|
|
19
21
|
error: LocalQualityGatesError;
|
|
22
|
+
onLog?: (message: string) => void;
|
|
20
23
|
onWarning?: (message: string) => void;
|
|
21
24
|
workItem: string;
|
|
22
25
|
}): Promise<void>;
|
|
@@ -10,6 +10,7 @@ export async function reportLocalRunTests(options) {
|
|
|
10
10
|
if (options.logs !== undefined && options.logs.length > 0) {
|
|
11
11
|
body.logs = options.logs;
|
|
12
12
|
}
|
|
13
|
+
options.onLog?.('Uploading local quality gates results...');
|
|
13
14
|
try {
|
|
14
15
|
await CicdGateway.call({
|
|
15
16
|
body,
|
|
@@ -19,6 +20,7 @@ export async function reportLocalRunTests(options) {
|
|
|
19
20
|
},
|
|
20
21
|
method: 'POST',
|
|
21
22
|
});
|
|
23
|
+
options.onLog?.('Done.');
|
|
22
24
|
}
|
|
23
25
|
catch (error) {
|
|
24
26
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -30,6 +32,7 @@ export async function reportLocalQualityGatesOutcome(options) {
|
|
|
30
32
|
if (options.qualityGates.ran) {
|
|
31
33
|
await reportLocalRunTests({
|
|
32
34
|
logs: options.qualityGates.output,
|
|
35
|
+
onLog: options.onLog,
|
|
33
36
|
onWarning: options.onWarning,
|
|
34
37
|
status: 'success',
|
|
35
38
|
workItem: options.workItem,
|
|
@@ -38,6 +41,7 @@ export async function reportLocalQualityGatesOutcome(options) {
|
|
|
38
41
|
}
|
|
39
42
|
if (options.qualityGates.skipped) {
|
|
40
43
|
await reportLocalRunTests({
|
|
44
|
+
onLog: options.onLog,
|
|
41
45
|
onWarning: options.onWarning,
|
|
42
46
|
status: 'skipped',
|
|
43
47
|
workItem: options.workItem,
|
|
@@ -48,6 +52,7 @@ export async function reportLocalQualityGatesOutcome(options) {
|
|
|
48
52
|
export async function reportLocalQualityGatesFailure(options) {
|
|
49
53
|
await reportLocalRunTests({
|
|
50
54
|
logs: options.error.output,
|
|
55
|
+
onLog: options.onLog,
|
|
51
56
|
onWarning: options.onWarning,
|
|
52
57
|
status: 'failure',
|
|
53
58
|
workItem: options.workItem,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"work-local-run-tests.js","sourceRoot":"","sources":["../../src/lib/work-local-run-tests.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,WAAW,EAAC,MAAM,mBAAmB,CAAA;AAE7C,OAAO,EAIL,yBAAyB,GAC1B,MAAM,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"work-local-run-tests.js","sourceRoot":"","sources":["../../src/lib/work-local-run-tests.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,WAAW,EAAC,MAAM,mBAAmB,CAAA;AAE7C,OAAO,EAIL,yBAAyB,GAC1B,MAAM,iBAAiB,CAAA;AAUxB,+GAA+G;AAC/G,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,OAAmC;IAC3E,MAAM,IAAI,GAAiC;QACzC,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,QAAQ,EAAE,OAAO,CAAC,QAAQ;KAC3B,CAAA;IACD,mFAAmF;IACnF,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1D,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAA;IAC1B,CAAC;IAED,OAAO,CAAC,KAAK,EAAE,CAAC,0CAA0C,CAAC,CAAA;IAC3D,IAAI,CAAC;QACH,MAAM,WAAW,CAAC,IAAI,CAAwB;YAC5C,IAAI;YACJ,QAAQ,EAAE,yBAAyB,EAAE;YACrC,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;aACnC;YACD,MAAM,EAAE,MAAM;SACf,CAAC,CAAA;QACF,OAAO,CAAC,KAAK,EAAE,CAAC,OAAO,CAAC,CAAA;IAC1B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;QACtE,OAAO,CAAC,SAAS,EAAE,CAAC,gDAAgD,OAAO,CAAC,MAAM,MAAM,OAAO,EAAE,CAAC,CAAA;IACpG,CAAC;AACH,CAAC;AAED,4FAA4F;AAC5F,MAAM,CAAC,KAAK,UAAU,8BAA8B,CAAC,OAKpD;IACC,IAAI,OAAO,CAAC,YAAY,CAAC,GAAG,EAAE,CAAC;QAC7B,MAAM,mBAAmB,CAAC;YACxB,IAAI,EAAE,OAAO,CAAC,YAAY,CAAC,MAAM;YACjC,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,MAAM,EAAE,SAAS;YACjB,QAAQ,EAAE,OAAO,CAAC,QAAQ;SAC3B,CAAC,CAAA;QACF,OAAM;IACR,CAAC;IAED,IAAI,OAAO,CAAC,YAAY,CAAC,OAAO,EAAE,CAAC;QACjC,MAAM,mBAAmB,CAAC;YACxB,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,MAAM,EAAE,SAAS;YACjB,QAAQ,EAAE,OAAO,CAAC,QAAQ;SAC3B,CAAC,CAAA;IACJ,CAAC;AACH,CAAC;AAED,8DAA8D;AAC9D,MAAM,CAAC,KAAK,UAAU,8BAA8B,CAAC,OAKpD;IACC,MAAM,mBAAmB,CAAC;QACxB,IAAI,EAAE,OAAO,CAAC,KAAK,CAAC,MAAM;QAC1B,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,MAAM,EAAE,SAAS;QACjB,QAAQ,EAAE,OAAO,CAAC,QAAQ;KAC3B,CAAC,CAAA;AACJ,CAAC"}
|
package/dist/lib/work-push.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type HeadlessCommandResult, type HeadlessPushRequest, type WorkPushResult } from './work-types.js';
|
|
1
|
+
import { type HeadlessCommandResult, type HeadlessPushCommit, type HeadlessPushRequest, type WorkPushResult } from './work-types.js';
|
|
2
2
|
export declare const WORK_PUSH_RETRY_DOUBLE_AFTER_SECONDS: number;
|
|
3
3
|
export type WorkPushHooks = {
|
|
4
4
|
display: (render: () => void) => void;
|
|
@@ -13,14 +13,34 @@ export declare class WorkPushValidationError extends Error {
|
|
|
13
13
|
suggestions?: string[];
|
|
14
14
|
});
|
|
15
15
|
}
|
|
16
|
+
export type ResolvedWorkPushContext = {
|
|
17
|
+
baseBranch: string;
|
|
18
|
+
branch: string;
|
|
19
|
+
devOrgBranch: string;
|
|
20
|
+
workItem: string;
|
|
21
|
+
workItemId: string;
|
|
22
|
+
};
|
|
23
|
+
export declare function resolveActiveFeatureWorkItem(): Promise<{
|
|
24
|
+
branch: string;
|
|
25
|
+
workItem: string;
|
|
26
|
+
}>;
|
|
27
|
+
/** Resolve lastWorkItem / branch / lastBaseBranch / lastDevOrgBranch for work push. */
|
|
28
|
+
export declare function resolveWorkPushContext(): Promise<ResolvedWorkPushContext>;
|
|
16
29
|
export type SleepFn = (ms: number) => Promise<void>;
|
|
17
30
|
export declare function setSleepForTests(nextSleep: SleepFn): void;
|
|
18
31
|
export declare function resetSleepForTests(): void;
|
|
32
|
+
export type WorkPushChangedFiles = {
|
|
33
|
+
addedOrModified: string[];
|
|
34
|
+
deleted: string[];
|
|
35
|
+
};
|
|
36
|
+
export declare function listChangedFilesFromCommits(commits: HeadlessPushCommit[]): WorkPushChangedFiles;
|
|
19
37
|
export declare function resolveWorkPushRetryDelaySeconds(retryAfterSeconds: number, waitedSeconds: number): number;
|
|
20
38
|
export declare function registerHeadlessPush(pushBody: HeadlessPushRequest, hooks: Pick<WorkPushHooks, 'display' | 'log'>, timeoutSeconds?: number): Promise<HeadlessCommandResult>;
|
|
21
39
|
export declare function performWorkPush(options: {
|
|
22
40
|
baseBranch: string;
|
|
23
41
|
branch: string;
|
|
42
|
+
devOrgBranch: string;
|
|
24
43
|
hooks: WorkPushHooks;
|
|
25
44
|
workItem: string;
|
|
45
|
+
workItemId: string;
|
|
26
46
|
}): Promise<WorkPushResult>;
|
package/dist/lib/work-push.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { CicdGateway, CicdGatewayError } from './cicd-gateway.js';
|
|
2
|
-
import { DEFAULT_WORK_PUSH_RETRY_TIMEOUT_SECONDS, readWorkPushRetryTimeoutSeconds } from './defaults-store.js';
|
|
3
|
-
import { getCommitsNameStatus, pushOriginBranch } from './git-worktree.js';
|
|
4
|
-
import {
|
|
2
|
+
import { DEFAULT_WORK_PUSH_RETRY_TIMEOUT_SECONDS, readLastBaseBranch, readLastDevOrgBranch, readLastWorkItem, readWorkPushRetryTimeoutSeconds, } from './defaults-store.js';
|
|
3
|
+
import { checkoutBranch, commitAllTrackedChanges, getCommitsNameStatus, getCurrentBranch, getGitRepoRoot, listPendingChanges, mergeTheirsIgnoreSpaceChange, pullOriginBranch, pushOriginBranch, restoreFeatureBranch, } from './git-worktree.js';
|
|
4
|
+
import { buildVarReplacementEntries, runVarReplacement } from './var-replacement.js';
|
|
5
|
+
import { resolveWorkItemId, WorkActiveResolutionError } from './work-active.js';
|
|
6
|
+
import { featureBranchName, formatWorkCommandMessage, parseNameStatusLog, workPushEndpoint, workPushLockEndpoint, } from './work-types.js';
|
|
7
|
+
import { fetchWorkPushReplacementRules, runYamlReplacement, } from './yaml-replacement.js';
|
|
5
8
|
export const WORK_PUSH_RETRY_DOUBLE_AFTER_SECONDS = 5 * 60;
|
|
6
9
|
export class WorkPushValidationError extends Error {
|
|
7
10
|
exit;
|
|
@@ -13,6 +16,43 @@ export class WorkPushValidationError extends Error {
|
|
|
13
16
|
this.suggestions = options.suggestions;
|
|
14
17
|
}
|
|
15
18
|
}
|
|
19
|
+
export async function resolveActiveFeatureWorkItem() {
|
|
20
|
+
const workItem = (await readLastWorkItem())?.trim();
|
|
21
|
+
if (workItem === undefined || workItem.length === 0) {
|
|
22
|
+
throw new WorkPushValidationError('No work item recorded. Run `agentia cicd work set <work-item>` first.');
|
|
23
|
+
}
|
|
24
|
+
const expectedBranch = featureBranchName(workItem);
|
|
25
|
+
const branch = await getCurrentBranch();
|
|
26
|
+
if (branch !== expectedBranch) {
|
|
27
|
+
throw new WorkPushValidationError(`Current branch '${branch}' must be '${expectedBranch}'.`, {
|
|
28
|
+
suggestions: [`Checkout ${expectedBranch} (from \`agentia cicd work set\`).`],
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
return { branch, workItem };
|
|
32
|
+
}
|
|
33
|
+
/** Resolve lastWorkItem / branch / lastBaseBranch / lastDevOrgBranch for work push. */
|
|
34
|
+
export async function resolveWorkPushContext() {
|
|
35
|
+
const { branch, workItem } = await resolveActiveFeatureWorkItem();
|
|
36
|
+
const baseBranch = (await readLastBaseBranch())?.trim();
|
|
37
|
+
if (baseBranch === undefined || baseBranch.length === 0) {
|
|
38
|
+
throw new WorkPushValidationError('No base branch recorded. Run `agentia cicd work set <work-item>` first.');
|
|
39
|
+
}
|
|
40
|
+
const devOrgBranch = (await readLastDevOrgBranch())?.trim();
|
|
41
|
+
if (devOrgBranch === undefined || devOrgBranch.length === 0) {
|
|
42
|
+
throw new WorkPushValidationError('No lastDevOrgBranch recorded in `.agentia/config.user.json`. Set it before pushing.');
|
|
43
|
+
}
|
|
44
|
+
let workItemId;
|
|
45
|
+
try {
|
|
46
|
+
workItemId = await resolveWorkItemId();
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
if (error instanceof WorkActiveResolutionError) {
|
|
50
|
+
throw new WorkPushValidationError(error.message);
|
|
51
|
+
}
|
|
52
|
+
throw error;
|
|
53
|
+
}
|
|
54
|
+
return { baseBranch, branch, devOrgBranch, workItem, workItemId };
|
|
55
|
+
}
|
|
16
56
|
const defaultSleep = async (ms) => new Promise((resolve) => {
|
|
17
57
|
setTimeout(resolve, ms);
|
|
18
58
|
});
|
|
@@ -25,20 +65,40 @@ export function setSleepForTests(nextSleep) {
|
|
|
25
65
|
export function resetSleepForTests() {
|
|
26
66
|
sleepFn = defaultSleep;
|
|
27
67
|
}
|
|
28
|
-
function
|
|
29
|
-
const
|
|
68
|
+
export function listChangedFilesFromCommits(commits) {
|
|
69
|
+
const addedOrModified = new Set();
|
|
70
|
+
const deleted = new Set();
|
|
30
71
|
for (const commit of commits) {
|
|
31
72
|
for (const file of commit.added) {
|
|
32
|
-
|
|
73
|
+
addedOrModified.add(file);
|
|
74
|
+
deleted.delete(file);
|
|
33
75
|
}
|
|
34
76
|
for (const file of commit.modified) {
|
|
35
|
-
|
|
77
|
+
addedOrModified.add(file);
|
|
78
|
+
deleted.delete(file);
|
|
36
79
|
}
|
|
37
80
|
for (const file of commit.removed) {
|
|
38
|
-
|
|
81
|
+
deleted.add(file);
|
|
82
|
+
addedOrModified.delete(file);
|
|
39
83
|
}
|
|
40
84
|
}
|
|
41
|
-
return
|
|
85
|
+
return {
|
|
86
|
+
addedOrModified: [...addedOrModified].sort((a, b) => a.localeCompare(b)),
|
|
87
|
+
deleted: [...deleted].sort((a, b) => a.localeCompare(b)),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
function listChangedFiles(commits) {
|
|
91
|
+
const { addedOrModified, deleted } = listChangedFilesFromCommits(commits);
|
|
92
|
+
return [...addedOrModified, ...deleted].sort((a, b) => a.localeCompare(b));
|
|
93
|
+
}
|
|
94
|
+
function assertReplacementSucceeded(kind, result) {
|
|
95
|
+
if (result.exitCode === 0) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
const detail = result.stderr.trim() || result.stdout.trim();
|
|
99
|
+
throw new WorkPushValidationError(detail.length > 0
|
|
100
|
+
? `Copado ${kind} replacement failed (exit ${result.exitCode ?? 'unknown'}): ${detail}`
|
|
101
|
+
: `Copado ${kind} replacement failed with exit code ${result.exitCode ?? 'unknown'}.`);
|
|
42
102
|
}
|
|
43
103
|
export function resolveWorkPushRetryDelaySeconds(retryAfterSeconds, waitedSeconds) {
|
|
44
104
|
if (waitedSeconds >= WORK_PUSH_RETRY_DOUBLE_AFTER_SECONDS) {
|
|
@@ -95,8 +155,87 @@ export async function registerHeadlessPush(pushBody, hooks, timeoutSeconds = DEF
|
|
|
95
155
|
}
|
|
96
156
|
}
|
|
97
157
|
}
|
|
158
|
+
async function applyCopadoReplacements(options) {
|
|
159
|
+
const { baseBranch, changedFiles, commits, devOrgBranch, hooks, tipCommitId, workItem, workItemId } = options;
|
|
160
|
+
const { display, log, withSpinner } = hooks;
|
|
161
|
+
const { addedOrModified } = listChangedFilesFromCommits(commits);
|
|
162
|
+
if (addedOrModified.length === 0) {
|
|
163
|
+
return { commits, tipCommitId };
|
|
164
|
+
}
|
|
165
|
+
display(() => {
|
|
166
|
+
log(`Added/modified files for replacements: ${addedOrModified.length}`);
|
|
167
|
+
});
|
|
168
|
+
const rules = await withSpinner('Fetching Copado replacements', () => fetchWorkPushReplacementRules(workItemId));
|
|
169
|
+
const environmentVariables = rules.environmentVariables ?? [];
|
|
170
|
+
const varReplacements = buildVarReplacementEntries(environmentVariables);
|
|
171
|
+
const replacementRulesYml = rules.replacementRulesYml?.trim() ?? '';
|
|
172
|
+
if (varReplacements.length === 0 && replacementRulesYml.length === 0) {
|
|
173
|
+
display(() => {
|
|
174
|
+
log('No Copado environment variables or yaml replacement rules linked to the work item.');
|
|
175
|
+
});
|
|
176
|
+
return { commits, tipCommitId };
|
|
177
|
+
}
|
|
178
|
+
const repoRoot = await getGitRepoRoot();
|
|
179
|
+
if (varReplacements.length > 0) {
|
|
180
|
+
const replacement = await withSpinner('Applying Copado environment variable replacements', () => runVarReplacement({
|
|
181
|
+
changedFiles: addedOrModified,
|
|
182
|
+
cwd: repoRoot,
|
|
183
|
+
environmentVariables,
|
|
184
|
+
targetFolder: repoRoot,
|
|
185
|
+
}));
|
|
186
|
+
assertReplacementSucceeded('environment variable', replacement);
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
display(() => {
|
|
190
|
+
log('No Copado environment variables linked to the work item.');
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
if (replacementRulesYml.length > 0) {
|
|
194
|
+
const replacement = await withSpinner('Applying Copado yaml replacements', () => runYamlReplacement({
|
|
195
|
+
branchName: devOrgBranch,
|
|
196
|
+
changedFiles,
|
|
197
|
+
cwd: repoRoot,
|
|
198
|
+
replacementRulesYml,
|
|
199
|
+
targetFolder: repoRoot,
|
|
200
|
+
}));
|
|
201
|
+
assertReplacementSucceeded('yaml', replacement);
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
display(() => {
|
|
205
|
+
log('No Copado yaml replacement rules linked to the work item pipeline.');
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
const pendingAfterReplacement = await listPendingChanges();
|
|
209
|
+
if (pendingAfterReplacement.length === 0) {
|
|
210
|
+
return { commits, tipCommitId };
|
|
211
|
+
}
|
|
212
|
+
const tipMessage = commits.at(-1)?.message;
|
|
213
|
+
if (tipMessage === undefined || tipMessage.length === 0) {
|
|
214
|
+
throw new WorkPushValidationError('Unable to determine tip commit message for replacement commit.');
|
|
215
|
+
}
|
|
216
|
+
// Env-var and yaml edits share one follow-up commit when either changed tracked files.
|
|
217
|
+
await withSpinner('Committing Copado replacements', () => commitAllTrackedChanges(`${tipMessage} copado replacements`));
|
|
218
|
+
const logStdout = await getCommitsNameStatus(baseBranch);
|
|
219
|
+
const refreshedCommits = parseNameStatusLog(logStdout, workItem);
|
|
220
|
+
const refreshedTipCommitId = refreshedCommits.at(-1)?.id;
|
|
221
|
+
if (refreshedTipCommitId === undefined) {
|
|
222
|
+
throw new WorkPushValidationError('Unable to determine tip commit id after Copado replacements.', {
|
|
223
|
+
exit: 1,
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
return { commits: refreshedCommits, tipCommitId: refreshedTipCommitId };
|
|
227
|
+
}
|
|
228
|
+
async function syncDevOrgBranch(options) {
|
|
229
|
+
const { branch, devOrgBranch, hooks } = options;
|
|
230
|
+
const { withSpinner } = hooks;
|
|
231
|
+
await withSpinner(`Checking out ${devOrgBranch}`, () => checkoutBranch(devOrgBranch));
|
|
232
|
+
await withSpinner(`Pulling origin/${devOrgBranch}`, () => pullOriginBranch(devOrgBranch));
|
|
233
|
+
await withSpinner(`Merging ${branch} into ${devOrgBranch}`, () => mergeTheirsIgnoreSpaceChange(branch));
|
|
234
|
+
await withSpinner(`Pushing origin/${devOrgBranch}`, () => pushOriginBranch(devOrgBranch));
|
|
235
|
+
await withSpinner(`Checking out ${branch}`, () => checkoutBranch(branch));
|
|
236
|
+
}
|
|
98
237
|
export async function performWorkPush(options) {
|
|
99
|
-
const { baseBranch, branch, hooks, workItem } = options;
|
|
238
|
+
const { baseBranch, branch, devOrgBranch, hooks, workItem } = options;
|
|
100
239
|
const { display, log, withSpinner } = hooks;
|
|
101
240
|
const logStdout = await withSpinner(`Collecting commits since ${baseBranch}`, () => getCommitsNameStatus(baseBranch));
|
|
102
241
|
const commits = parseNameStatusLog(logStdout, workItem);
|
|
@@ -121,35 +260,59 @@ export async function performWorkPush(options) {
|
|
|
121
260
|
}
|
|
122
261
|
}
|
|
123
262
|
});
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
263
|
+
let commitsToRegister = commits;
|
|
264
|
+
let tipCommitToLock = tipCommitId;
|
|
265
|
+
try {
|
|
266
|
+
const replacementResult = await applyCopadoReplacements({
|
|
267
|
+
baseBranch,
|
|
268
|
+
changedFiles,
|
|
269
|
+
commits,
|
|
270
|
+
devOrgBranch,
|
|
271
|
+
hooks: { display, log, withSpinner },
|
|
272
|
+
tipCommitId,
|
|
273
|
+
workItem,
|
|
274
|
+
workItemId: options.workItemId,
|
|
275
|
+
});
|
|
276
|
+
commitsToRegister = replacementResult.commits;
|
|
277
|
+
tipCommitToLock = replacementResult.tipCommitId;
|
|
278
|
+
const lockBody = {
|
|
279
|
+
commitId: tipCommitToLock,
|
|
280
|
+
workItem,
|
|
281
|
+
};
|
|
282
|
+
const lock = await withSpinner('Notifying headless push', () => CicdGateway.call({
|
|
283
|
+
body: lockBody,
|
|
284
|
+
endpoint: workPushLockEndpoint(),
|
|
285
|
+
headers: {
|
|
286
|
+
'Content-Type': 'application/json',
|
|
287
|
+
},
|
|
288
|
+
method: 'POST',
|
|
289
|
+
}));
|
|
290
|
+
await withSpinner(`Pushing origin/${branch}`, () => pushOriginBranch(branch));
|
|
291
|
+
const pushBody = { commits: commitsToRegister };
|
|
292
|
+
const timeoutSeconds = await readWorkPushRetryTimeoutSeconds();
|
|
293
|
+
const push = await withSpinner('Registering headless push', () => registerHeadlessPush(pushBody, { display, log }, timeoutSeconds));
|
|
294
|
+
display(() => {
|
|
295
|
+
log(formatWorkCommandMessage(push.message));
|
|
296
|
+
});
|
|
297
|
+
await syncDevOrgBranch({ branch, devOrgBranch, hooks });
|
|
298
|
+
return {
|
|
299
|
+
baseBranch,
|
|
300
|
+
branch,
|
|
301
|
+
commits: commitsToRegister,
|
|
302
|
+
devOrgBranch,
|
|
303
|
+
lock,
|
|
304
|
+
push,
|
|
305
|
+
workItem,
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
catch (error) {
|
|
309
|
+
try {
|
|
310
|
+
await restoreFeatureBranch(branch);
|
|
311
|
+
}
|
|
312
|
+
catch {
|
|
313
|
+
// Prefer the original failure; restoration is best-effort.
|
|
314
|
+
}
|
|
315
|
+
throw error;
|
|
316
|
+
}
|
|
154
317
|
}
|
|
155
318
|
//# sourceMappingURL=work-push.js.map
|