@copado/agentia-cli 0.15.0-alpha.0 → 0.17.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/README.md +148 -43
- package/dist/commands/auth/get.d.ts +2 -0
- package/dist/commands/auth/get.js +46 -8
- package/dist/commands/auth/get.js.map +1 -1
- package/dist/commands/work/done.d.ts +7 -0
- package/dist/commands/work/done.js +99 -0
- package/dist/commands/work/done.js.map +1 -0
- package/dist/commands/work/push.d.ts +7 -0
- package/dist/commands/work/push.js +67 -0
- package/dist/commands/work/push.js.map +1 -0
- package/dist/commands/work/set.d.ts +13 -0
- package/dist/commands/work/set.js +98 -0
- package/dist/commands/work/set.js.map +1 -0
- package/dist/commands/work/submit.d.ts +7 -0
- package/dist/commands/work/submit.js +99 -0
- package/dist/commands/work/submit.js.map +1 -0
- package/dist/lib/cicd-gateway.d.ts +9 -1
- package/dist/lib/cicd-gateway.js +91 -9
- package/dist/lib/cicd-gateway.js.map +1 -1
- package/dist/lib/defaults-store.d.ts +7 -0
- package/dist/lib/defaults-store.js +25 -0
- package/dist/lib/defaults-store.js.map +1 -1
- package/dist/lib/git-worktree.d.ts +22 -0
- package/dist/lib/git-worktree.js +75 -0
- package/dist/lib/git-worktree.js.map +1 -0
- package/dist/lib/job-types.js +7 -7
- package/dist/lib/job-types.js.map +1 -1
- package/dist/lib/work-push.d.ts +20 -0
- package/dist/lib/work-push.js +92 -0
- package/dist/lib/work-push.js.map +1 -0
- package/dist/lib/work-types.d.ts +72 -0
- package/dist/lib/work-types.js +107 -0
- package/dist/lib/work-types.js.map +1 -1
- package/oclif.manifest.json +147 -1
- package/package.json +1 -1
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { AgentiaCommand } from '../../lib/agentia-command.js';
|
|
2
|
+
import { CicdGateway } from '../../lib/cicd-gateway.js';
|
|
3
|
+
import { readLastBaseBranch } from '../../lib/defaults-store.js';
|
|
4
|
+
import { getCurrentBranch, hasUnpushedCommits, listPendingChanges } from '../../lib/git-worktree.js';
|
|
5
|
+
import { performWorkPush, WorkPushValidationError } from '../../lib/work-push.js';
|
|
6
|
+
import { extractUserStoryFromFeatureBranch, workDoneEndpoint, } from '../../lib/work-types.js';
|
|
7
|
+
export default class WorkDone extends AgentiaCommand {
|
|
8
|
+
static description = [
|
|
9
|
+
'Mark the current feature/US-XXXX work item done and promote it to the next environment.',
|
|
10
|
+
'Pushes and registers commits when the branch is ahead of origin, then calls headless work/done',
|
|
11
|
+
'(Promote Action with validate=false).',
|
|
12
|
+
].join(' ');
|
|
13
|
+
static examples = ['<%= config.bin %> <%= command.id %>', '<%= config.bin %> <%= command.id %> --json'];
|
|
14
|
+
async run() {
|
|
15
|
+
await this.parse(WorkDone);
|
|
16
|
+
const branch = await getCurrentBranch();
|
|
17
|
+
const workItem = extractUserStoryFromFeatureBranch(branch);
|
|
18
|
+
if (workItem === undefined) {
|
|
19
|
+
this.error(`Current branch '${branch}' is not a feature/US-XXXX branch.`, {
|
|
20
|
+
exit: 1,
|
|
21
|
+
suggestions: ['Checkout a feature/US-XXXX branch created by `agentia work set`.'],
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
const pendingFiles = await listPendingChanges();
|
|
25
|
+
if (pendingFiles.length > 0) {
|
|
26
|
+
this.error([
|
|
27
|
+
'Uncommitted tracked changes present. Commit them, then try again:',
|
|
28
|
+
...pendingFiles.map((file) => ` ${file}`),
|
|
29
|
+
].join('\n'), {
|
|
30
|
+
exit: 2,
|
|
31
|
+
suggestions: ['git add -A && git commit', `${this.config.bin} work done`],
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
this.display(() => {
|
|
35
|
+
this.log(`Branch: ${branch}`);
|
|
36
|
+
this.log(`Work item: ${workItem}`);
|
|
37
|
+
});
|
|
38
|
+
let pushResult;
|
|
39
|
+
const needsPush = await hasUnpushedCommits(branch);
|
|
40
|
+
if (needsPush) {
|
|
41
|
+
const baseBranch = (await readLastBaseBranch())?.trim();
|
|
42
|
+
if (baseBranch === undefined || baseBranch.length === 0) {
|
|
43
|
+
this.error('No base branch recorded. Run `agentia work set <work-item>` first.', {
|
|
44
|
+
exit: 1,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
this.display(() => {
|
|
48
|
+
this.log(`Base branch: ${baseBranch}`);
|
|
49
|
+
this.log('Unpushed commits detected; pushing before done.');
|
|
50
|
+
});
|
|
51
|
+
try {
|
|
52
|
+
pushResult = await performWorkPush({
|
|
53
|
+
baseBranch,
|
|
54
|
+
branch,
|
|
55
|
+
hooks: {
|
|
56
|
+
display: (render) => this.display(render),
|
|
57
|
+
log: (message) => this.log(message),
|
|
58
|
+
withSpinner: (message, action) => this.withSpinner(message, action),
|
|
59
|
+
},
|
|
60
|
+
workItem,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
if (error instanceof WorkPushValidationError) {
|
|
65
|
+
this.error(error.message, {
|
|
66
|
+
exit: error.exit,
|
|
67
|
+
suggestions: error.suggestions,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
throw error;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
this.display(() => {
|
|
75
|
+
this.log('Branch is up to date with origin; skipping push.');
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
const doneBody = { workItem };
|
|
79
|
+
const done = await this.withSpinner('Marking work item done', () => CicdGateway.call({
|
|
80
|
+
body: doneBody,
|
|
81
|
+
endpoint: workDoneEndpoint(),
|
|
82
|
+
headers: {
|
|
83
|
+
'Content-Type': 'application/json',
|
|
84
|
+
},
|
|
85
|
+
method: 'POST',
|
|
86
|
+
}));
|
|
87
|
+
this.display(() => {
|
|
88
|
+
this.log(done.message);
|
|
89
|
+
});
|
|
90
|
+
return {
|
|
91
|
+
branch,
|
|
92
|
+
done,
|
|
93
|
+
push: pushResult,
|
|
94
|
+
pushed: needsPush,
|
|
95
|
+
workItem,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=done.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"done.js","sourceRoot":"","sources":["../../../src/commands/work/done.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { AgentiaCommand } from '../../lib/agentia-command.js';
|
|
2
|
+
import { type WorkPushResult } from '../../lib/work-types.js';
|
|
3
|
+
export default class WorkPush extends AgentiaCommand {
|
|
4
|
+
static description: string;
|
|
5
|
+
static examples: string[];
|
|
6
|
+
run(): Promise<WorkPushResult>;
|
|
7
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { AgentiaCommand } from '../../lib/agentia-command.js';
|
|
2
|
+
import { readLastBaseBranch } from '../../lib/defaults-store.js';
|
|
3
|
+
import { getCurrentBranch, listPendingChanges } from '../../lib/git-worktree.js';
|
|
4
|
+
import { performWorkPush, WorkPushValidationError } from '../../lib/work-push.js';
|
|
5
|
+
import { extractUserStoryFromFeatureBranch } from '../../lib/work-types.js';
|
|
6
|
+
export default class WorkPush extends AgentiaCommand {
|
|
7
|
+
static description = [
|
|
8
|
+
'Push the current feature/US-XXXX branch and register commits with Copado.',
|
|
9
|
+
'Requires a clean working tree and a base branch from `agentia work set`.',
|
|
10
|
+
'Locks the tip commit, runs `git push origin <branch>`, then calls headless work/push.',
|
|
11
|
+
].join(' ');
|
|
12
|
+
static examples = ['<%= config.bin %> <%= command.id %>', '<%= config.bin %> <%= command.id %> --json'];
|
|
13
|
+
async run() {
|
|
14
|
+
await this.parse(WorkPush);
|
|
15
|
+
const branch = await getCurrentBranch();
|
|
16
|
+
const workItem = extractUserStoryFromFeatureBranch(branch);
|
|
17
|
+
if (workItem === undefined) {
|
|
18
|
+
this.error(`Current branch '${branch}' is not a feature/US-XXXX branch.`, {
|
|
19
|
+
exit: 1,
|
|
20
|
+
suggestions: ['Checkout a feature/US-XXXX branch created by `agentia work set`.'],
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
const pendingFiles = await listPendingChanges();
|
|
24
|
+
if (pendingFiles.length > 0) {
|
|
25
|
+
this.error([
|
|
26
|
+
'Uncommitted tracked changes present. Commit them, then try again:',
|
|
27
|
+
...pendingFiles.map((file) => ` ${file}`),
|
|
28
|
+
].join('\n'), {
|
|
29
|
+
exit: 2,
|
|
30
|
+
suggestions: ['git add -A && git commit', `${this.config.bin} work push`],
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
const baseBranch = (await readLastBaseBranch())?.trim();
|
|
34
|
+
if (baseBranch === undefined || baseBranch.length === 0) {
|
|
35
|
+
this.error('No base branch recorded. Run `agentia work set <work-item>` first.', {
|
|
36
|
+
exit: 1,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
this.display(() => {
|
|
40
|
+
this.log(`Branch: ${branch}`);
|
|
41
|
+
this.log(`Work item: ${workItem}`);
|
|
42
|
+
this.log(`Base branch: ${baseBranch}`);
|
|
43
|
+
});
|
|
44
|
+
try {
|
|
45
|
+
return await performWorkPush({
|
|
46
|
+
baseBranch,
|
|
47
|
+
branch,
|
|
48
|
+
hooks: {
|
|
49
|
+
display: (render) => this.display(render),
|
|
50
|
+
log: (message) => this.log(message),
|
|
51
|
+
withSpinner: (message, action) => this.withSpinner(message, action),
|
|
52
|
+
},
|
|
53
|
+
workItem,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
if (error instanceof WorkPushValidationError) {
|
|
58
|
+
this.error(error.message, {
|
|
59
|
+
exit: error.exit,
|
|
60
|
+
suggestions: error.suggestions,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
throw error;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=push.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"push.js","sourceRoot":"","sources":["../../../src/commands/work/push.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { AgentiaCommand } from '../../lib/agentia-command.js';
|
|
2
|
+
import { type WorkSetResult } from '../../lib/work-types.js';
|
|
3
|
+
export default class WorkSet extends AgentiaCommand {
|
|
4
|
+
static args: {
|
|
5
|
+
id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
6
|
+
};
|
|
7
|
+
static description: string;
|
|
8
|
+
static examples: string[];
|
|
9
|
+
static flags: {
|
|
10
|
+
'base-branch': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
};
|
|
12
|
+
run(): Promise<WorkSetResult>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { Args, Flags } from '@oclif/core';
|
|
2
|
+
import { AgentiaCommand } from '../../lib/agentia-command.js';
|
|
3
|
+
import { CicdGateway } from '../../lib/cicd-gateway.js';
|
|
4
|
+
import { writeLastWorkSelection } from '../../lib/defaults-store.js';
|
|
5
|
+
import { checkoutFeatureBranch, fetchOriginBranch, listPendingChanges } from '../../lib/git-worktree.js';
|
|
6
|
+
import { featureBranchName, workSetEndpoint, } from '../../lib/work-types.js';
|
|
7
|
+
export default class WorkSet extends AgentiaCommand {
|
|
8
|
+
static args = {
|
|
9
|
+
id: Args.string({
|
|
10
|
+
description: 'User story ID or name',
|
|
11
|
+
required: true,
|
|
12
|
+
}),
|
|
13
|
+
};
|
|
14
|
+
static description = [
|
|
15
|
+
'Set the active work item and prepare a local feature branch.',
|
|
16
|
+
'Calls the CICD gateway work/set endpoint, requires a clean tracked working tree,',
|
|
17
|
+
'fetches the work item base branch from origin, then creates or checks out',
|
|
18
|
+
'feature/<user-story-name>.',
|
|
19
|
+
].join(' ');
|
|
20
|
+
static examples = [
|
|
21
|
+
'<%= config.bin %> <%= command.id %> a0SKC000000CmZ2AU',
|
|
22
|
+
'<%= config.bin %> <%= command.id %> US-0001 --json',
|
|
23
|
+
'<%= config.bin %> <%= command.id %> US-0001 --base-branch develop',
|
|
24
|
+
];
|
|
25
|
+
static flags = {
|
|
26
|
+
'base-branch': Flags.string({
|
|
27
|
+
description: 'Override the base branch returned by the work/set endpoint',
|
|
28
|
+
}),
|
|
29
|
+
};
|
|
30
|
+
async run() {
|
|
31
|
+
const { args, flags } = await this.parse(WorkSet);
|
|
32
|
+
const setResponse = await this.withSpinner('Setting work item', () => CicdGateway.call({
|
|
33
|
+
body: { workItem: args.id },
|
|
34
|
+
endpoint: workSetEndpoint(),
|
|
35
|
+
headers: {
|
|
36
|
+
'Content-Type': 'application/json',
|
|
37
|
+
},
|
|
38
|
+
method: 'POST',
|
|
39
|
+
}));
|
|
40
|
+
this.display(() => {
|
|
41
|
+
this.log(setResponse.message);
|
|
42
|
+
});
|
|
43
|
+
const overrideBaseBranch = flags['base-branch']?.trim();
|
|
44
|
+
const baseBranch = (overrideBaseBranch !== undefined && overrideBaseBranch.length > 0
|
|
45
|
+
? overrideBaseBranch
|
|
46
|
+
: setResponse.baseBranch?.trim());
|
|
47
|
+
if (baseBranch === undefined || baseBranch.length === 0) {
|
|
48
|
+
this.error('Work set response did not include a base branch.', { exit: 1 });
|
|
49
|
+
}
|
|
50
|
+
const userStoryName = setResponse.workItem.name?.trim();
|
|
51
|
+
if (userStoryName === undefined || userStoryName.length === 0) {
|
|
52
|
+
this.error('Work set response did not include a user story name for the feature branch.', { exit: 1 });
|
|
53
|
+
}
|
|
54
|
+
const branch = featureBranchName(userStoryName);
|
|
55
|
+
const pendingFiles = await listPendingChanges();
|
|
56
|
+
if (pendingFiles.length > 0) {
|
|
57
|
+
this.error([
|
|
58
|
+
'Pending tracked changes present. Stash or commit them, then try again:',
|
|
59
|
+
...pendingFiles.map((file) => ` ${file}`),
|
|
60
|
+
].join('\n'), {
|
|
61
|
+
exit: 2,
|
|
62
|
+
suggestions: [`git stash push && ${this.config.bin} work set ${args.id}`],
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
this.display(() => {
|
|
66
|
+
this.log('Working tree is clean.');
|
|
67
|
+
});
|
|
68
|
+
await this.withSpinner(`Fetching origin/${baseBranch}`, () => fetchOriginBranch(baseBranch));
|
|
69
|
+
this.display(() => {
|
|
70
|
+
this.log(`Fetched origin/${baseBranch}.`);
|
|
71
|
+
});
|
|
72
|
+
const checkout = await this.withSpinner(`Checking out ${branch}`, () => checkoutFeatureBranch(branch, baseBranch));
|
|
73
|
+
this.display(() => {
|
|
74
|
+
if (checkout.created) {
|
|
75
|
+
this.log(`Created and checked out ${checkout.branch} from ${baseBranch}.`);
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
this.log(`Checked out existing branch ${checkout.branch}.`);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
await writeLastWorkSelection({
|
|
82
|
+
lastBaseBranch: baseBranch,
|
|
83
|
+
lastWorkItem: userStoryName,
|
|
84
|
+
});
|
|
85
|
+
return {
|
|
86
|
+
baseBranch,
|
|
87
|
+
command: setResponse.command,
|
|
88
|
+
git: {
|
|
89
|
+
branch: checkout.branch,
|
|
90
|
+
branchCreated: checkout.created,
|
|
91
|
+
fetched: true,
|
|
92
|
+
},
|
|
93
|
+
message: setResponse.message,
|
|
94
|
+
workItem: setResponse.workItem,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=set.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"set.js","sourceRoot":"","sources":["../../../src/commands/work/set.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { AgentiaCommand } from '../../lib/agentia-command.js';
|
|
2
|
+
import { type WorkSubmitResult } from '../../lib/work-types.js';
|
|
3
|
+
export default class WorkSubmit extends AgentiaCommand {
|
|
4
|
+
static description: string;
|
|
5
|
+
static examples: string[];
|
|
6
|
+
run(): Promise<WorkSubmitResult>;
|
|
7
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { AgentiaCommand } from '../../lib/agentia-command.js';
|
|
2
|
+
import { CicdGateway } from '../../lib/cicd-gateway.js';
|
|
3
|
+
import { readLastBaseBranch } from '../../lib/defaults-store.js';
|
|
4
|
+
import { getCurrentBranch, hasUnpushedCommits, listPendingChanges } from '../../lib/git-worktree.js';
|
|
5
|
+
import { performWorkPush, WorkPushValidationError } from '../../lib/work-push.js';
|
|
6
|
+
import { extractUserStoryFromFeatureBranch, workSubmitEndpoint, } from '../../lib/work-types.js';
|
|
7
|
+
export default class WorkSubmit extends AgentiaCommand {
|
|
8
|
+
static description = [
|
|
9
|
+
'Submit the current feature/US-XXXX work item for pipeline quality gates and validation.',
|
|
10
|
+
'Pushes and registers commits when the branch is ahead of origin, then calls headless work/submit',
|
|
11
|
+
'(Promote Action with validate=true).',
|
|
12
|
+
].join(' ');
|
|
13
|
+
static examples = ['<%= config.bin %> <%= command.id %>', '<%= config.bin %> <%= command.id %> --json'];
|
|
14
|
+
async run() {
|
|
15
|
+
await this.parse(WorkSubmit);
|
|
16
|
+
const branch = await getCurrentBranch();
|
|
17
|
+
const workItem = extractUserStoryFromFeatureBranch(branch);
|
|
18
|
+
if (workItem === undefined) {
|
|
19
|
+
this.error(`Current branch '${branch}' is not a feature/US-XXXX branch.`, {
|
|
20
|
+
exit: 1,
|
|
21
|
+
suggestions: ['Checkout a feature/US-XXXX branch created by `agentia work set`.'],
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
const pendingFiles = await listPendingChanges();
|
|
25
|
+
if (pendingFiles.length > 0) {
|
|
26
|
+
this.error([
|
|
27
|
+
'Uncommitted tracked changes present. Commit them, then try again:',
|
|
28
|
+
...pendingFiles.map((file) => ` ${file}`),
|
|
29
|
+
].join('\n'), {
|
|
30
|
+
exit: 2,
|
|
31
|
+
suggestions: ['git add -A && git commit', `${this.config.bin} work submit`],
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
this.display(() => {
|
|
35
|
+
this.log(`Branch: ${branch}`);
|
|
36
|
+
this.log(`Work item: ${workItem}`);
|
|
37
|
+
});
|
|
38
|
+
let pushResult;
|
|
39
|
+
const needsPush = await hasUnpushedCommits(branch);
|
|
40
|
+
if (needsPush) {
|
|
41
|
+
const baseBranch = (await readLastBaseBranch())?.trim();
|
|
42
|
+
if (baseBranch === undefined || baseBranch.length === 0) {
|
|
43
|
+
this.error('No base branch recorded. Run `agentia work set <work-item>` first.', {
|
|
44
|
+
exit: 1,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
this.display(() => {
|
|
48
|
+
this.log(`Base branch: ${baseBranch}`);
|
|
49
|
+
this.log('Unpushed commits detected; pushing before submit.');
|
|
50
|
+
});
|
|
51
|
+
try {
|
|
52
|
+
pushResult = await performWorkPush({
|
|
53
|
+
baseBranch,
|
|
54
|
+
branch,
|
|
55
|
+
hooks: {
|
|
56
|
+
display: (render) => this.display(render),
|
|
57
|
+
log: (message) => this.log(message),
|
|
58
|
+
withSpinner: (message, action) => this.withSpinner(message, action),
|
|
59
|
+
},
|
|
60
|
+
workItem,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
if (error instanceof WorkPushValidationError) {
|
|
65
|
+
this.error(error.message, {
|
|
66
|
+
exit: error.exit,
|
|
67
|
+
suggestions: error.suggestions,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
throw error;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
this.display(() => {
|
|
75
|
+
this.log('Branch is up to date with origin; skipping push.');
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
const submitBody = { workItem };
|
|
79
|
+
const submit = await this.withSpinner('Submitting work item', () => CicdGateway.call({
|
|
80
|
+
body: submitBody,
|
|
81
|
+
endpoint: workSubmitEndpoint(),
|
|
82
|
+
headers: {
|
|
83
|
+
'Content-Type': 'application/json',
|
|
84
|
+
},
|
|
85
|
+
method: 'POST',
|
|
86
|
+
}));
|
|
87
|
+
this.display(() => {
|
|
88
|
+
this.log(submit.message);
|
|
89
|
+
});
|
|
90
|
+
return {
|
|
91
|
+
branch,
|
|
92
|
+
push: pushResult,
|
|
93
|
+
pushed: needsPush,
|
|
94
|
+
submit,
|
|
95
|
+
workItem,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=submit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"submit.js","sourceRoot":"","sources":["../../../src/commands/work/submit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,cAAc,EAAC,MAAM,8BAA8B,CAAA;AAC3D,OAAO,EAAC,WAAW,EAAC,MAAM,2BAA2B,CAAA;AACrD,OAAO,EAAC,kBAAkB,EAAC,MAAM,6BAA6B,CAAA;AAC9D,OAAO,EAAC,gBAAgB,EAAE,kBAAkB,EAAE,kBAAkB,EAAC,MAAM,2BAA2B,CAAA;AAClG,OAAO,EAAC,eAAe,EAAE,uBAAuB,EAAC,MAAM,wBAAwB,CAAA;AAC/E,OAAO,EACL,iCAAiC,EAGjC,kBAAkB,GAEnB,MAAM,yBAAyB,CAAA;AAEhC,MAAM,CAAC,OAAO,OAAO,UAAW,SAAQ,cAAc;IACpD,MAAM,CAAU,WAAW,GAAG;QAC5B,yFAAyF;QACzF,kGAAkG;QAClG,sCAAsC;KACvC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IACX,MAAM,CAAU,QAAQ,GAAG,CAAC,qCAAqC,EAAE,4CAA4C,CAAC,CAAA;IAEzG,KAAK,CAAC,GAAG;QACd,MAAM,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAA;QAE5B,MAAM,MAAM,GAAG,MAAM,gBAAgB,EAAE,CAAA;QACvC,MAAM,QAAQ,GAAG,iCAAiC,CAAC,MAAM,CAAC,CAAA;QAC1D,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,IAAI,CAAC,KAAK,CAAC,mBAAmB,MAAM,oCAAoC,EAAE;gBACxE,IAAI,EAAE,CAAC;gBACP,WAAW,EAAE,CAAC,kEAAkE,CAAC;aAClF,CAAC,CAAA;QACJ,CAAC;QAED,MAAM,YAAY,GAAG,MAAM,kBAAkB,EAAE,CAAA;QAC/C,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC5B,IAAI,CAAC,KAAK,CACR;gBACE,mEAAmE;gBACnE,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC;aAC3C,CAAC,IAAI,CAAC,IAAI,CAAC,EACZ;gBACE,IAAI,EAAE,CAAC;gBACP,WAAW,EAAE,CAAC,0BAA0B,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,cAAc,CAAC;aAC5E,CACF,CAAA;QACH,CAAC;QAED,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE;YAChB,IAAI,CAAC,GAAG,CAAC,WAAW,MAAM,EAAE,CAAC,CAAA;YAC7B,IAAI,CAAC,GAAG,CAAC,cAAc,QAAQ,EAAE,CAAC,CAAA;QACpC,CAAC,CAAC,CAAA;QAEF,IAAI,UAAoC,CAAA;QACxC,MAAM,SAAS,GAAG,MAAM,kBAAkB,CAAC,MAAM,CAAC,CAAA;QAElD,IAAI,SAAS,EAAE,CAAC;YACd,MAAM,UAAU,GAAG,CAAC,MAAM,kBAAkB,EAAE,CAAC,EAAE,IAAI,EAAE,CAAA;YACvD,IAAI,UAAU,KAAK,SAAS,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACxD,IAAI,CAAC,KAAK,CAAC,oEAAoE,EAAE;oBAC/E,IAAI,EAAE,CAAC;iBACR,CAAC,CAAA;YACJ,CAAC;YAED,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE;gBAChB,IAAI,CAAC,GAAG,CAAC,gBAAgB,UAAU,EAAE,CAAC,CAAA;gBACtC,IAAI,CAAC,GAAG,CAAC,mDAAmD,CAAC,CAAA;YAC/D,CAAC,CAAC,CAAA;YAEF,IAAI,CAAC;gBACH,UAAU,GAAG,MAAM,eAAe,CAAC;oBACjC,UAAU;oBACV,MAAM;oBACN,KAAK,EAAE;wBACL,OAAO,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC;wBACzC,GAAG,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC;wBACnC,WAAW,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,MAAM,CAAC;qBACpE;oBACD,QAAQ;iBACT,CAAC,CAAA;YACJ,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,KAAK,YAAY,uBAAuB,EAAE,CAAC;oBAC7C,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE;wBACxB,IAAI,EAAE,KAAK,CAAC,IAAI;wBAChB,WAAW,EAAE,KAAK,CAAC,WAAW;qBAC/B,CAAC,CAAA;gBACJ,CAAC;gBAED,MAAM,KAAK,CAAA;YACb,CAAC;QACH,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE;gBAChB,IAAI,CAAC,GAAG,CAAC,kDAAkD,CAAC,CAAA;YAC9D,CAAC,CAAC,CAAA;QACJ,CAAC;QAED,MAAM,UAAU,GAA4B,EAAC,QAAQ,EAAC,CAAA;QACtD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,sBAAsB,EAAE,GAAG,EAAE,CACjE,WAAW,CAAC,IAAI,CAAwB;YACtC,IAAI,EAAE,UAAU;YAChB,QAAQ,EAAE,kBAAkB,EAAE;YAC9B,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;aACnC;YACD,MAAM,EAAE,MAAM;SACf,CAAC,CACH,CAAA;QAED,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE;YAChB,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;QAC1B,CAAC,CAAC,CAAA;QAEF,OAAO;YACL,MAAM;YACN,IAAI,EAAE,UAAU;YAChB,MAAM,EAAE,SAAS;YACjB,MAAM;YACN,QAAQ;SACT,CAAA;IACH,CAAC"}
|
|
@@ -44,13 +44,20 @@ export type CicdEndpointOptions = {
|
|
|
44
44
|
region?: CicdRegion;
|
|
45
45
|
};
|
|
46
46
|
export type HttpRequestFn = (url: URL, method: string, headers: Record<string, string>, body: unknown) => Promise<string>;
|
|
47
|
+
export type CicdGatewayErrorRequest = {
|
|
48
|
+
method?: string;
|
|
49
|
+
url?: string;
|
|
50
|
+
};
|
|
47
51
|
export declare class CicdGatewayError extends Error {
|
|
48
52
|
readonly categories: string[];
|
|
49
53
|
readonly code?: string;
|
|
54
|
+
readonly requestMethod?: string;
|
|
55
|
+
readonly requestUrl?: string;
|
|
50
56
|
readonly statusCode?: number;
|
|
51
57
|
constructor(message: string, options?: {
|
|
52
58
|
categories?: string[];
|
|
53
59
|
code?: string;
|
|
60
|
+
request?: CicdGatewayErrorRequest;
|
|
54
61
|
statusCode?: number;
|
|
55
62
|
});
|
|
56
63
|
}
|
|
@@ -58,7 +65,8 @@ export declare function gatewayUserAgent(): string;
|
|
|
58
65
|
export declare function createTransactionId(): string;
|
|
59
66
|
export declare function normalizeCicdBaseUrl(value: string): string;
|
|
60
67
|
export declare function sanitizeCicdErrorMessage(message: string): string;
|
|
61
|
-
export declare function
|
|
68
|
+
export declare function maskUrlSecrets(url: string | URL): string;
|
|
69
|
+
export declare function formatCicdGatewayError(status: number, raw: string, request?: CicdGatewayErrorRequest): Error;
|
|
62
70
|
export declare function cicdApiUrl(serverUrl: string, apiKey: string): URL;
|
|
63
71
|
export declare function maskedCicdApiUrl(serverUrl: string): string;
|
|
64
72
|
export declare function discoverCicdBaseUrl(apiKey: string): Promise<string>;
|
package/dist/lib/cicd-gateway.js
CHANGED
|
@@ -15,12 +15,19 @@ let activeHttpRequestOptions = {};
|
|
|
15
15
|
export class CicdGatewayError extends Error {
|
|
16
16
|
categories;
|
|
17
17
|
code;
|
|
18
|
+
requestMethod;
|
|
19
|
+
requestUrl;
|
|
18
20
|
statusCode;
|
|
19
21
|
constructor(message, options = {}) {
|
|
20
22
|
super(message);
|
|
21
23
|
this.name = 'CicdGatewayError';
|
|
22
24
|
this.categories = options.categories ?? [];
|
|
23
25
|
this.code = options.code;
|
|
26
|
+
this.requestMethod = options.request?.method;
|
|
27
|
+
this.requestUrl =
|
|
28
|
+
options.request?.url !== undefined && options.request.url.trim().length > 0
|
|
29
|
+
? maskUrlSecrets(options.request.url)
|
|
30
|
+
: undefined;
|
|
24
31
|
this.statusCode = options.statusCode;
|
|
25
32
|
}
|
|
26
33
|
}
|
|
@@ -77,15 +84,44 @@ export function sanitizeCicdErrorMessage(message) {
|
|
|
77
84
|
}
|
|
78
85
|
return message.trim();
|
|
79
86
|
}
|
|
80
|
-
export function
|
|
87
|
+
export function maskUrlSecrets(url) {
|
|
88
|
+
let parsed;
|
|
89
|
+
try {
|
|
90
|
+
parsed = typeof url === 'string' ? new URL(url) : new URL(url.toString());
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
return typeof url === 'string' ? url : url.toString();
|
|
94
|
+
}
|
|
95
|
+
if (parsed.searchParams.has('api_key')) {
|
|
96
|
+
parsed.searchParams.set('api_key', '<masked>');
|
|
97
|
+
}
|
|
98
|
+
return parsed.toString();
|
|
99
|
+
}
|
|
100
|
+
function formatRequestSuffix(request) {
|
|
101
|
+
if (request?.url === undefined || request.url.trim().length === 0) {
|
|
102
|
+
return '';
|
|
103
|
+
}
|
|
104
|
+
const url = maskUrlSecrets(request.url);
|
|
105
|
+
const method = request.method !== undefined && request.method.trim().length > 0 ? `${request.method} ` : '';
|
|
106
|
+
return ` Request: ${method}${url}`;
|
|
107
|
+
}
|
|
108
|
+
function withRequestSuffix(message, request) {
|
|
109
|
+
const suffix = formatRequestSuffix(request);
|
|
110
|
+
if (suffix.length === 0) {
|
|
111
|
+
return message;
|
|
112
|
+
}
|
|
113
|
+
return message.endsWith('.') ? `${message}${suffix}` : `${message}.${suffix}`;
|
|
114
|
+
}
|
|
115
|
+
export function formatCicdGatewayError(status, raw, request) {
|
|
81
116
|
try {
|
|
82
117
|
const parsed = JSON.parse(raw);
|
|
83
118
|
const detailMessage = extractCicdErrorDetailMessage(parsed.data?.details);
|
|
84
119
|
const message = detailMessage ?? parsed.message;
|
|
85
120
|
if (message !== undefined && message.trim().length > 0) {
|
|
86
|
-
return new CicdGatewayError(sanitizeCicdErrorMessage(message), {
|
|
121
|
+
return new CicdGatewayError(withRequestSuffix(sanitizeCicdErrorMessage(message), request), {
|
|
87
122
|
categories: extractCicdErrorCategories(parsed.data?.details),
|
|
88
123
|
code: parsed.data?.code,
|
|
124
|
+
request,
|
|
89
125
|
statusCode: status,
|
|
90
126
|
});
|
|
91
127
|
}
|
|
@@ -93,20 +129,25 @@ export function formatCicdGatewayError(status, raw) {
|
|
|
93
129
|
catch {
|
|
94
130
|
// Fall through to the generic error below.
|
|
95
131
|
}
|
|
96
|
-
return new CicdGatewayError(`CICD Gateway request failed (${status}).`, {
|
|
132
|
+
return new CicdGatewayError(withRequestSuffix(`CICD Gateway request failed (${status}).`, request), {
|
|
133
|
+
request,
|
|
134
|
+
statusCode: status,
|
|
135
|
+
});
|
|
97
136
|
}
|
|
98
|
-
function createCicdGatewayError(parsed) {
|
|
137
|
+
function createCicdGatewayError(parsed, request) {
|
|
99
138
|
const detailMessage = extractCicdErrorDetailMessage(parsed.data?.details);
|
|
100
139
|
const message = detailMessage ?? parsed.message;
|
|
101
140
|
if (message !== undefined && message.trim().length > 0) {
|
|
102
|
-
return new CicdGatewayError(sanitizeCicdErrorMessage(message), {
|
|
141
|
+
return new CicdGatewayError(withRequestSuffix(sanitizeCicdErrorMessage(message), request), {
|
|
103
142
|
categories: extractCicdErrorCategories(parsed.data?.details),
|
|
104
143
|
code: parsed.data?.code,
|
|
144
|
+
request,
|
|
105
145
|
});
|
|
106
146
|
}
|
|
107
|
-
return new CicdGatewayError('CICD Gateway request failed.', {
|
|
147
|
+
return new CicdGatewayError(withRequestSuffix('CICD Gateway request failed.', request), {
|
|
108
148
|
categories: extractCicdErrorCategories(parsed.data?.details),
|
|
109
149
|
code: parsed.data?.code,
|
|
150
|
+
request,
|
|
110
151
|
});
|
|
111
152
|
}
|
|
112
153
|
function buildOuterHeaders(transactionId) {
|
|
@@ -229,6 +270,7 @@ function unwrapResponse(parsed) {
|
|
|
229
270
|
return data;
|
|
230
271
|
}
|
|
231
272
|
async function executeRequest(credentials, request, onDebugRequest, options = {}) {
|
|
273
|
+
const errorRequest = buildErrorRequestInfo(credentials, request, options);
|
|
232
274
|
const raw = await sendRequest(credentials, request, onDebugRequest, options);
|
|
233
275
|
if (raw.trim().length === 0) {
|
|
234
276
|
return undefined;
|
|
@@ -237,7 +279,7 @@ async function executeRequest(credentials, request, onDebugRequest, options = {}
|
|
|
237
279
|
try {
|
|
238
280
|
const possibleError = JSON.parse(raw);
|
|
239
281
|
if (possibleError.status === 'error') {
|
|
240
|
-
throw createCicdGatewayError(possibleError);
|
|
282
|
+
throw createCicdGatewayError(possibleError, errorRequest);
|
|
241
283
|
}
|
|
242
284
|
}
|
|
243
285
|
catch (error) {
|
|
@@ -255,16 +297,32 @@ async function executeRequest(credentials, request, onDebugRequest, options = {}
|
|
|
255
297
|
throw new Error(`CICD Gateway returned non-JSON response: ${raw}`);
|
|
256
298
|
}
|
|
257
299
|
if (parsed.status === 'error') {
|
|
258
|
-
throw createCicdGatewayError(parsed);
|
|
300
|
+
throw createCicdGatewayError(parsed, errorRequest);
|
|
259
301
|
}
|
|
260
302
|
return unwrapResponse(parsed);
|
|
261
303
|
}
|
|
304
|
+
function buildErrorRequestInfo(credentials, request, options = {}) {
|
|
305
|
+
if (!options.forceRemote && readLocalGatewayConfig().enabled) {
|
|
306
|
+
return {
|
|
307
|
+
method: request.method,
|
|
308
|
+
url: maskUrlSecrets(new URL(request.endpoint, `${credentials.baseUrl}/`)),
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
return {
|
|
312
|
+
method: request.method,
|
|
313
|
+
url: `${request.endpoint} via ${maskedCicdApiUrl(credentials.baseUrl)}`,
|
|
314
|
+
};
|
|
315
|
+
}
|
|
262
316
|
async function sendRemoteRequest(credentials, request, onDebugRequest) {
|
|
263
317
|
const endpoint = cicdApiUrl(credentials.baseUrl, credentials.apiKey);
|
|
264
318
|
const transactionId = createTransactionId();
|
|
265
319
|
const outerHeaders = buildOuterHeaders(transactionId);
|
|
266
320
|
const gatewayRequest = { ...request };
|
|
267
321
|
delete gatewayRequest.responseType;
|
|
322
|
+
const errorRequest = {
|
|
323
|
+
method: request.method,
|
|
324
|
+
url: `${request.endpoint} via ${maskedCicdApiUrl(credentials.baseUrl)}`,
|
|
325
|
+
};
|
|
268
326
|
if (onDebugRequest) {
|
|
269
327
|
onDebugRequest({
|
|
270
328
|
body: gatewayRequest,
|
|
@@ -277,7 +335,12 @@ async function sendRemoteRequest(credentials, request, onDebugRequest) {
|
|
|
277
335
|
url: maskedCicdApiUrl(credentials.baseUrl),
|
|
278
336
|
});
|
|
279
337
|
}
|
|
280
|
-
|
|
338
|
+
try {
|
|
339
|
+
return await httpRequestFn(endpoint, 'POST', outerHeaders, gatewayRequest);
|
|
340
|
+
}
|
|
341
|
+
catch (error) {
|
|
342
|
+
throw enrichCicdGatewayError(error, errorRequest);
|
|
343
|
+
}
|
|
281
344
|
}
|
|
282
345
|
async function sendLocalRequest(credentials, request, onDebugRequest) {
|
|
283
346
|
const localConfig = readLocalGatewayConfig();
|
|
@@ -286,6 +349,10 @@ async function sendLocalRequest(credentials, request, onDebugRequest) {
|
|
|
286
349
|
const headers = buildLocalGatewayHeaders(credentials.apiKey, sfContext, request.headers, transactionId);
|
|
287
350
|
const url = new URL(request.endpoint, `${credentials.baseUrl}/`);
|
|
288
351
|
const { body } = request;
|
|
352
|
+
const errorRequest = {
|
|
353
|
+
method: request.method,
|
|
354
|
+
url: maskUrlSecrets(url),
|
|
355
|
+
};
|
|
289
356
|
if (onDebugRequest) {
|
|
290
357
|
onDebugRequest({
|
|
291
358
|
body: body ?? null,
|
|
@@ -304,10 +371,25 @@ async function sendLocalRequest(credentials, request, onDebugRequest) {
|
|
|
304
371
|
try {
|
|
305
372
|
return await httpRequestFn(url, request.method, headers, body);
|
|
306
373
|
}
|
|
374
|
+
catch (error) {
|
|
375
|
+
throw enrichCicdGatewayError(error, errorRequest);
|
|
376
|
+
}
|
|
307
377
|
finally {
|
|
308
378
|
activeHttpRequestOptions = previousOptions;
|
|
309
379
|
}
|
|
310
380
|
}
|
|
381
|
+
function enrichCicdGatewayError(error, request) {
|
|
382
|
+
if (!(error instanceof CicdGatewayError)) {
|
|
383
|
+
return error;
|
|
384
|
+
}
|
|
385
|
+
const baseMessage = error.message.replace(/\.\s*Request: .+$/u, '.');
|
|
386
|
+
return new CicdGatewayError(withRequestSuffix(baseMessage, request), {
|
|
387
|
+
categories: error.categories,
|
|
388
|
+
code: error.code,
|
|
389
|
+
request,
|
|
390
|
+
statusCode: error.statusCode,
|
|
391
|
+
});
|
|
392
|
+
}
|
|
311
393
|
async function sendRequest(credentials, request, onDebugRequest, options = {}) {
|
|
312
394
|
if (!options.forceRemote && readLocalGatewayConfig().enabled) {
|
|
313
395
|
return sendLocalRequest(credentials, request, onDebugRequest);
|