automata-cli 0.9.0-develop.382 → 0.9.0-develop.387
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 +1 -1
- package/dist/{ConfigWizard-T7N4CZFT.js → ConfigWizard-RGXIC7OS.js} +34 -7
- package/dist/index.js +128 -35
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -71,7 +71,7 @@ Checks that the PR exists and is merged, the working tree is clean, and the remo
|
|
|
71
71
|
|
|
72
72
|
### `automata git publish-release`
|
|
73
73
|
|
|
74
|
-
Execute the
|
|
74
|
+
Execute the release sequence — GitFlow, or trunk-based for a repository without `develop` — and push to `origin`. Only requires `git`.
|
|
75
75
|
|
|
76
76
|
```bash
|
|
77
77
|
automata git publish-release # auto-detect version from the trunk tag
|
|
@@ -40,6 +40,11 @@ var DUMP_ON_BLOCK_OPTIONS = [
|
|
|
40
40
|
{ label: "Yes \u2014 print the full health report when a tick does nothing", value: true },
|
|
41
41
|
{ label: "No \u2014 print only the one-line reason", value: false }
|
|
42
42
|
];
|
|
43
|
+
var RELEASE_FLOW_OPTIONS = [
|
|
44
|
+
{ label: "Detect from origin (gitflow if origin/develop exists)", value: void 0 },
|
|
45
|
+
{ label: "GitFlow", value: "gitflow" },
|
|
46
|
+
{ label: "Trunk-based", value: "trunk" }
|
|
47
|
+
];
|
|
43
48
|
var MAIN_MENU_OPTIONS = ["Remote / Mode", "Implement-Next", "Prompts", "Issue Watch", "Do Work", "Git"];
|
|
44
49
|
var PROMPTS_MENU_OPTIONS = [
|
|
45
50
|
"Sonar",
|
|
@@ -186,6 +191,8 @@ function ConfigWizard() {
|
|
|
186
191
|
existing.doWork?.prompts?.prOrphan ?? DEFAULT_DO_WORK_PR_ORPHAN_PROMPT
|
|
187
192
|
);
|
|
188
193
|
const [gitTrunkBranch, setGitTrunkBranch] = useState(existing.git?.trunkBranch ?? "");
|
|
194
|
+
const initialReleaseFlowIndex = RELEASE_FLOW_OPTIONS.findIndex((o) => o.value === existing.git?.releaseFlow);
|
|
195
|
+
const [releaseFlowIndex, setReleaseFlowIndex] = useState(Math.max(initialReleaseFlowIndex, 0));
|
|
189
196
|
const [pendingRemote, setPendingRemote] = useState(existing.remoteType ?? "gh");
|
|
190
197
|
const [pendingTechnique, setPendingTechnique] = useState(
|
|
191
198
|
existing.issueDiscoveryTechnique ?? "label"
|
|
@@ -374,12 +381,7 @@ function ConfigWizard() {
|
|
|
374
381
|
},
|
|
375
382
|
"git-trunk-branch": {
|
|
376
383
|
setValue: setGitTrunkBranch,
|
|
377
|
-
onSubmit: () =>
|
|
378
|
-
const branch = gitTrunkBranch.trim();
|
|
379
|
-
const current = readRawConfig();
|
|
380
|
-
writeConfig({ ...current, git: { ...current.git, trunkBranch: branch || void 0 } });
|
|
381
|
-
exit();
|
|
382
|
-
},
|
|
384
|
+
onSubmit: () => setScreen("git-release-flow"),
|
|
383
385
|
onBack: () => setScreen("main")
|
|
384
386
|
}
|
|
385
387
|
};
|
|
@@ -464,6 +466,25 @@ function ConfigWizard() {
|
|
|
464
466
|
exit();
|
|
465
467
|
},
|
|
466
468
|
onBack: () => setScreen("do-work-lock-stale")
|
|
469
|
+
},
|
|
470
|
+
"git-release-flow": {
|
|
471
|
+
index: releaseFlowIndex,
|
|
472
|
+
length: RELEASE_FLOW_OPTIONS.length,
|
|
473
|
+
setIndex: setReleaseFlowIndex,
|
|
474
|
+
onSelect: () => {
|
|
475
|
+
const branch = gitTrunkBranch.trim();
|
|
476
|
+
const current = readRawConfig();
|
|
477
|
+
writeConfig({
|
|
478
|
+
...current,
|
|
479
|
+
git: {
|
|
480
|
+
...current.git,
|
|
481
|
+
trunkBranch: branch || void 0,
|
|
482
|
+
releaseFlow: RELEASE_FLOW_OPTIONS[releaseFlowIndex].value
|
|
483
|
+
}
|
|
484
|
+
});
|
|
485
|
+
exit();
|
|
486
|
+
},
|
|
487
|
+
onBack: () => setScreen("git-trunk-branch")
|
|
467
488
|
}
|
|
468
489
|
};
|
|
469
490
|
useInput((input, key) => {
|
|
@@ -591,7 +612,7 @@ function ConfigWizard() {
|
|
|
591
612
|
title: "Git \u2014 Trunk Branch",
|
|
592
613
|
label: "Branch publish-release releases to (blank = detect it from the remote):",
|
|
593
614
|
value: gitTrunkBranch,
|
|
594
|
-
hint: `Type branch \xB7 Enter to
|
|
615
|
+
hint: `Type branch \xB7 Enter to continue \xB7 ${BACK}`
|
|
595
616
|
}
|
|
596
617
|
};
|
|
597
618
|
const menuViews = {
|
|
@@ -630,6 +651,12 @@ function ConfigWizard() {
|
|
|
630
651
|
options: DUMP_ON_BLOCK_OPTIONS.map((o) => o.label),
|
|
631
652
|
index: doWorkDumpOnBlockIndex,
|
|
632
653
|
hint: `\u2191/\u2193 to move \xB7 Enter to save and exit \xB7 ${BACK}`
|
|
654
|
+
},
|
|
655
|
+
"git-release-flow": {
|
|
656
|
+
title: "Git \u2014 Release Flow",
|
|
657
|
+
options: RELEASE_FLOW_OPTIONS.map((o) => o.label),
|
|
658
|
+
index: releaseFlowIndex,
|
|
659
|
+
hint: `\u2191/\u2193 to move \xB7 Enter to save \xB7 ${BACK}`
|
|
633
660
|
}
|
|
634
661
|
};
|
|
635
662
|
const textView = textViews[screen];
|
package/dist/index.js
CHANGED
|
@@ -26,6 +26,60 @@ var version = packageJson.version;
|
|
|
26
26
|
|
|
27
27
|
// src/commands/config.ts
|
|
28
28
|
import { Command } from "commander";
|
|
29
|
+
|
|
30
|
+
// src/git/releaseFlow.ts
|
|
31
|
+
var RELEASE_FLOWS = ["gitflow", "trunk"];
|
|
32
|
+
var RELEASE_FLOW_CONFIG_KEY = "git.releaseFlow";
|
|
33
|
+
function isReleaseFlow(value) {
|
|
34
|
+
return typeof value === "string" && RELEASE_FLOWS.includes(value);
|
|
35
|
+
}
|
|
36
|
+
function describeReleaseFlowSource(source) {
|
|
37
|
+
switch (source) {
|
|
38
|
+
case "config":
|
|
39
|
+
return `configured as ${RELEASE_FLOW_CONFIG_KEY}`;
|
|
40
|
+
case "develop-present":
|
|
41
|
+
return "detected: origin/develop exists";
|
|
42
|
+
case "develop-absent":
|
|
43
|
+
return "detected: origin/develop does not exist";
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function invalidReleaseFlowMessage(value) {
|
|
47
|
+
return `Invalid ${RELEASE_FLOW_CONFIG_KEY} ${JSON.stringify(value)} in .automata/config.json; expected one of: ${RELEASE_FLOWS.join(", ")}.
|
|
48
|
+
Fix it with: automata config set git-release-flow <gitflow|trunk>`;
|
|
49
|
+
}
|
|
50
|
+
function planRelease(flow, version2, trunk, trunkIsLocal) {
|
|
51
|
+
if (flow === "trunk") {
|
|
52
|
+
const message = `chore(release): ${version2}`;
|
|
53
|
+
return [
|
|
54
|
+
{ args: ["commit", "--allow-empty", "-m", message], desc: `git commit --allow-empty -m "${message}"` },
|
|
55
|
+
{ args: ["tag", version2], desc: `git tag ${version2}` },
|
|
56
|
+
{
|
|
57
|
+
args: ["push", "--atomic", "origin", trunk, version2],
|
|
58
|
+
desc: `git push --atomic origin ${trunk} ${version2}`
|
|
59
|
+
}
|
|
60
|
+
];
|
|
61
|
+
}
|
|
62
|
+
const releaseBranch = `release/${version2}`;
|
|
63
|
+
const checkoutTrunk = trunkIsLocal ? { args: ["checkout", trunk], desc: `git checkout ${trunk}` } : {
|
|
64
|
+
args: ["checkout", "-b", trunk, `origin/${trunk}`],
|
|
65
|
+
desc: `git checkout -b ${trunk} origin/${trunk}`
|
|
66
|
+
};
|
|
67
|
+
return [
|
|
68
|
+
{ args: ["checkout", "-b", releaseBranch], desc: `git checkout -b ${releaseBranch}` },
|
|
69
|
+
checkoutTrunk,
|
|
70
|
+
{ args: ["merge", "--no-ff", releaseBranch], desc: `git merge --no-ff ${releaseBranch}` },
|
|
71
|
+
{ args: ["tag", version2], desc: `git tag ${version2}` },
|
|
72
|
+
{ args: ["checkout", "develop"], desc: `git checkout develop` },
|
|
73
|
+
{ args: ["merge", "--no-ff", releaseBranch], desc: `git merge --no-ff ${releaseBranch}` },
|
|
74
|
+
{ args: ["branch", "-d", releaseBranch], desc: `git branch -d ${releaseBranch}` },
|
|
75
|
+
{
|
|
76
|
+
args: ["push", "origin", "develop", trunk, version2],
|
|
77
|
+
desc: `git push origin develop ${trunk} ${version2}`
|
|
78
|
+
}
|
|
79
|
+
];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// src/commands/config.ts
|
|
29
83
|
var VALID_TYPES = ["gh", "azdo"];
|
|
30
84
|
var VALID_TECHNIQUES = ["label", "assignee", "title-contains"];
|
|
31
85
|
var VALID_EXECUTORS = ["claude", "codex"];
|
|
@@ -239,12 +293,24 @@ var configSetGitTrunkBranch = new Command("git-trunk-branch").description("Pin t
|
|
|
239
293
|
process.stdout.write(`git trunk branch set to: ${branch}
|
|
240
294
|
`);
|
|
241
295
|
});
|
|
242
|
-
var
|
|
296
|
+
var configSetGitReleaseFlow = new Command("git-release-flow").description("Pin the release procedure `publish-release` runs, instead of detecting it").argument("<value>", `Release flow: ${RELEASE_FLOWS.join(" | ")} (when the key is absent, the flow is detected from origin/develop)`).action((value) => {
|
|
297
|
+
const flow = value.trim();
|
|
298
|
+
if (!isReleaseFlow(flow)) {
|
|
299
|
+
process.stderr.write(`Error: invalid release flow "${value}". Must be one of: ${RELEASE_FLOWS.join(", ")}
|
|
300
|
+
`);
|
|
301
|
+
process.exit(1);
|
|
302
|
+
}
|
|
303
|
+
const current = readRawConfig();
|
|
304
|
+
writeConfig({ ...current, git: { ...current.git, releaseFlow: flow } });
|
|
305
|
+
process.stdout.write(`git release flow set to: ${flow}
|
|
306
|
+
`);
|
|
307
|
+
});
|
|
308
|
+
var configSet = new Command("set").description("Set a configuration value").addCommand(configSetType).addCommand(configSetIssueDiscoveryTechnique).addCommand(configSetIssueDiscoveryValue).addCommand(configSetClaudeSystemPrompt).addCommand(configSetAllowedUsers).addCommand(configSetAgentUser).addCommand(configSetDoWorkBaseBranch).addCommand(configSetDoWorkProtectedBranches).addCommand(configSetDoWorkExecutor).addCommand(configSetDoWorkModel).addCommand(configSetDoWorkEffort).addCommand(configSetDoWorkMaxRuns).addCommand(configSetDoWorkLockStaleMinutes).addCommand(configSetDoWorkDumpOnBlock).addCommand(configSetDoWorkPrompt).addCommand(configSetGitTrunkBranch).addCommand(configSetGitReleaseFlow);
|
|
243
309
|
var configCommand = new Command("config").description("Configure automata settings").addCommand(configSet).action(async () => {
|
|
244
310
|
const [{ render }, React, { ConfigWizard }] = await Promise.all([
|
|
245
311
|
import("ink"),
|
|
246
312
|
import("react"),
|
|
247
|
-
import("./ConfigWizard-
|
|
313
|
+
import("./ConfigWizard-RGXIC7OS.js")
|
|
248
314
|
]);
|
|
249
315
|
const { waitUntilExit } = render(React.createElement(ConfigWizard));
|
|
250
316
|
await waitUntilExit();
|
|
@@ -1147,17 +1213,44 @@ ${stderr.trim()}`);
|
|
|
1147
1213
|
}
|
|
1148
1214
|
return stdout.trim().length > 0;
|
|
1149
1215
|
}
|
|
1150
|
-
function
|
|
1216
|
+
function probeRemoteBranch(branch) {
|
|
1217
|
+
const ref = `refs/heads/${branch}`;
|
|
1218
|
+
const { status, stderr } = run2("git", ["ls-remote", "--exit-code", "origin", ref]);
|
|
1219
|
+
if (status === 0) return { ok: true, exists: true };
|
|
1220
|
+
if (status === 2) return { ok: true, exists: false };
|
|
1221
|
+
return {
|
|
1222
|
+
ok: false,
|
|
1223
|
+
message: stderr.trim() || `git ls-remote --exit-code origin ${ref} failed.`
|
|
1224
|
+
};
|
|
1225
|
+
}
|
|
1226
|
+
function resolveReleaseFlow() {
|
|
1227
|
+
const configured = readRawConfig().git?.releaseFlow;
|
|
1228
|
+
if (configured !== void 0) {
|
|
1229
|
+
if (!isReleaseFlow(configured)) return { ok: false, message: invalidReleaseFlowMessage(configured) };
|
|
1230
|
+
return { ok: true, flow: configured, source: "config" };
|
|
1231
|
+
}
|
|
1232
|
+
const develop = probeRemoteBranch("develop");
|
|
1233
|
+
if (!develop.ok) {
|
|
1234
|
+
return {
|
|
1235
|
+
ok: false,
|
|
1236
|
+
message: `Could not check whether origin has a 'develop' branch to choose the release flow.
|
|
1237
|
+
${develop.message}
|
|
1238
|
+
Set it explicitly with: automata config set git-release-flow <gitflow|trunk>`
|
|
1239
|
+
};
|
|
1240
|
+
}
|
|
1241
|
+
return develop.exists ? { ok: true, flow: "gitflow", source: "develop-present" } : { ok: true, flow: "trunk", source: "develop-absent" };
|
|
1242
|
+
}
|
|
1243
|
+
function checkReleasePreconditions(expectedBranch) {
|
|
1151
1244
|
let branch;
|
|
1152
1245
|
try {
|
|
1153
1246
|
branch = getCurrentBranch();
|
|
1154
1247
|
} catch (err) {
|
|
1155
1248
|
return { ok: false, message: err.message };
|
|
1156
1249
|
}
|
|
1157
|
-
if (branch !==
|
|
1250
|
+
if (branch !== expectedBranch) {
|
|
1158
1251
|
return {
|
|
1159
1252
|
ok: false,
|
|
1160
|
-
message: `publish-release must be run from the '
|
|
1253
|
+
message: `publish-release must be run from the '${expectedBranch}' branch (currently on '${branch}').`
|
|
1161
1254
|
};
|
|
1162
1255
|
}
|
|
1163
1256
|
if (hasUncommittedChanges()) {
|
|
@@ -1168,25 +1261,8 @@ function checkReleasePreconditions() {
|
|
|
1168
1261
|
}
|
|
1169
1262
|
return { ok: true };
|
|
1170
1263
|
}
|
|
1171
|
-
function publishRelease(version2, dryRun, trunk) {
|
|
1172
|
-
const
|
|
1173
|
-
const checkoutTrunk = localBranchExists(trunk) ? { args: ["checkout", trunk], desc: `git checkout ${trunk}` } : {
|
|
1174
|
-
args: ["checkout", "-b", trunk, `origin/${trunk}`],
|
|
1175
|
-
desc: `git checkout -b ${trunk} origin/${trunk}`
|
|
1176
|
-
};
|
|
1177
|
-
const steps = [
|
|
1178
|
-
{ args: ["checkout", "-b", releaseBranch], desc: `git checkout -b ${releaseBranch}` },
|
|
1179
|
-
checkoutTrunk,
|
|
1180
|
-
{ args: ["merge", "--no-ff", releaseBranch], desc: `git merge --no-ff ${releaseBranch}` },
|
|
1181
|
-
{ args: ["tag", version2], desc: `git tag ${version2}` },
|
|
1182
|
-
{ args: ["checkout", "develop"], desc: `git checkout develop` },
|
|
1183
|
-
{ args: ["merge", "--no-ff", releaseBranch], desc: `git merge --no-ff ${releaseBranch}` },
|
|
1184
|
-
{ args: ["branch", "-d", releaseBranch], desc: `git branch -d ${releaseBranch}` },
|
|
1185
|
-
{
|
|
1186
|
-
args: ["push", "origin", "develop", trunk, version2],
|
|
1187
|
-
desc: `git push origin develop ${trunk} ${version2}`
|
|
1188
|
-
}
|
|
1189
|
-
];
|
|
1264
|
+
function publishRelease(version2, dryRun, trunk, flow) {
|
|
1265
|
+
const steps = planRelease(flow, version2, trunk, flow === "gitflow" && localBranchExists(trunk));
|
|
1190
1266
|
for (const step of steps) {
|
|
1191
1267
|
if (dryRun) {
|
|
1192
1268
|
process.stdout.write(`[dry-run] ${step.desc}
|
|
@@ -1614,10 +1690,13 @@ var finishFeatureCmd = new Command2("finish-feature").description("Clean up a me
|
|
|
1614
1690
|
process.exit(1);
|
|
1615
1691
|
}
|
|
1616
1692
|
});
|
|
1617
|
-
var publishReleaseCmd = new Command2("publish-release").description("Execute the
|
|
1693
|
+
var publishReleaseCmd = new Command2("publish-release").description("Execute the release sequence (GitFlow or trunk-based) and push to origin").argument("[version]", "Release version in X.Y.Z format (auto-detected from the trunk tag if omitted)").option("--dry-run", "Print git commands without executing them").addHelpText(
|
|
1618
1694
|
"after",
|
|
1619
1695
|
`
|
|
1620
|
-
Release
|
|
1696
|
+
Release flow: git.releaseFlow in .automata/config.json if set, else detected \u2014
|
|
1697
|
+
gitflow when origin has a 'develop' branch, trunk when it does not.
|
|
1698
|
+
|
|
1699
|
+
GitFlow sequence (run from develop):
|
|
1621
1700
|
1. git checkout -b release/<version>
|
|
1622
1701
|
2. git checkout <trunk> && git merge --no-ff release/<version>
|
|
1623
1702
|
3. git tag <version>
|
|
@@ -1625,6 +1704,11 @@ Release sequence:
|
|
|
1625
1704
|
5. git branch -d release/<version>
|
|
1626
1705
|
6. git push origin develop <trunk> <version>
|
|
1627
1706
|
|
|
1707
|
+
Trunk sequence (run from <trunk>; it may be ahead of origin, not behind):
|
|
1708
|
+
1. git commit --allow-empty -m "chore(release): <version>"
|
|
1709
|
+
2. git tag <version>
|
|
1710
|
+
3. git push --atomic origin <trunk> <version>
|
|
1711
|
+
|
|
1628
1712
|
<trunk> is resolved from origin \u2014 git.trunkBranch in .automata/config.json if
|
|
1629
1713
|
set, else origin/HEAD, the remote's advertised HEAD, or a probe of main/master.
|
|
1630
1714
|
Tags are fetched from origin first, in --dry-run too, so the version a dry run
|
|
@@ -1635,16 +1719,10 @@ and the minor segment is incremented (e.g. 1.2.0 \u2192 1.3.0).
|
|
|
1635
1719
|
|
|
1636
1720
|
CHANGELOG.md must already carry a '## [<version>] - YYYY-MM-DD' section for the
|
|
1637
1721
|
version being released; the release is refused otherwise, in --dry-run too, and
|
|
1638
|
-
before any branch, merge or tag is created. A repository with no
|
|
1639
|
-
not subject to the check.`
|
|
1722
|
+
before any commit, branch, merge or tag is created. A repository with no
|
|
1723
|
+
CHANGELOG.md is not subject to the check.`
|
|
1640
1724
|
).action((version2, options) => {
|
|
1641
1725
|
const dryRun = options.dryRun ?? false;
|
|
1642
|
-
const preconditions = checkReleasePreconditions();
|
|
1643
|
-
if (!preconditions.ok) {
|
|
1644
|
-
process.stderr.write(`Error: ${preconditions.message}
|
|
1645
|
-
`);
|
|
1646
|
-
process.exit(1);
|
|
1647
|
-
}
|
|
1648
1726
|
const trunk = resolveTrunkBranch();
|
|
1649
1727
|
if (!trunk.ok) {
|
|
1650
1728
|
process.stderr.write(`Error: ${unresolvedTrunkMessage(trunk.attempted)}
|
|
@@ -1653,7 +1731,22 @@ not subject to the check.`
|
|
|
1653
1731
|
}
|
|
1654
1732
|
const trunkBranch = trunk.branch;
|
|
1655
1733
|
const trunkRef = `origin/${trunkBranch}`;
|
|
1734
|
+
const flow = resolveReleaseFlow();
|
|
1735
|
+
if (!flow.ok) {
|
|
1736
|
+
process.stderr.write(`Error: ${flow.message}
|
|
1737
|
+
`);
|
|
1738
|
+
process.exit(1);
|
|
1739
|
+
}
|
|
1740
|
+
const releaseFlow = flow.flow;
|
|
1741
|
+
const preconditions = checkReleasePreconditions(releaseFlow === "gitflow" ? "develop" : trunkBranch);
|
|
1742
|
+
if (!preconditions.ok) {
|
|
1743
|
+
process.stderr.write(`Error: ${preconditions.message}
|
|
1744
|
+
`);
|
|
1745
|
+
process.exit(1);
|
|
1746
|
+
}
|
|
1656
1747
|
process.stdout.write(`Trunk branch: ${trunkBranch} (${describeTrunkSource(trunk.source, trunkBranch)})
|
|
1748
|
+
`);
|
|
1749
|
+
process.stdout.write(`Release flow: ${releaseFlow} (${describeReleaseFlowSource(flow.source)})
|
|
1657
1750
|
`);
|
|
1658
1751
|
const fetched = fetchTrunkAndTags(trunkBranch);
|
|
1659
1752
|
if (!fetched.ok) {
|
|
@@ -1700,7 +1793,7 @@ ${fetched.message}
|
|
|
1700
1793
|
`);
|
|
1701
1794
|
}
|
|
1702
1795
|
try {
|
|
1703
|
-
publishRelease(resolvedVersion, dryRun, trunkBranch);
|
|
1796
|
+
publishRelease(resolvedVersion, dryRun, trunkBranch, releaseFlow);
|
|
1704
1797
|
} catch (err) {
|
|
1705
1798
|
process.stderr.write(`Error: ${err.message}
|
|
1706
1799
|
`);
|