@ethlete/agent-rules 0.1.0-next.5 → 0.1.0-next.7
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/CHANGELOG.md +20 -0
- package/README.md +175 -4
- package/content/git-hooks/post-checkout.sh +10 -0
- package/content/git-hooks/pre-push.sh +5 -0
- package/content/hooks/context-warning.py +39 -11
- package/content/skills/git-commit/SKILL.md +6 -7
- package/content/skills/git-flow/SKILL.md +83 -0
- package/content/skills/handoff/SKILL.md +4 -0
- package/content/skills/query/SKILL.md +17 -13
- package/package.json +12 -1
- package/src/index.js +11 -10
- package/src/index.js.map +1 -1
- package/src/lib/commitlint.d.ts +10 -0
- package/src/lib/commitlint.js +51 -0
- package/src/lib/commitlint.js.map +1 -0
- package/src/lib/config.d.ts +28 -0
- package/src/lib/config.js +21 -3
- package/src/lib/config.js.map +1 -1
- package/src/lib/git-flow/build.d.ts +35 -0
- package/src/lib/git-flow/build.js +24 -0
- package/src/lib/git-flow/build.js.map +1 -0
- package/src/lib/git-flow/config.d.ts +59 -0
- package/src/lib/git-flow/config.js +50 -0
- package/src/lib/git-flow/config.js.map +1 -0
- package/src/lib/git-flow/index.d.ts +5 -0
- package/src/lib/git-flow/index.js +9 -0
- package/src/lib/git-flow/index.js.map +1 -0
- package/src/lib/git-flow/parse.d.ts +49 -0
- package/src/lib/git-flow/parse.js +274 -0
- package/src/lib/git-flow/parse.js.map +1 -0
- package/src/lib/git-flow/start.d.ts +22 -0
- package/src/lib/git-flow/start.js +24 -0
- package/src/lib/git-flow/start.js.map +1 -0
- package/src/lib/git-flow/validate.d.ts +34 -0
- package/src/lib/git-flow/validate.js +72 -0
- package/src/lib/git-flow/validate.js.map +1 -0
- package/src/lib/git-flow-command.d.ts +4 -0
- package/src/lib/git-flow-command.js +157 -0
- package/src/lib/git-flow-command.js.map +1 -0
- package/src/lib/git-flow-repair.d.ts +17 -0
- package/src/lib/git-flow-repair.js +150 -0
- package/src/lib/git-flow-repair.js.map +1 -0
- package/src/lib/git-flow-start.d.ts +20 -0
- package/src/lib/git-flow-start.js +147 -0
- package/src/lib/git-flow-start.js.map +1 -0
- package/src/lib/git.d.ts +27 -0
- package/src/lib/git.js +49 -0
- package/src/lib/git.js.map +1 -0
- package/src/lib/gitlab.d.ts +35 -0
- package/src/lib/gitlab.js +98 -0
- package/src/lib/gitlab.js.map +1 -0
- package/src/lib/jira.d.ts +28 -0
- package/src/lib/jira.js +83 -0
- package/src/lib/jira.js.map +1 -0
- package/src/lib/plan.d.ts +0 -1
- package/src/lib/plan.js +20 -4
- package/src/lib/plan.js.map +1 -1
- package/src/lib/prompt.d.ts +8 -0
- package/src/lib/prompt.js +27 -0
- package/src/lib/prompt.js.map +1 -0
- package/src/lib/render.d.ts +20 -2
- package/src/lib/render.js +31 -8
- package/src/lib/render.js.map +1 -1
- package/src/lib/sync.d.ts +0 -1
- package/src/lib/sync.js +2 -2
- package/src/lib/sync.js.map +1 -1
- package/src/lib/targets/claude-hooks.js +1 -1
- package/src/lib/targets/claude-hooks.js.map +1 -1
- package/src/lib/targets/claude.js +1 -1
- package/src/lib/targets/claude.js.map +1 -1
- package/src/lib/targets/codex-hooks.js +1 -1
- package/src/lib/targets/codex-hooks.js.map +1 -1
- package/src/lib/targets/codex.js +1 -1
- package/src/lib/targets/codex.js.map +1 -1
- package/src/lib/targets/copilot.js +1 -1
- package/src/lib/targets/copilot.js.map +1 -1
- package/src/lib/targets/cursor.js +1 -1
- package/src/lib/targets/cursor.js.map +1 -1
- package/src/lib/targets/git-hooks.d.ts +24 -0
- package/src/lib/targets/git-hooks.js +70 -0
- package/src/lib/targets/git-hooks.js.map +1 -0
- package/src/lib/targets/hooks-shared.d.ts +0 -1
- package/src/lib/targets/hooks-shared.js +7 -7
- package/src/lib/targets/hooks-shared.js.map +1 -1
- package/src/lib/targets/shared.d.ts +0 -1
- package/src/lib/targets/shared.js +1 -1
- package/src/lib/targets/shared.js.map +1 -1
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { SyncConfig } from './config';
|
|
2
|
+
export type RepairRequest = {
|
|
3
|
+
root: string;
|
|
4
|
+
config: SyncConfig;
|
|
5
|
+
ref?: string;
|
|
6
|
+
to?: string;
|
|
7
|
+
key?: string;
|
|
8
|
+
skipMrCheck: boolean;
|
|
9
|
+
assumeYes: boolean;
|
|
10
|
+
dryRun: boolean;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Renames a non-conforming branch and moves the merge requests aimed at it. Everything that can be
|
|
14
|
+
* checked is checked before the first mutation, and the order of the mutations is chosen so a
|
|
15
|
+
* failure leaves the old branch and every merge request intact.
|
|
16
|
+
*/
|
|
17
|
+
export declare const gitFlowRepair: (request: RepairRequest) => Promise<1 | 0>;
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.gitFlowRepair = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const git_1 = require("./git");
|
|
6
|
+
const git_flow_1 = require("./git-flow");
|
|
7
|
+
const gitlab_1 = require("./gitlab");
|
|
8
|
+
const prompt_1 = require("./prompt");
|
|
9
|
+
const KEY_PLACEHOLDER = '<KEY>';
|
|
10
|
+
const resolveNewName = (options) => {
|
|
11
|
+
const { branch, to, key, config } = options;
|
|
12
|
+
if (to)
|
|
13
|
+
return to;
|
|
14
|
+
const parse = (0, git_flow_1.parseBranch)({ branch, config: config.gitFlow });
|
|
15
|
+
if (!parse.suggestedName) {
|
|
16
|
+
throw new Error(parse.ok
|
|
17
|
+
? `${branch} already conforms — nothing to repair.`
|
|
18
|
+
: `No conforming name can be derived from ${branch}. Pass --to <branch>.`);
|
|
19
|
+
}
|
|
20
|
+
if (!parse.suggestedName.includes(KEY_PLACEHOLDER))
|
|
21
|
+
return parse.suggestedName;
|
|
22
|
+
if (!key) {
|
|
23
|
+
throw new Error(`${branch} carries no issue key, so the new name needs one: \`git-flow repair ${branch} --key FIP-1234\` (or --to <branch>).`);
|
|
24
|
+
}
|
|
25
|
+
return parse.suggestedName.replace(KEY_PLACEHOLDER, key.toUpperCase());
|
|
26
|
+
};
|
|
27
|
+
const readRemoteState = (options) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
|
|
28
|
+
const { root, branch, skipMrCheck } = options;
|
|
29
|
+
const remote = (0, git_1.defaultRemote)(root);
|
|
30
|
+
if (!remote || !(0, git_1.remoteBranchExists)({ root, remote, branch }))
|
|
31
|
+
return { pushed: false };
|
|
32
|
+
if (skipMrCheck)
|
|
33
|
+
return { pushed: true, remote, retarget: [] };
|
|
34
|
+
const project = (0, gitlab_1.parseRemoteUrl)((0, git_1.remoteUrl)({ root, remote }));
|
|
35
|
+
const token = (0, gitlab_1.gitLabToken)();
|
|
36
|
+
if (!project || !token) {
|
|
37
|
+
throw new Error([
|
|
38
|
+
`${branch} is pushed to ${remote}, so open merge requests may point at it and must be retargeted.`,
|
|
39
|
+
project
|
|
40
|
+
? 'Set GITLAB_TOKEN to a token with the "api" scope, or pass --no-mr-check if you know none do.'
|
|
41
|
+
: `The ${remote} URL is not a recognisable GitLab remote. Pass --no-mr-check if no merge request points at ${branch}.`,
|
|
42
|
+
].join('\n'));
|
|
43
|
+
}
|
|
44
|
+
const mergeRequests = yield (0, gitlab_1.openMergeRequestsFor)({ project, token, branch });
|
|
45
|
+
const blocking = (0, gitlab_1.blockingMergeRequests)({ mergeRequests, branch });
|
|
46
|
+
if (blocking.length > 0) {
|
|
47
|
+
throw new Error([
|
|
48
|
+
`${branch} is the source branch of ${blocking.length} open merge request(s):`,
|
|
49
|
+
...blocking.map((mr) => ` !${mr.iid} ${mr.title} ${mr.url}`),
|
|
50
|
+
'GitLab cannot move a merge request to a different source branch, and closing it would lose its discussion.',
|
|
51
|
+
'Merge or close it first, then repair the branch.',
|
|
52
|
+
].join('\n'));
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
pushed: true,
|
|
56
|
+
remote,
|
|
57
|
+
retarget: mergeRequests.filter((mr) => mr.targetBranch === branch),
|
|
58
|
+
api: { project, token },
|
|
59
|
+
};
|
|
60
|
+
});
|
|
61
|
+
const describeSteps = (options) => {
|
|
62
|
+
const { branch, newName, remote } = options;
|
|
63
|
+
const steps = [`git branch -m ${branch} ${newName}`];
|
|
64
|
+
if (remote.pushed) {
|
|
65
|
+
steps.push(`git push -u ${remote.remote} ${newName}`);
|
|
66
|
+
steps.push(...remote.retarget.map((mr) => `retarget !${mr.iid} to ${newName}`));
|
|
67
|
+
steps.push(`git push ${remote.remote} --delete ${branch}`);
|
|
68
|
+
}
|
|
69
|
+
return steps;
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* Renames a non-conforming branch and moves the merge requests aimed at it. Everything that can be
|
|
73
|
+
* checked is checked before the first mutation, and the order of the mutations is chosen so a
|
|
74
|
+
* failure leaves the old branch and every merge request intact.
|
|
75
|
+
*/
|
|
76
|
+
const gitFlowRepair = (request) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
|
|
77
|
+
var _a;
|
|
78
|
+
const { root, config, dryRun } = request;
|
|
79
|
+
const branch = (_a = request.ref) !== null && _a !== void 0 ? _a : (0, git_1.currentBranch)(root);
|
|
80
|
+
const newName = resolveNewName({ branch, to: request.to, key: request.key, config });
|
|
81
|
+
const parsed = (0, git_flow_1.parseBranch)({ branch: newName, config: config.gitFlow });
|
|
82
|
+
if (!parsed.ok) {
|
|
83
|
+
console.error(`${newName} does not conform either:`);
|
|
84
|
+
parsed.findings.forEach((finding) => console.error(` ${finding.rule} ${finding.message}`));
|
|
85
|
+
return 1;
|
|
86
|
+
}
|
|
87
|
+
if (!(0, git_1.localBranchExists)({ root, branch })) {
|
|
88
|
+
console.error(`${branch} does not exist locally — check it out first, then repair it.`);
|
|
89
|
+
return 1;
|
|
90
|
+
}
|
|
91
|
+
if ((0, git_1.localBranchExists)({ root, branch: newName })) {
|
|
92
|
+
console.error(`${newName} already exists locally.`);
|
|
93
|
+
return 1;
|
|
94
|
+
}
|
|
95
|
+
const remote = yield readRemoteState({ root, branch, skipMrCheck: request.skipMrCheck });
|
|
96
|
+
if (remote.pushed && (0, git_1.remoteBranchExists)({ root, remote: remote.remote, branch: newName })) {
|
|
97
|
+
console.error(`${newName} already exists on ${remote.remote}.`);
|
|
98
|
+
return 1;
|
|
99
|
+
}
|
|
100
|
+
const steps = describeSteps({ branch, newName, remote });
|
|
101
|
+
console.log('Repair');
|
|
102
|
+
console.log(` from ${branch}`);
|
|
103
|
+
console.log(` to ${newName}`);
|
|
104
|
+
console.log(` remote ${remote.pushed ? `${remote.remote} (pushed)` : 'not pushed'}`);
|
|
105
|
+
if (remote.pushed && !remote.api)
|
|
106
|
+
console.log(' warn merge requests were not checked (--no-mr-check)');
|
|
107
|
+
if (remote.pushed) {
|
|
108
|
+
remote.retarget.forEach((mr) => console.log(` retarget !${mr.iid} ${mr.title}`));
|
|
109
|
+
}
|
|
110
|
+
console.log('\nSteps');
|
|
111
|
+
steps.forEach((step, index) => console.log(` ${index + 1}. ${step}`));
|
|
112
|
+
if (dryRun) {
|
|
113
|
+
console.log('\nDry run — nothing was changed.');
|
|
114
|
+
return 0;
|
|
115
|
+
}
|
|
116
|
+
if (!(yield (0, prompt_1.confirm)({ question: `\nRename ${branch} to ${newName}?`, assumeYes: request.assumeYes }))) {
|
|
117
|
+
console.log('Nothing was changed.');
|
|
118
|
+
return 1;
|
|
119
|
+
}
|
|
120
|
+
(0, git_1.git)({ root, args: ['branch', '-m', branch, newName] });
|
|
121
|
+
if (!remote.pushed) {
|
|
122
|
+
console.log(`\nRenamed to ${newName}.`);
|
|
123
|
+
return 0;
|
|
124
|
+
}
|
|
125
|
+
(0, git_1.gitLoud)({ root, args: ['push', '-u', remote.remote, newName] });
|
|
126
|
+
const { api } = remote;
|
|
127
|
+
for (const mergeRequest of remote.retarget) {
|
|
128
|
+
if (!api)
|
|
129
|
+
break;
|
|
130
|
+
try {
|
|
131
|
+
yield (0, gitlab_1.retargetMergeRequest)({ project: api.project, token: api.token, iid: mergeRequest.iid, target: newName });
|
|
132
|
+
console.log(`Retargeted !${mergeRequest.iid} to ${newName}.`);
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
console.error(`\nFailed to retarget !${mergeRequest.iid}: ${error instanceof Error ? error.message : error}`);
|
|
136
|
+
console.error([
|
|
137
|
+
`${branch} still exists on ${remote.remote}, so nothing is broken — but the rename is half done.`,
|
|
138
|
+
`Retarget the remaining merge request(s) to ${newName} by hand, then run:`,
|
|
139
|
+
` git push ${remote.remote} --delete ${branch}`,
|
|
140
|
+
`Or undo: git branch -m ${newName} ${branch} && git push ${remote.remote} --delete ${newName}`,
|
|
141
|
+
].join('\n'));
|
|
142
|
+
return 1;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
(0, git_1.gitLoud)({ root, args: ['push', remote.remote, '--delete', branch] });
|
|
146
|
+
console.log(`\nRenamed to ${newName} on ${remote.remote}.`);
|
|
147
|
+
return 0;
|
|
148
|
+
});
|
|
149
|
+
exports.gitFlowRepair = gitFlowRepair;
|
|
150
|
+
//# sourceMappingURL=git-flow-repair.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-flow-repair.js","sourceRoot":"","sources":["../../../../../libs/agent-rules/src/lib/git-flow-repair.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { SyncConfig } from './config';
|
|
2
|
+
export type StartRequest = {
|
|
3
|
+
root: string;
|
|
4
|
+
config: SyncConfig;
|
|
5
|
+
key?: string;
|
|
6
|
+
subject?: string;
|
|
7
|
+
type?: string;
|
|
8
|
+
parent?: string;
|
|
9
|
+
hotfix: boolean;
|
|
10
|
+
releaseDate?: string;
|
|
11
|
+
push: boolean;
|
|
12
|
+
assumeYes: boolean;
|
|
13
|
+
dryRun: boolean;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* The prospective flow: an issue key in, a correctly named branch off the correct base out. Shares
|
|
17
|
+
* `planStart` with `@ethlete/timetrack`'s ticket → branch flow, so the two cannot name a branch
|
|
18
|
+
* differently.
|
|
19
|
+
*/
|
|
20
|
+
export declare const gitFlowStart: (request: StartRequest) => Promise<1 | 0>;
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.gitFlowStart = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const git_1 = require("./git");
|
|
6
|
+
const git_flow_1 = require("./git-flow");
|
|
7
|
+
const jira_1 = require("./jira");
|
|
8
|
+
const prompt_1 = require("./prompt");
|
|
9
|
+
/**
|
|
10
|
+
* A Task's parent Story must already have its feature branch, because that branch is what the
|
|
11
|
+
* sub-feature nests under and is based on. A Story whose branch still uses a deprecated spelling
|
|
12
|
+
* therefore has to be repaired before anything can nest under it — the name is the parent path.
|
|
13
|
+
*/
|
|
14
|
+
const findParentBranch = (options) => {
|
|
15
|
+
const { root, storyKey, config } = options;
|
|
16
|
+
const matches = (0, git_1.allBranches)(root).filter((branch) => {
|
|
17
|
+
const parse = (0, git_flow_1.parseBranch)({ branch, config });
|
|
18
|
+
return parse.kind === 'main-feature' && !parse.deprecated && parse.storyKey === storyKey;
|
|
19
|
+
});
|
|
20
|
+
if (matches.length === 0) {
|
|
21
|
+
throw new Error([
|
|
22
|
+
`No feature branch found for the parent story ${storyKey}.`,
|
|
23
|
+
`Create it first with \`ethlete-agents git-flow start ${storyKey}\`, or pass --of <branch>.`,
|
|
24
|
+
"If it exists under a deprecated name, `git-flow repair` it first — a sub-feature nests under its parent's full name.",
|
|
25
|
+
].join('\n'));
|
|
26
|
+
}
|
|
27
|
+
if (matches.length > 1) {
|
|
28
|
+
throw new Error(`${storyKey} has more than one feature branch: ${matches.join(', ')}. Pass --of <branch>.`);
|
|
29
|
+
}
|
|
30
|
+
return matches[0];
|
|
31
|
+
};
|
|
32
|
+
const specForParent = (options) => {
|
|
33
|
+
const { parent, key, subject, config } = options;
|
|
34
|
+
const kind = (0, git_flow_1.parseBranch)({ branch: parent, config }).kind === 'release' ? 'release-fix' : 'sub-feature';
|
|
35
|
+
return { kind, parent, key, subject };
|
|
36
|
+
};
|
|
37
|
+
const resolveSubject = (options) => {
|
|
38
|
+
const { issue, settings } = options;
|
|
39
|
+
if (issue.subject)
|
|
40
|
+
return { subject: issue.subject, subjectSource: `${settings.subjectField}` };
|
|
41
|
+
return {
|
|
42
|
+
subject: issue.summary,
|
|
43
|
+
subjectSource: settings.subjectField
|
|
44
|
+
? `the summary — ${settings.subjectField} is empty on ${issue.key}`
|
|
45
|
+
: 'the summary — set jira.subjectField to use the story subject field instead',
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
const resolve = (request) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
|
|
49
|
+
var _a;
|
|
50
|
+
const { root, config, subject, type, parent, hotfix, releaseDate } = request;
|
|
51
|
+
if (releaseDate)
|
|
52
|
+
return { spec: { kind: 'release', date: releaseDate }, subjectSource: '—' };
|
|
53
|
+
if (!request.key)
|
|
54
|
+
throw new Error('Pass an issue key, e.g. `git-flow start FIP-2177`.');
|
|
55
|
+
const key = request.key.toUpperCase();
|
|
56
|
+
if (subject) {
|
|
57
|
+
if (hotfix)
|
|
58
|
+
return { spec: { kind: 'hotfix', key, subject }, subjectSource: '--subject' };
|
|
59
|
+
if (parent) {
|
|
60
|
+
return { spec: specForParent({ parent, key, subject, config: config.gitFlow }), subjectSource: '--subject' };
|
|
61
|
+
}
|
|
62
|
+
return { spec: { kind: 'main-feature', type, key, subject }, subjectSource: '--subject' };
|
|
63
|
+
}
|
|
64
|
+
const issue = yield (0, jira_1.fetchJiraIssue)({
|
|
65
|
+
key,
|
|
66
|
+
credentials: (0, jira_1.resolveJiraCredentials)({ root, settings: config.jira }),
|
|
67
|
+
settings: config.jira,
|
|
68
|
+
});
|
|
69
|
+
const resolvedSubject = resolveSubject({ issue, settings: config.jira });
|
|
70
|
+
const resolvedType = type !== null && type !== void 0 ? type : (_a = config.jira.typeByIssueType) === null || _a === void 0 ? void 0 : _a[issue.issueType];
|
|
71
|
+
const shared = { key, subject: resolvedSubject.subject };
|
|
72
|
+
if (hotfix)
|
|
73
|
+
return { spec: Object.assign({ kind: 'hotfix' }, shared), issue, subjectSource: resolvedSubject.subjectSource };
|
|
74
|
+
const parentBranch = parent !== null && parent !== void 0 ? parent : (issue.parentKey ? findParentBranch({ root, storyKey: issue.parentKey, config: config.gitFlow }) : undefined);
|
|
75
|
+
if (parentBranch) {
|
|
76
|
+
return {
|
|
77
|
+
spec: specForParent(Object.assign(Object.assign({ parent: parentBranch }, shared), { config: config.gitFlow })),
|
|
78
|
+
issue,
|
|
79
|
+
subjectSource: resolvedSubject.subjectSource,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
spec: Object.assign({ kind: 'main-feature', type: resolvedType }, shared),
|
|
84
|
+
issue,
|
|
85
|
+
subjectSource: resolvedSubject.subjectSource,
|
|
86
|
+
};
|
|
87
|
+
});
|
|
88
|
+
const describePlan = (options) => {
|
|
89
|
+
const { plan, baseRef } = options;
|
|
90
|
+
const { issue, spec, subjectSource } = options.resolved;
|
|
91
|
+
const lines = [` branch ${plan.branch}`, ` from ${baseRef}`, ` merges to ${plan.mrTargets.join(' or ')}`];
|
|
92
|
+
if (issue)
|
|
93
|
+
lines.unshift(` issue ${issue.key} ${issue.issueType}${issue.summary ? ` — ${issue.summary}` : ''}`);
|
|
94
|
+
if (spec.kind !== 'release')
|
|
95
|
+
lines.push(` subject from ${subjectSource}`);
|
|
96
|
+
return lines;
|
|
97
|
+
};
|
|
98
|
+
/**
|
|
99
|
+
* The prospective flow: an issue key in, a correctly named branch off the correct base out. Shares
|
|
100
|
+
* `planStart` with `@ethlete/timetrack`'s ticket → branch flow, so the two cannot name a branch
|
|
101
|
+
* differently.
|
|
102
|
+
*/
|
|
103
|
+
const gitFlowStart = (request) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
|
|
104
|
+
const { root, config, push, dryRun } = request;
|
|
105
|
+
const resolved = yield resolve(request);
|
|
106
|
+
const plan = (0, git_flow_1.planStart)({ spec: resolved.spec, config: config.gitFlow });
|
|
107
|
+
if (plan.problems.length > 0) {
|
|
108
|
+
console.error(`Cannot name a conforming branch for this issue:`);
|
|
109
|
+
plan.problems.forEach((problem) => console.error(` ${problem}`));
|
|
110
|
+
return 1;
|
|
111
|
+
}
|
|
112
|
+
const base = plan.base;
|
|
113
|
+
const remote = (0, git_1.defaultRemote)(root);
|
|
114
|
+
const baseOnRemote = remote ? (0, git_1.remoteBranchExists)({ root, remote, branch: base }) : false;
|
|
115
|
+
const baseRef = baseOnRemote ? `${remote}/${base}` : base;
|
|
116
|
+
console.log(`Plan`);
|
|
117
|
+
describePlan({ plan, resolved, baseRef }).forEach((line) => console.log(line));
|
|
118
|
+
if ((0, git_1.localBranchExists)({ root, branch: plan.branch })) {
|
|
119
|
+
console.error(`\n${plan.branch} already exists — \`git switch ${plan.branch}\`.`);
|
|
120
|
+
return 1;
|
|
121
|
+
}
|
|
122
|
+
if (!baseOnRemote && !(0, git_1.localBranchExists)({ root, branch: base })) {
|
|
123
|
+
console.error(`\nThe base branch ${base} exists neither locally nor on ${remote !== null && remote !== void 0 ? remote : 'any remote'}.`);
|
|
124
|
+
return 1;
|
|
125
|
+
}
|
|
126
|
+
if ((0, git_1.isDirty)(root)) {
|
|
127
|
+
console.error('\nThe working tree has uncommitted changes — commit or stash them first.');
|
|
128
|
+
return 1;
|
|
129
|
+
}
|
|
130
|
+
if (dryRun) {
|
|
131
|
+
console.log('\nDry run — nothing was created.');
|
|
132
|
+
return 0;
|
|
133
|
+
}
|
|
134
|
+
if (!(yield (0, prompt_1.confirm)({ question: `\nCreate ${plan.branch} from ${baseRef}?`, assumeYes: request.assumeYes }))) {
|
|
135
|
+
console.log('Nothing was created.');
|
|
136
|
+
return 1;
|
|
137
|
+
}
|
|
138
|
+
if (remote && baseOnRemote)
|
|
139
|
+
(0, git_1.gitLoud)({ root, args: ['fetch', remote, base] });
|
|
140
|
+
(0, git_1.gitLoud)({ root, args: ['switch', '-c', plan.branch, '--no-track', baseRef] });
|
|
141
|
+
if (push && remote)
|
|
142
|
+
(0, git_1.gitLoud)({ root, args: ['push', '-u', remote, plan.branch] });
|
|
143
|
+
console.log(`\nOn ${(0, git_1.currentBranch)(root)}. Open the merge request against ${plan.mrTargets[0]}.`);
|
|
144
|
+
return 0;
|
|
145
|
+
});
|
|
146
|
+
exports.gitFlowStart = gitFlowStart;
|
|
147
|
+
//# sourceMappingURL=git-flow-start.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-flow-start.js","sourceRoot":"","sources":["../../../../../libs/agent-rules/src/lib/git-flow-start.ts"],"names":[],"mappings":"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"}
|
package/src/lib/git.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export declare const git: (options: {
|
|
2
|
+
root: string;
|
|
3
|
+
args: string[];
|
|
4
|
+
}) => string;
|
|
5
|
+
/** Runs a command whose output belongs on the terminal — a push, a fetch, a branch creation. */
|
|
6
|
+
export declare const gitLoud: (options: {
|
|
7
|
+
root: string;
|
|
8
|
+
args: string[];
|
|
9
|
+
}) => string;
|
|
10
|
+
export declare const currentBranch: (root: string) => string;
|
|
11
|
+
export declare const allBranches: (root: string) => string[];
|
|
12
|
+
export declare const isDirty: (root: string) => boolean;
|
|
13
|
+
export declare const localBranchExists: (options: {
|
|
14
|
+
root: string;
|
|
15
|
+
branch: string;
|
|
16
|
+
}) => boolean;
|
|
17
|
+
export declare const remoteBranchExists: (options: {
|
|
18
|
+
root: string;
|
|
19
|
+
remote: string;
|
|
20
|
+
branch: string;
|
|
21
|
+
}) => boolean;
|
|
22
|
+
/** `origin` when it exists, otherwise the only remote — a repo with several and no `origin` is ambiguous. */
|
|
23
|
+
export declare const defaultRemote: (root: string) => string | undefined;
|
|
24
|
+
export declare const remoteUrl: (options: {
|
|
25
|
+
root: string;
|
|
26
|
+
remote: string;
|
|
27
|
+
}) => string;
|
package/src/lib/git.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.remoteUrl = exports.defaultRemote = exports.remoteBranchExists = exports.localBranchExists = exports.isDirty = exports.allBranches = exports.currentBranch = exports.gitLoud = exports.git = void 0;
|
|
4
|
+
const child_process_1 = require("child_process");
|
|
5
|
+
const git_flow_1 = require("./git-flow");
|
|
6
|
+
const git = (options) => (0, child_process_1.execFileSync)('git', options.args, { cwd: options.root, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }).trim();
|
|
7
|
+
exports.git = git;
|
|
8
|
+
/** Runs a command whose output belongs on the terminal — a push, a fetch, a branch creation. */
|
|
9
|
+
const gitLoud = (options) => (0, child_process_1.execFileSync)('git', options.args, { cwd: options.root, encoding: 'utf8', stdio: ['ignore', 'inherit', 'inherit'] });
|
|
10
|
+
exports.gitLoud = gitLoud;
|
|
11
|
+
const currentBranch = (root) => (0, exports.git)({ root, args: ['rev-parse', '--abbrev-ref', 'HEAD'] });
|
|
12
|
+
exports.currentBranch = currentBranch;
|
|
13
|
+
const allBranches = (root) => {
|
|
14
|
+
const refs = (0, exports.git)({ root, args: ['for-each-ref', '--format=%(refname)', 'refs/heads', 'refs/remotes'] });
|
|
15
|
+
const names = refs
|
|
16
|
+
.split('\n')
|
|
17
|
+
.filter((ref) => ref && !ref.endsWith('/HEAD'))
|
|
18
|
+
.map(git_flow_1.stripRefPrefix);
|
|
19
|
+
return [...new Set(names)].sort();
|
|
20
|
+
};
|
|
21
|
+
exports.allBranches = allBranches;
|
|
22
|
+
const isDirty = (root) => (0, exports.git)({ root, args: ['status', '--porcelain'] }).length > 0;
|
|
23
|
+
exports.isDirty = isDirty;
|
|
24
|
+
const refExists = (options) => {
|
|
25
|
+
try {
|
|
26
|
+
(0, exports.git)({ root: options.root, args: ['rev-parse', '--verify', '--quiet', options.ref] });
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
catch (_a) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
const localBranchExists = (options) => refExists({ root: options.root, ref: `refs/heads/${options.branch}` });
|
|
34
|
+
exports.localBranchExists = localBranchExists;
|
|
35
|
+
const remoteBranchExists = (options) => (0, exports.git)({ root: options.root, args: ['ls-remote', '--heads', options.remote, options.branch] }).length > 0;
|
|
36
|
+
exports.remoteBranchExists = remoteBranchExists;
|
|
37
|
+
/** `origin` when it exists, otherwise the only remote — a repo with several and no `origin` is ambiguous. */
|
|
38
|
+
const defaultRemote = (root) => {
|
|
39
|
+
const remotes = (0, exports.git)({ root, args: ['remote'] })
|
|
40
|
+
.split('\n')
|
|
41
|
+
.filter(Boolean);
|
|
42
|
+
if (remotes.includes('origin'))
|
|
43
|
+
return 'origin';
|
|
44
|
+
return remotes.length === 1 ? remotes[0] : undefined;
|
|
45
|
+
};
|
|
46
|
+
exports.defaultRemote = defaultRemote;
|
|
47
|
+
const remoteUrl = (options) => (0, exports.git)({ root: options.root, args: ['remote', 'get-url', options.remote] });
|
|
48
|
+
exports.remoteUrl = remoteUrl;
|
|
49
|
+
//# sourceMappingURL=git.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.js","sourceRoot":"","sources":["../../../../../libs/agent-rules/src/lib/git.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export type GitLabProject = {
|
|
2
|
+
host: string;
|
|
3
|
+
project: string;
|
|
4
|
+
};
|
|
5
|
+
export type GitLabMergeRequest = {
|
|
6
|
+
iid: number;
|
|
7
|
+
title: string;
|
|
8
|
+
sourceBranch: string;
|
|
9
|
+
targetBranch: string;
|
|
10
|
+
url: string;
|
|
11
|
+
};
|
|
12
|
+
/** `git@host:group/sub/project.git`, `ssh://git@host:2222/group/project.git` and the https form. */
|
|
13
|
+
export declare const parseRemoteUrl: (url: string) => GitLabProject | undefined;
|
|
14
|
+
export declare const gitLabToken: () => string | undefined;
|
|
15
|
+
/** Open merge requests whose source **or** target is the branch — both block a rename, differently. */
|
|
16
|
+
export declare const openMergeRequestsFor: (options: {
|
|
17
|
+
project: GitLabProject;
|
|
18
|
+
token: string;
|
|
19
|
+
branch: string;
|
|
20
|
+
}) => Promise<GitLabMergeRequest[]>;
|
|
21
|
+
export declare const retargetMergeRequest: (options: {
|
|
22
|
+
project: GitLabProject;
|
|
23
|
+
token: string;
|
|
24
|
+
iid: number;
|
|
25
|
+
target: string;
|
|
26
|
+
}) => Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* The GitLab API can change a merge request's target branch but never its source branch, so an open
|
|
29
|
+
* merge request *from* the branch cannot survive a rename — it has to be closed and reopened, which
|
|
30
|
+
* loses its discussion. `repair` reports these and refuses rather than doing that to someone.
|
|
31
|
+
*/
|
|
32
|
+
export declare const blockingMergeRequests: (options: {
|
|
33
|
+
mergeRequests: GitLabMergeRequest[];
|
|
34
|
+
branch: string;
|
|
35
|
+
}) => GitLabMergeRequest[];
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.blockingMergeRequests = exports.retargetMergeRequest = exports.openMergeRequestsFor = exports.gitLabToken = exports.parseRemoteUrl = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const TIMEOUT_MS = 15000;
|
|
6
|
+
/** `git@host:group/sub/project.git`, `ssh://git@host:2222/group/project.git` and the https form. */
|
|
7
|
+
const parseRemoteUrl = (url) => {
|
|
8
|
+
const trimmed = url.trim();
|
|
9
|
+
const scp = /^[^@/\s]+@([^:/\s]+):(.+?)(?:\.git)?\/?$/.exec(trimmed);
|
|
10
|
+
if ((scp === null || scp === void 0 ? void 0 : scp[1]) && scp[2])
|
|
11
|
+
return { host: scp[1], project: scp[2] };
|
|
12
|
+
const withScheme = /^[a-z+]+:\/\/(?:[^@/]+@)?([^/:\s]+)(?::\d+)?\/(.+?)(?:\.git)?\/?$/.exec(trimmed);
|
|
13
|
+
if ((withScheme === null || withScheme === void 0 ? void 0 : withScheme[1]) && withScheme[2])
|
|
14
|
+
return { host: withScheme[1], project: withScheme[2] };
|
|
15
|
+
return undefined;
|
|
16
|
+
};
|
|
17
|
+
exports.parseRemoteUrl = parseRemoteUrl;
|
|
18
|
+
const gitLabToken = () => { var _a, _b; return ((_a = process.env['GITLAB_TOKEN']) === null || _a === void 0 ? void 0 : _a.trim()) || ((_b = process.env['CI_JOB_TOKEN']) === null || _b === void 0 ? void 0 : _b.trim()) || undefined; };
|
|
19
|
+
exports.gitLabToken = gitLabToken;
|
|
20
|
+
const TARGET_BRANCH_FIELD = 'target_branch';
|
|
21
|
+
/* eslint-enable @typescript-eslint/naming-convention */
|
|
22
|
+
const toMergeRequest = (raw) => {
|
|
23
|
+
var _a, _b, _c, _d, _e;
|
|
24
|
+
return ({
|
|
25
|
+
iid: (_a = raw.iid) !== null && _a !== void 0 ? _a : 0,
|
|
26
|
+
title: (_b = raw.title) !== null && _b !== void 0 ? _b : '',
|
|
27
|
+
sourceBranch: (_c = raw.source_branch) !== null && _c !== void 0 ? _c : '',
|
|
28
|
+
targetBranch: (_d = raw.target_branch) !== null && _d !== void 0 ? _d : '',
|
|
29
|
+
url: (_e = raw.web_url) !== null && _e !== void 0 ? _e : '',
|
|
30
|
+
});
|
|
31
|
+
};
|
|
32
|
+
const request = (options) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
|
|
33
|
+
const { project, token, path, method, body } = options;
|
|
34
|
+
const base = `https://${project.host}/api/v4/projects/${encodeURIComponent(project.project)}`;
|
|
35
|
+
const response = yield fetch(`${base}${path}`, {
|
|
36
|
+
method: method !== null && method !== void 0 ? method : 'GET',
|
|
37
|
+
headers: Object.assign({ 'private-token': token, accept: 'application/json' }, (body ? { 'content-type': 'application/json' } : {})),
|
|
38
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
39
|
+
signal: AbortSignal.timeout(TIMEOUT_MS),
|
|
40
|
+
});
|
|
41
|
+
if (response.status === 401 || response.status === 403) {
|
|
42
|
+
throw new Error(`GitLab rejected the token (${response.status}) for ${project.project}. GITLAB_TOKEN needs the "api" scope and Developer access.`);
|
|
43
|
+
}
|
|
44
|
+
if (!response.ok) {
|
|
45
|
+
throw new Error(`GitLab responded ${response.status} ${response.statusText} for ${method !== null && method !== void 0 ? method : 'GET'} ${path}.`);
|
|
46
|
+
}
|
|
47
|
+
return { body: (yield response.json()), nextPage: response.headers.get('x-next-page') || undefined };
|
|
48
|
+
});
|
|
49
|
+
/**
|
|
50
|
+
* Follows `x-next-page` rather than reading the first page only: a rename that silently skipped a
|
|
51
|
+
* merge request on page two would leave exactly the half-finished state `repair` must never produce.
|
|
52
|
+
*/
|
|
53
|
+
const listMergeRequests = (options) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
|
|
54
|
+
const { project, token, filter } = options;
|
|
55
|
+
const found = [];
|
|
56
|
+
let page = '1';
|
|
57
|
+
while (page) {
|
|
58
|
+
const result = yield request({
|
|
59
|
+
project,
|
|
60
|
+
token,
|
|
61
|
+
path: `/merge_requests?state=opened&per_page=100&${filter}&page=${page}`,
|
|
62
|
+
});
|
|
63
|
+
found.push(...result.body.map(toMergeRequest));
|
|
64
|
+
page = result.nextPage;
|
|
65
|
+
}
|
|
66
|
+
return found;
|
|
67
|
+
});
|
|
68
|
+
/** Open merge requests whose source **or** target is the branch — both block a rename, differently. */
|
|
69
|
+
const openMergeRequestsFor = (options) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
|
|
70
|
+
const { project, token, branch } = options;
|
|
71
|
+
const encoded = encodeURIComponent(branch);
|
|
72
|
+
const [fromBranch, intoBranch] = yield Promise.all([
|
|
73
|
+
listMergeRequests({ project, token, filter: `source_branch=${encoded}` }),
|
|
74
|
+
listMergeRequests({ project, token, filter: `target_branch=${encoded}` }),
|
|
75
|
+
]);
|
|
76
|
+
const byIid = new Map([...fromBranch, ...intoBranch].map((mr) => [mr.iid, mr]));
|
|
77
|
+
return [...byIid.values()].sort((a, b) => a.iid - b.iid);
|
|
78
|
+
});
|
|
79
|
+
exports.openMergeRequestsFor = openMergeRequestsFor;
|
|
80
|
+
const retargetMergeRequest = (options) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
|
|
81
|
+
const { project, token, iid, target } = options;
|
|
82
|
+
yield request({
|
|
83
|
+
project,
|
|
84
|
+
token,
|
|
85
|
+
path: `/merge_requests/${iid}`,
|
|
86
|
+
method: 'PUT',
|
|
87
|
+
body: { [TARGET_BRANCH_FIELD]: target },
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
exports.retargetMergeRequest = retargetMergeRequest;
|
|
91
|
+
/**
|
|
92
|
+
* The GitLab API can change a merge request's target branch but never its source branch, so an open
|
|
93
|
+
* merge request *from* the branch cannot survive a rename — it has to be closed and reopened, which
|
|
94
|
+
* loses its discussion. `repair` reports these and refuses rather than doing that to someone.
|
|
95
|
+
*/
|
|
96
|
+
const blockingMergeRequests = (options) => options.mergeRequests.filter((mr) => mr.sourceBranch === options.branch);
|
|
97
|
+
exports.blockingMergeRequests = blockingMergeRequests;
|
|
98
|
+
//# sourceMappingURL=gitlab.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gitlab.js","sourceRoot":"","sources":["../../../../../libs/agent-rules/src/lib/gitlab.ts"],"names":[],"mappings":";;;;AAUA,MAAM,UAAU,GAAG,KAAM,CAAC;AAE1B,oGAAoG;AAC7F,MAAM,cAAc,GAAG,CAAC,GAAW,EAA6B,EAAE;IACvE,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;IAC3B,MAAM,GAAG,GAAG,0CAA0C,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAErE,IAAI,CAAA,GAAG,aAAH,GAAG,uBAAH,GAAG,CAAG,CAAC,CAAC,KAAI,GAAG,CAAC,CAAC,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IAEjE,MAAM,UAAU,GAAG,mEAAmE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAErG,IAAI,CAAA,UAAU,aAAV,UAAU,uBAAV,UAAU,CAAG,CAAC,CAAC,KAAI,UAAU,CAAC,CAAC,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC;IAE7F,OAAO,SAAS,CAAC;AACnB,CAAC,CAAC;AAXW,QAAA,cAAc,kBAWzB;AAEK,MAAM,WAAW,GAAG,GAAG,EAAE,eAC9B,OAAA,CAAA,MAAA,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,0CAAE,IAAI,EAAE,MAAI,MAAA,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,0CAAE,IAAI,EAAE,CAAA,IAAI,SAAS,CAAA,EAAA,CAAC;AAD7E,QAAA,WAAW,eACkE;AAW1F,MAAM,mBAAmB,GAAG,eAAe,CAAC;AAC5C,wDAAwD;AAExD,MAAM,cAAc,GAAG,CAAC,GAAyB,EAAsB,EAAE;;IAAC,OAAA,CAAC;QACzE,GAAG,EAAE,MAAA,GAAG,CAAC,GAAG,mCAAI,CAAC;QACjB,KAAK,EAAE,MAAA,GAAG,CAAC,KAAK,mCAAI,EAAE;QACtB,YAAY,EAAE,MAAA,GAAG,CAAC,aAAa,mCAAI,EAAE;QACrC,YAAY,EAAE,MAAA,GAAG,CAAC,aAAa,mCAAI,EAAE;QACrC,GAAG,EAAE,MAAA,GAAG,CAAC,OAAO,mCAAI,EAAE;KACvB,CAAC,CAAA;CAAA,CAAC;AAEH,MAAM,OAAO,GAAG,CAAO,OAMtB,EAAE,EAAE;IACH,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC;IACvD,MAAM,IAAI,GAAG,WAAW,OAAO,CAAC,IAAI,oBAAoB,kBAAkB,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;IAC9F,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,GAAG,IAAI,EAAE,EAAE;QAC7C,MAAM,EAAE,MAAM,aAAN,MAAM,cAAN,MAAM,GAAI,KAAK;QACvB,OAAO,kBACL,eAAe,EAAE,KAAK,EACtB,MAAM,EAAE,kBAAkB,IACvB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CACxD;QACD,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS;QAC7C,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,UAAU,CAAC;KACxC,CAAC,CAAC;IAEH,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;QACvD,MAAM,IAAI,KAAK,CACb,8BAA8B,QAAQ,CAAC,MAAM,SAAS,OAAO,CAAC,OAAO,4DAA4D,CAClI,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,oBAAoB,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,QAAQ,MAAM,aAAN,MAAM,cAAN,MAAM,GAAI,KAAK,IAAI,IAAI,GAAG,CAAC,CAAC;IAChH,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAY,EAAE,QAAQ,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,IAAI,SAAS,EAAE,CAAC;AAClH,CAAC,CAAA,CAAC;AAEF;;;GAGG;AACH,MAAM,iBAAiB,GAAG,CAAO,OAAkE,EAAE,EAAE;IACrG,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;IAC3C,MAAM,KAAK,GAAyB,EAAE,CAAC;IACvC,IAAI,IAAI,GAAuB,GAAG,CAAC;IAEnC,OAAO,IAAI,EAAE,CAAC;QACZ,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC;YAC3B,OAAO;YACP,KAAK;YACL,IAAI,EAAE,6CAA6C,MAAM,SAAS,IAAI,EAAE;SACzE,CAAC,CAAC;QAEH,KAAK,CAAC,IAAI,CAAC,GAAI,MAAM,CAAC,IAA+B,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC;QAC3E,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC;IACzB,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC,CAAA,CAAC;AAEF,uGAAuG;AAChG,MAAM,oBAAoB,GAAG,CAAO,OAI1C,EAAiC,EAAE;IAClC,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;IAC3C,MAAM,OAAO,GAAG,kBAAkB,CAAC,MAAM,CAAC,CAAC;IAC3C,MAAM,CAAC,UAAU,EAAE,UAAU,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QACjD,iBAAiB,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,iBAAiB,OAAO,EAAE,EAAE,CAAC;QACzE,iBAAiB,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,iBAAiB,OAAO,EAAE,EAAE,CAAC;KAC1E,CAAC,CAAC;IACH,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,UAAU,EAAE,GAAG,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;IAEhF,OAAO,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;AAC3D,CAAC,CAAA,CAAC;AAdW,QAAA,oBAAoB,wBAc/B;AAEK,MAAM,oBAAoB,GAAG,CAAO,OAK1C,EAAE,EAAE;IACH,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;IAEhD,MAAM,OAAO,CAAC;QACZ,OAAO;QACP,KAAK;QACL,IAAI,EAAE,mBAAmB,GAAG,EAAE;QAC9B,MAAM,EAAE,KAAK;QACb,IAAI,EAAE,EAAE,CAAC,mBAAmB,CAAC,EAAE,MAAM,EAAE;KACxC,CAAC,CAAC;AACL,CAAC,CAAA,CAAC;AAfW,QAAA,oBAAoB,wBAe/B;AAEF;;;;GAIG;AACI,MAAM,qBAAqB,GAAG,CAAC,OAAgE,EAAE,EAAE,CACxG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,YAAY,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;AAD9D,QAAA,qBAAqB,yBACyC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { JiraSettings } from './config';
|
|
2
|
+
export type JiraCredentials = {
|
|
3
|
+
host: string;
|
|
4
|
+
email: string;
|
|
5
|
+
token: string;
|
|
6
|
+
};
|
|
7
|
+
export type JiraIssue = {
|
|
8
|
+
key: string;
|
|
9
|
+
summary: string;
|
|
10
|
+
issueType: string;
|
|
11
|
+
/** The parent issue, when the instance reports one — a Task under its Story. */
|
|
12
|
+
parentKey?: string;
|
|
13
|
+
/** The configured subject field's value, when the instance has one and this issue sets it. */
|
|
14
|
+
subject?: string;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* The host may also be committed in `ethlete-agents.config.json` (it is not a secret); the email and
|
|
18
|
+
* token may only come from the environment or the gitignored local config.
|
|
19
|
+
*/
|
|
20
|
+
export declare const resolveJiraCredentials: (options: {
|
|
21
|
+
root: string;
|
|
22
|
+
settings: JiraSettings;
|
|
23
|
+
}) => JiraCredentials;
|
|
24
|
+
export declare const fetchJiraIssue: (options: {
|
|
25
|
+
key: string;
|
|
26
|
+
credentials: JiraCredentials;
|
|
27
|
+
settings: JiraSettings;
|
|
28
|
+
}) => Promise<JiraIssue>;
|