@brainervirus/workit-claude-code 8.0.1 → 8.1.1
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/.claude-plugin/plugin.json +1 -1
- package/dist/workit-hook.js +37 -36
- package/dist/workit.js +937 -458
- package/package.json +3 -3
package/dist/workit.js
CHANGED
|
@@ -65,7 +65,7 @@ var package_default;
|
|
|
65
65
|
var init_package = __esm(() => {
|
|
66
66
|
package_default = {
|
|
67
67
|
name: "@brainervirus/workit-cli",
|
|
68
|
-
version: "8.
|
|
68
|
+
version: "8.1.1",
|
|
69
69
|
private: false,
|
|
70
70
|
description: "Workit CLI — setup wizard, doctor, and task control for agentic coding workflows",
|
|
71
71
|
keywords: [
|
|
@@ -103,9 +103,9 @@ var init_package = __esm(() => {
|
|
|
103
103
|
},
|
|
104
104
|
dependencies: {
|
|
105
105
|
"@babel/parser": "7.29.9",
|
|
106
|
-
"@brainervirus/workit-core": "^8.
|
|
107
|
-
"@brainervirus/workit-cursor": "^8.
|
|
108
|
-
"@brainervirus/workit-opencode": "^8.
|
|
106
|
+
"@brainervirus/workit-core": "^8.1.1",
|
|
107
|
+
"@brainervirus/workit-cursor": "^8.1.1",
|
|
108
|
+
"@brainervirus/workit-opencode": "^8.1.1",
|
|
109
109
|
"@inkjs/ui": "2.0.0",
|
|
110
110
|
ink: "7.1.1",
|
|
111
111
|
react: "19.2.8"
|
|
@@ -12831,12 +12831,13 @@ function pushForge(cwd, options = {}) {
|
|
|
12831
12831
|
};
|
|
12832
12832
|
const forge = deriveForge(url, options);
|
|
12833
12833
|
if (!forge) {
|
|
12834
|
-
const
|
|
12834
|
+
const parsed = parseRemoteUrl(url);
|
|
12835
|
+
const host = parsed?.host || null;
|
|
12835
12836
|
return {
|
|
12836
12837
|
ok: false,
|
|
12837
12838
|
code: "unavailable",
|
|
12838
|
-
error: host ? `cannot tell whether push host "${host}" is GitHub or GitLab` : `push URL ${redactRemote(url)} of remote "${remote}" is not a GitHub/GitLab URL`,
|
|
12839
|
-
unblock: host ? `
|
|
12839
|
+
error: host ? parsed?.protocol === "ssh" && !host.includes(".") ? `unsupported_forge: could not resolve ssh alias "${host}" to GitHub or GitLab; PR and CI verbs need one (workit git push still pushes with git)` : parsed?.protocol === "ssh" ? `unsupported_forge: ssh host "${host}" is not a GitHub/GitLab host or an alias workit can resolve; PR and CI verbs need one (workit git push still pushes with git)` : `unsupported_forge: cannot tell whether push host "${host}" is GitHub or GitLab; PR and CI verbs need one (workit git push still pushes with git)` : `push URL ${redactRemote(url)} of remote "${remote}" is not a GitHub/GitLab URL`,
|
|
12840
|
+
unblock: host ? `if ${host} is an ssh alias of GitHub/GitLab, add "Host ${host}" with "HostName github.com" (or gitlab.com) to ~/.ssh/config; if it is a self-hosted GitHub/GitLab, set github.host or gitlab.host in ~/.config/workit/vcs.json; other forges (Bitbucket, Gitea…) have no PR/CI verbs` : `git remote set-url --push ${remote} <ssh-or-https-url>`
|
|
12840
12841
|
};
|
|
12841
12842
|
}
|
|
12842
12843
|
return { ok: true, remote, url: redactRemote(url), forge };
|
|
@@ -12847,8 +12848,8 @@ function forgeConflict(derived, configuredProvider) {
|
|
|
12847
12848
|
return null;
|
|
12848
12849
|
return {
|
|
12849
12850
|
code: "blocked",
|
|
12850
|
-
error: `forge_mismatch: push remote ${derived.host} is ${derived.kind} but the workspace vcs.provider is ${configured}`,
|
|
12851
|
-
unblock: `
|
|
12851
|
+
error: `forge_mismatch: push remote ${derived.host} is ${derived.kind} but the workspace vcs.provider is ${configured}, so its account and grants do not apply; PR and CI verbs stop here (workit git push still pushes with git)`,
|
|
12852
|
+
unblock: `add a workspace whose glob matches only this repo, with "vcs": {"provider": "${derived.kind}"} (and its account), to ~/.config/workit/workspaces.json; the most specific glob wins`
|
|
12852
12853
|
};
|
|
12853
12854
|
}
|
|
12854
12855
|
var MAX_BUFFER, GIT_TIMEOUTS, git2 = (cwd, args, options = {}) => {
|
|
@@ -14919,60 +14920,6 @@ var init_task_context = __esm(() => {
|
|
|
14919
14920
|
privateKey = /-----BEGIN [^-]*PRIVATE KEY-----[\s\S]*?-----END [^-]*PRIVATE KEY-----/gi;
|
|
14920
14921
|
});
|
|
14921
14922
|
|
|
14922
|
-
// packages/workit-core/src/core/commit-flavors.ts
|
|
14923
|
-
var CONVENTIONAL_RE, TICKET_RE, GITMOJI_RE, matchCommitFlavor = (message, flavor, pattern) => {
|
|
14924
|
-
const subject = message.split(`
|
|
14925
|
-
`, 1)[0] ?? "";
|
|
14926
|
-
switch (flavor) {
|
|
14927
|
-
case "conventional":
|
|
14928
|
-
return CONVENTIONAL_RE.test(subject);
|
|
14929
|
-
case "gitmoji":
|
|
14930
|
-
return GITMOJI_RE.test(subject);
|
|
14931
|
-
case "ticket-prefix":
|
|
14932
|
-
return TICKET_RE.test(subject);
|
|
14933
|
-
case "freeform":
|
|
14934
|
-
return subject.trim().length > 0;
|
|
14935
|
-
case "custom":
|
|
14936
|
-
if (!pattern)
|
|
14937
|
-
return false;
|
|
14938
|
-
try {
|
|
14939
|
-
return new RegExp(pattern).test(subject);
|
|
14940
|
-
} catch {
|
|
14941
|
-
return false;
|
|
14942
|
-
}
|
|
14943
|
-
case "auto":
|
|
14944
|
-
return subject.trim().length > 0;
|
|
14945
|
-
}
|
|
14946
|
-
}, DETECTABLE, detectCommitFlavor = (subjects, threshold = 0.7) => {
|
|
14947
|
-
if (subjects.length === 0)
|
|
14948
|
-
return { flavor: null, confidence: 0 };
|
|
14949
|
-
const votes = new Map;
|
|
14950
|
-
for (const subject of subjects) {
|
|
14951
|
-
const hit = DETECTABLE.find((flavor) => matchCommitFlavor(subject, flavor));
|
|
14952
|
-
if (hit)
|
|
14953
|
-
votes.set(hit, (votes.get(hit) ?? 0) + 1);
|
|
14954
|
-
}
|
|
14955
|
-
let best = null;
|
|
14956
|
-
let count = 0;
|
|
14957
|
-
for (const [flavor, votesFor] of votes) {
|
|
14958
|
-
if (votesFor > count) {
|
|
14959
|
-
best = flavor;
|
|
14960
|
-
count = votesFor;
|
|
14961
|
-
}
|
|
14962
|
-
}
|
|
14963
|
-
const confidence = count / subjects.length;
|
|
14964
|
-
return best !== null && confidence >= threshold ? { flavor: best, confidence } : { flavor: null, confidence };
|
|
14965
|
-
};
|
|
14966
|
-
var init_commit_flavors = __esm(() => {
|
|
14967
|
-
CONVENTIONAL_RE = /^(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)(\(.+\))?(!)?: .+/;
|
|
14968
|
-
TICKET_RE = /^[A-Z]{2,}-\d+\b/;
|
|
14969
|
-
GITMOJI_RE = /^\p{Extended_Pictographic}/u;
|
|
14970
|
-
DETECTABLE = ["conventional", "gitmoji", "ticket-prefix"];
|
|
14971
|
-
});
|
|
14972
|
-
|
|
14973
|
-
// packages/workit-core/src/core/git.ts
|
|
14974
|
-
var init_git = () => {};
|
|
14975
|
-
|
|
14976
14923
|
// packages/workit-core/src/core/boundary.ts
|
|
14977
14924
|
var EVENT, errorDetail = (err) => {
|
|
14978
14925
|
if (err instanceof Error) {
|
|
@@ -15057,7 +15004,7 @@ var diagnosticLogger, setDiagnosticLogger = (logger) => {
|
|
|
15057
15004
|
if (!migrationFailed)
|
|
15058
15005
|
migratedDir = dir;
|
|
15059
15006
|
return dir;
|
|
15060
|
-
}, configDir = () => ensureConfigDir(), LOCALE_RE, DEFAULTS, readSafe = (p) => {
|
|
15007
|
+
}, configDir = () => ensureConfigDir(), LOCALE_RE, DEFAULTS, unchosenBranchPolicy, readSafe = (p) => {
|
|
15061
15008
|
try {
|
|
15062
15009
|
return readFileSync3(p, "utf8");
|
|
15063
15010
|
} catch {
|
|
@@ -15079,22 +15026,21 @@ var diagnosticLogger, setDiagnosticLogger = (logger) => {
|
|
|
15079
15026
|
const locale = LOCALE_RE.test(String(input.locale ?? "")) ? input.locale : DEFAULTS.locale;
|
|
15080
15027
|
const preset = Object.hasOwn(PRESETS, input.branchPolicy?.preset) ? input.branchPolicy?.preset : "gitflow";
|
|
15081
15028
|
const commitPreset = COMMIT_PRESETS.includes(String(input.commitPolicy?.preset ?? "")) ? input.commitPolicy?.preset : "conventional";
|
|
15082
|
-
|
|
15083
|
-
|
|
15084
|
-
|
|
15085
|
-
|
|
15086
|
-
|
|
15087
|
-
|
|
15088
|
-
|
|
15089
|
-
|
|
15090
|
-
|
|
15091
|
-
}
|
|
15092
|
-
commitPolicy: {
|
|
15093
|
-
preset: commitPreset,
|
|
15094
|
-
...typeof input.commitPolicy?.pattern === "string" ? { pattern: input.commitPolicy.pattern } : {}
|
|
15095
|
-
}
|
|
15029
|
+
const config = {
|
|
15030
|
+
locale,
|
|
15031
|
+
localeOptions: Array.isArray(input.localeOptions) ? input.localeOptions : DEFAULTS.localeOptions,
|
|
15032
|
+
branchPolicy: mergePreset(preset, {
|
|
15033
|
+
allowed: Array.isArray(input.branchPolicy?.allowed) ? input.branchPolicy.allowed : undefined,
|
|
15034
|
+
protectedNames: Array.isArray(input.branchPolicy?.protected) ? input.branchPolicy.protected : undefined
|
|
15035
|
+
}, DEFAULTS),
|
|
15036
|
+
commitPolicy: {
|
|
15037
|
+
preset: commitPreset,
|
|
15038
|
+
...typeof input.commitPolicy?.pattern === "string" ? { pattern: input.commitPolicy.pattern } : {}
|
|
15096
15039
|
}
|
|
15097
15040
|
};
|
|
15041
|
+
if (!Object.hasOwn(PRESETS, input.branchPolicy?.preset))
|
|
15042
|
+
unchosenBranchPolicy.add(config);
|
|
15043
|
+
return { status: "valid", path: file, config };
|
|
15098
15044
|
}, readConfigTyped = (dir) => {
|
|
15099
15045
|
const file = path13.join(dir ?? configDir(), "config.json");
|
|
15100
15046
|
return parseConfigResult(readSafe(file), file);
|
|
@@ -15108,9 +15054,11 @@ var diagnosticLogger, setDiagnosticLogger = (logger) => {
|
|
|
15108
15054
|
if (result.status === "malformed")
|
|
15109
15055
|
throw new Error(result.error);
|
|
15110
15056
|
return result.config ?? DEFAULTS;
|
|
15111
|
-
}, vcsConfigPath = () => process.env.WORKFLOW_VCS_CONFIG ?? path13.join(configDir(), "vcs.json"), branchGlobPattern = (glob) => new RegExp(`^${glob.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*")}$`, "i"), resolveBranchPolicy = (config, workspace) => {
|
|
15057
|
+
}, vcsConfigPath = () => process.env.WORKFLOW_VCS_CONFIG ?? path13.join(configDir(), "vcs.json"), branchGlobPattern = (glob) => new RegExp(`^${glob.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*")}$`, "i"), resolveBranchPolicy = (config, workspace, repoPreset) => {
|
|
15112
15058
|
const wp = workspace?.branchPolicy ?? {};
|
|
15113
|
-
const
|
|
15059
|
+
const workspacePreset = Object.hasOwn(PRESETS, wp.preset);
|
|
15060
|
+
const detected = !workspacePreset && unchosenBranchPolicy.has(config) ? repoPreset?.() ?? null : null;
|
|
15061
|
+
const preset = workspacePreset ? wp.preset : detected ?? config.branchPolicy?.preset ?? "gitflow";
|
|
15114
15062
|
const merged = mergePreset(preset, {
|
|
15115
15063
|
allowed: wp.allowed,
|
|
15116
15064
|
protectedNames: wp.protected
|
|
@@ -15119,9 +15067,10 @@ var diagnosticLogger, setDiagnosticLogger = (logger) => {
|
|
|
15119
15067
|
return {
|
|
15120
15068
|
preset,
|
|
15121
15069
|
allowed,
|
|
15122
|
-
protected: new Set(merged.protected.map((p) => p.toLowerCase())),
|
|
15070
|
+
protected: new Set([...merged.protected, ...detected ? PRESETS.gitflow.protected : []].map((p) => p.toLowerCase())),
|
|
15123
15071
|
integration: wp.integration === "merge" ? "merge" : "pr",
|
|
15124
|
-
defaultTargetBranch: preset === "github-flow" ? "main" : preset === "trunk-based" ? "master" : "develop"
|
|
15072
|
+
defaultTargetBranch: preset === "github-flow" ? "main" : preset === "trunk-based" ? "master" : "develop",
|
|
15073
|
+
detected: detected !== null
|
|
15125
15074
|
};
|
|
15126
15075
|
}, COMMIT_PRESETS, resolveCommitPolicy = (config, workspace) => {
|
|
15127
15076
|
const wp = workspace?.commitPolicy ?? {};
|
|
@@ -15155,6 +15104,7 @@ var init_config = __esm(() => {
|
|
|
15155
15104
|
},
|
|
15156
15105
|
commitPolicy: { preset: "conventional" }
|
|
15157
15106
|
};
|
|
15107
|
+
unchosenBranchPolicy = new WeakSet([DEFAULTS]);
|
|
15158
15108
|
COMMIT_PRESETS = [
|
|
15159
15109
|
"conventional",
|
|
15160
15110
|
"gitmoji",
|
|
@@ -15165,7 +15115,128 @@ var init_config = __esm(() => {
|
|
|
15165
15115
|
];
|
|
15166
15116
|
});
|
|
15167
15117
|
|
|
15118
|
+
// packages/workit-core/src/core/branch-policy.ts
|
|
15119
|
+
import { spawnSync as spawnSync7 } from "node:child_process";
|
|
15120
|
+
var PREFIXES, branchNames = (workspaceRoot) => {
|
|
15121
|
+
const r = spawnSync7("git", ["for-each-ref", "--format=%(refname)", "refs/heads", "refs/remotes/origin"], { cwd: workspaceRoot, encoding: "utf8" });
|
|
15122
|
+
if (r.status !== 0)
|
|
15123
|
+
return new Set;
|
|
15124
|
+
return new Set((r.stdout ?? "").split(`
|
|
15125
|
+
`).filter(Boolean).map((ref) => ref.startsWith("refs/heads/") ? ref.slice("refs/heads/".length) : ref.slice("refs/remotes/origin/".length)));
|
|
15126
|
+
}, detectBranchPolicy = (workspaceRoot) => {
|
|
15127
|
+
const names = branchNames(workspaceRoot);
|
|
15128
|
+
const develop = names.has("develop");
|
|
15129
|
+
const main = names.has("main");
|
|
15130
|
+
const master = names.has("master");
|
|
15131
|
+
const root = main ? "main" : master ? "master" : null;
|
|
15132
|
+
if (develop) {
|
|
15133
|
+
return {
|
|
15134
|
+
preset: "gitflow",
|
|
15135
|
+
developBranch: "develop",
|
|
15136
|
+
integration: "merge",
|
|
15137
|
+
protected: root ? [root, "develop"] : ["develop"],
|
|
15138
|
+
allowed: [...PRESETS.gitflow.allowed],
|
|
15139
|
+
prefixes: PREFIXES
|
|
15140
|
+
};
|
|
15141
|
+
}
|
|
15142
|
+
if (root === "main") {
|
|
15143
|
+
return {
|
|
15144
|
+
preset: "github-flow",
|
|
15145
|
+
developBranch: null,
|
|
15146
|
+
integration: "pr",
|
|
15147
|
+
protected: [root],
|
|
15148
|
+
allowed: ["*"],
|
|
15149
|
+
prefixes: PREFIXES
|
|
15150
|
+
};
|
|
15151
|
+
}
|
|
15152
|
+
if (root === "master") {
|
|
15153
|
+
return {
|
|
15154
|
+
preset: "trunk-based",
|
|
15155
|
+
developBranch: null,
|
|
15156
|
+
integration: "pr",
|
|
15157
|
+
protected: [root],
|
|
15158
|
+
allowed: ["*"],
|
|
15159
|
+
prefixes: PREFIXES
|
|
15160
|
+
};
|
|
15161
|
+
}
|
|
15162
|
+
return {
|
|
15163
|
+
preset: "gitflow",
|
|
15164
|
+
developBranch: null,
|
|
15165
|
+
integration: "merge",
|
|
15166
|
+
protected: [],
|
|
15167
|
+
allowed: [],
|
|
15168
|
+
prefixes: PREFIXES
|
|
15169
|
+
};
|
|
15170
|
+
}, repoBranchPreset = (workspaceRoot) => {
|
|
15171
|
+
const detected = detectBranchPolicy(workspaceRoot);
|
|
15172
|
+
return detected.developBranch || detected.allowed.length ? detected.preset : null;
|
|
15173
|
+
};
|
|
15174
|
+
var init_branch_policy = __esm(() => {
|
|
15175
|
+
init_config();
|
|
15176
|
+
PREFIXES = {
|
|
15177
|
+
feature: "feature/*",
|
|
15178
|
+
bugfix: "bugfix/*",
|
|
15179
|
+
release: "release/*",
|
|
15180
|
+
hotfix: "hotfix/*"
|
|
15181
|
+
};
|
|
15182
|
+
});
|
|
15183
|
+
|
|
15184
|
+
// packages/workit-core/src/core/commit-flavors.ts
|
|
15185
|
+
var CONVENTIONAL_RE, TICKET_RE, GITMOJI_RE, matchCommitFlavor = (message, flavor, pattern) => {
|
|
15186
|
+
const subject = message.split(`
|
|
15187
|
+
`, 1)[0] ?? "";
|
|
15188
|
+
switch (flavor) {
|
|
15189
|
+
case "conventional":
|
|
15190
|
+
return CONVENTIONAL_RE.test(subject);
|
|
15191
|
+
case "gitmoji":
|
|
15192
|
+
return GITMOJI_RE.test(subject);
|
|
15193
|
+
case "ticket-prefix":
|
|
15194
|
+
return TICKET_RE.test(subject);
|
|
15195
|
+
case "freeform":
|
|
15196
|
+
return subject.trim().length > 0;
|
|
15197
|
+
case "custom":
|
|
15198
|
+
if (!pattern)
|
|
15199
|
+
return false;
|
|
15200
|
+
try {
|
|
15201
|
+
return new RegExp(pattern).test(subject);
|
|
15202
|
+
} catch {
|
|
15203
|
+
return false;
|
|
15204
|
+
}
|
|
15205
|
+
case "auto":
|
|
15206
|
+
return subject.trim().length > 0;
|
|
15207
|
+
}
|
|
15208
|
+
}, DETECTABLE, detectCommitFlavor = (subjects, threshold = 0.7) => {
|
|
15209
|
+
if (subjects.length === 0)
|
|
15210
|
+
return { flavor: null, confidence: 0 };
|
|
15211
|
+
const votes = new Map;
|
|
15212
|
+
for (const subject of subjects) {
|
|
15213
|
+
const hit = DETECTABLE.find((flavor) => matchCommitFlavor(subject, flavor));
|
|
15214
|
+
if (hit)
|
|
15215
|
+
votes.set(hit, (votes.get(hit) ?? 0) + 1);
|
|
15216
|
+
}
|
|
15217
|
+
let best = null;
|
|
15218
|
+
let count = 0;
|
|
15219
|
+
for (const [flavor, votesFor] of votes) {
|
|
15220
|
+
if (votesFor > count) {
|
|
15221
|
+
best = flavor;
|
|
15222
|
+
count = votesFor;
|
|
15223
|
+
}
|
|
15224
|
+
}
|
|
15225
|
+
const confidence = count / subjects.length;
|
|
15226
|
+
return best !== null && confidence >= threshold ? { flavor: best, confidence } : { flavor: null, confidence };
|
|
15227
|
+
};
|
|
15228
|
+
var init_commit_flavors = __esm(() => {
|
|
15229
|
+
CONVENTIONAL_RE = /^(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)(\(.+\))?(!)?: .+/;
|
|
15230
|
+
TICKET_RE = /^[A-Z]{2,}-\d+\b/;
|
|
15231
|
+
GITMOJI_RE = /^\p{Extended_Pictographic}/u;
|
|
15232
|
+
DETECTABLE = ["conventional", "gitmoji", "ticket-prefix"];
|
|
15233
|
+
});
|
|
15234
|
+
|
|
15235
|
+
// packages/workit-core/src/core/git.ts
|
|
15236
|
+
var init_git = () => {};
|
|
15237
|
+
|
|
15168
15238
|
// packages/workit-core/src/core/workspaces.ts
|
|
15239
|
+
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
15169
15240
|
import { createHash as createHash8 } from "node:crypto";
|
|
15170
15241
|
import { readFileSync as readFileSync4, realpathSync as realpathSync3 } from "node:fs";
|
|
15171
15242
|
import path14 from "node:path";
|
|
@@ -15174,7 +15245,7 @@ function workspaceDefaultTarget(cwd, globalDefault) {
|
|
|
15174
15245
|
const wp = ws?.branchPolicy ?? {};
|
|
15175
15246
|
const hasWorkspacePolicy = typeof wp.preset === "string" && Object.hasOwn(PRESETS, wp.preset);
|
|
15176
15247
|
const selected = resolveRuntimeWorkspacePolicy(cwd, "branch", process.env.WORKFLOW_PROFILE?.trim() || undefined);
|
|
15177
|
-
const policyDefault = resolveBranchPolicy(readConfig(), selected.policy ? { branchPolicy: selected.policy } : null).defaultTargetBranch;
|
|
15248
|
+
const policyDefault = resolveBranchPolicy(readConfig(), selected.policy ? { branchPolicy: selected.policy } : null, () => repoBranchPreset(cwd)).defaultTargetBranch;
|
|
15178
15249
|
return String(ws?.vcs?.defaultTargetBranch ?? (hasWorkspacePolicy ? policyDefault : globalDefault ?? policyDefault) ?? "develop");
|
|
15179
15250
|
}
|
|
15180
15251
|
var nonBlank, branchPreset, commitPreset, workspaceBranchPolicySchema, workspaceCommitPolicySchema, workspaceProfileSchema, versionSourceSchema, releaseTrackSchema, workspaceConfigSchema, workspacesFileSchema, workspacesRevision = (contents) => createHash8("sha256").update(contents ?? "<missing>").digest("hex"), validateWorkspacesDocument = (parsed, file) => {
|
|
@@ -15293,6 +15364,46 @@ var nonBlank, branchPreset, commitPreset, workspaceBranchPolicySchema, workspace
|
|
|
15293
15364
|
};
|
|
15294
15365
|
}
|
|
15295
15366
|
return { ok: true };
|
|
15367
|
+
}, WorkspaceConfigError, mainCheckouts, mainCheckoutOf = (cwd) => {
|
|
15368
|
+
const cached = mainCheckouts.get(cwd);
|
|
15369
|
+
if (cached !== undefined)
|
|
15370
|
+
return cached;
|
|
15371
|
+
const env = { ...process.env };
|
|
15372
|
+
for (const name of ["GIT_DIR", "GIT_WORK_TREE", "GIT_COMMON_DIR", "GIT_INDEX_FILE"])
|
|
15373
|
+
delete env[name];
|
|
15374
|
+
const git = (args) => {
|
|
15375
|
+
const run = spawnSync8("git", args, {
|
|
15376
|
+
cwd,
|
|
15377
|
+
env,
|
|
15378
|
+
encoding: "utf8",
|
|
15379
|
+
timeout: 5000,
|
|
15380
|
+
windowsHide: true
|
|
15381
|
+
});
|
|
15382
|
+
return run.status === 0 ? run.stdout : null;
|
|
15383
|
+
};
|
|
15384
|
+
let main = null;
|
|
15385
|
+
const dirs = git(["rev-parse", "--path-format=absolute", "--git-dir", "--git-common-dir"]);
|
|
15386
|
+
const [gitDir, common] = (dirs ?? "").trim().split(/\r?\n/u);
|
|
15387
|
+
if (gitDir && common && path14.resolve(gitDir) !== path14.resolve(common)) {
|
|
15388
|
+
const listed = /^worktree (.+)$/mu.exec(git(["worktree", "list", "--porcelain"]) ?? "")?.[1];
|
|
15389
|
+
main = listed?.trim() || (path14.basename(common) === ".git" ? path14.dirname(common) : common);
|
|
15390
|
+
}
|
|
15391
|
+
mainCheckouts.set(cwd, main);
|
|
15392
|
+
return main;
|
|
15393
|
+
}, matchingEntries = (cwd, entries) => {
|
|
15394
|
+
const match = (where) => {
|
|
15395
|
+
const targets = [where, realpathOf(where)].map((value) => value.replaceAll("\\", "/"));
|
|
15396
|
+
return entries.filter((entry) => {
|
|
15397
|
+
const glob = entry.glob.replaceAll("\\", "/");
|
|
15398
|
+
const canonical = canonicalGlob(glob);
|
|
15399
|
+
return targets.some((target) => matchWorkspace(glob, target) || canonical !== glob && matchWorkspace(canonical, target));
|
|
15400
|
+
});
|
|
15401
|
+
};
|
|
15402
|
+
const direct = match(cwd);
|
|
15403
|
+
if (direct.length)
|
|
15404
|
+
return direct;
|
|
15405
|
+
const main = mainCheckoutOf(cwd);
|
|
15406
|
+
return main ? match(main) : direct;
|
|
15296
15407
|
}, realpathOf = (p) => {
|
|
15297
15408
|
const real = realpathSync3.native ?? realpathSync3;
|
|
15298
15409
|
try {
|
|
@@ -15314,41 +15425,27 @@ var nonBlank, branchPreset, commitPreset, workspaceBranchPolicySchema, workspace
|
|
|
15314
15425
|
if (named.length === 1)
|
|
15315
15426
|
return named[0];
|
|
15316
15427
|
if (named.length > 1)
|
|
15317
|
-
throw new
|
|
15428
|
+
throw new WorkspaceConfigError(`ambiguous workspace ${JSON.stringify(workspaceName)} for ${cwd}`);
|
|
15318
15429
|
if (matches.length > 0) {
|
|
15319
|
-
throw new
|
|
15430
|
+
throw new WorkspaceConfigError(`workspace ${JSON.stringify(workspaceName)} does not match ${cwd}; matching choices: ${matches.map((entry) => entry.name).join(", ")}`);
|
|
15320
15431
|
}
|
|
15321
|
-
throw new
|
|
15432
|
+
throw new WorkspaceConfigError(`workspace ${JSON.stringify(workspaceName)} does not match ${cwd}`);
|
|
15322
15433
|
}
|
|
15323
15434
|
if (matches.length < 2)
|
|
15324
15435
|
return matches[0];
|
|
15325
15436
|
const highestSpecificity = Math.max(...matches.map((entry) => globSpecificity(entry.glob)));
|
|
15326
15437
|
const mostSpecific = matches.filter((entry) => globSpecificity(entry.glob) === highestSpecificity);
|
|
15327
15438
|
if (mostSpecific.length > 1) {
|
|
15328
|
-
throw new
|
|
15439
|
+
throw new WorkspaceConfigError(`ambiguous workspace for ${cwd}; choose one of: ${mostSpecific.map((entry) => entry.name).join(", ")}`);
|
|
15329
15440
|
}
|
|
15330
15441
|
return mostSpecific[0];
|
|
15331
15442
|
}, resolveWorkspaceFromEntries = (cwd, entries, workspaceName) => {
|
|
15332
|
-
const
|
|
15333
|
-
const matches = [];
|
|
15334
|
-
for (const ws of entries) {
|
|
15335
|
-
const glob = ws.glob.replaceAll("\\", "/");
|
|
15336
|
-
const canonical = canonicalGlob(glob);
|
|
15337
|
-
let matched = false;
|
|
15338
|
-
for (const target of targets) {
|
|
15339
|
-
if (matchWorkspace(glob, target) || canonical !== glob && matchWorkspace(canonical, target)) {
|
|
15340
|
-
matched = true;
|
|
15341
|
-
break;
|
|
15342
|
-
}
|
|
15343
|
-
}
|
|
15344
|
-
if (matched)
|
|
15345
|
-
matches.push(ws);
|
|
15346
|
-
}
|
|
15443
|
+
const matches = matchingEntries(cwd, entries);
|
|
15347
15444
|
return selectWorkspaceMatch(matches, cwd, workspaceName) ?? null;
|
|
15348
15445
|
}, resolveWorkspaceFrom = (cwd, dir, workspaceName) => {
|
|
15349
15446
|
const result = readWorkspacesResult(dir);
|
|
15350
15447
|
if (result.status === "malformed" || result.status === "invalid")
|
|
15351
|
-
throw new
|
|
15448
|
+
throw new WorkspaceConfigError(result.error);
|
|
15352
15449
|
return resolveWorkspaceFromEntries(cwd, result.entries, workspaceName);
|
|
15353
15450
|
}, runtimeWorkspacePolicySchema = (kind) => {
|
|
15354
15451
|
const key = kind === "branch" ? "branchPolicy" : kind === "commit" ? "commitPolicy" : "vcs";
|
|
@@ -15374,31 +15471,26 @@ var nonBlank, branchPreset, commitPreset, workspaceBranchPolicySchema, workspace
|
|
|
15374
15471
|
} catch (error) {
|
|
15375
15472
|
if (error.code === "ENOENT")
|
|
15376
15473
|
return null;
|
|
15377
|
-
throw new
|
|
15474
|
+
throw new WorkspaceConfigError(`${file} could not be read: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
15378
15475
|
}
|
|
15379
15476
|
let parsed;
|
|
15380
15477
|
try {
|
|
15381
15478
|
parsed = JSON.parse(raw);
|
|
15382
15479
|
} catch {
|
|
15383
|
-
throw new
|
|
15480
|
+
throw new WorkspaceConfigError(`${file} is not valid JSON`);
|
|
15384
15481
|
}
|
|
15385
15482
|
const validated = runtimeWorkspaceIndexSchema.safeParse(parsed);
|
|
15386
15483
|
if (!validated.success) {
|
|
15387
15484
|
const detail = validated.error.issues.map((issue) => `${issue.path.map(String).join(".") || "workspaces"}: ${issue.message}`).join("; ");
|
|
15388
|
-
throw new
|
|
15485
|
+
throw new WorkspaceConfigError(`${file} has invalid workspace matching data: ${detail}`);
|
|
15389
15486
|
}
|
|
15390
15487
|
const entries = validated.data.workspaces;
|
|
15391
15488
|
for (const entry of entries ?? []) {
|
|
15392
15489
|
const glob = validateWorkspaceGlob(entry.glob);
|
|
15393
15490
|
if (!glob.ok)
|
|
15394
|
-
throw new
|
|
15491
|
+
throw new WorkspaceConfigError(`${file} workspace ${entry.name}: ${glob.error}`);
|
|
15395
15492
|
}
|
|
15396
|
-
const
|
|
15397
|
-
const matches = (entries ?? []).filter((entry) => {
|
|
15398
|
-
const glob = entry.glob.replaceAll("\\", "/");
|
|
15399
|
-
const canonical = canonicalGlob(glob);
|
|
15400
|
-
return targets.some((target) => matchWorkspace(glob, target) || canonical !== glob && matchWorkspace(canonical, target));
|
|
15401
|
-
});
|
|
15493
|
+
const matches = matchingEntries(cwd, entries ?? []);
|
|
15402
15494
|
const workspaceName = process.env.WORKFLOW_WORKSPACE_NAME?.trim() || undefined;
|
|
15403
15495
|
const selected = selectWorkspaceMatch(matches, cwd, workspaceName);
|
|
15404
15496
|
return selected ? { file, selected } : null;
|
|
@@ -15413,7 +15505,7 @@ var nonBlank, branchPreset, commitPreset, workspaceBranchPolicySchema, workspace
|
|
|
15413
15505
|
const policy = runtimeWorkspacePolicySchema(kind).safeParse(selected);
|
|
15414
15506
|
if (!policy.success) {
|
|
15415
15507
|
const detail = policy.error.issues.map((issue) => `${issue.path.map(String).join(".") || "workspace"}: ${issue.message}`).join("; ");
|
|
15416
|
-
throw new
|
|
15508
|
+
throw new WorkspaceConfigError(`${file} has invalid ${kind} policy configuration: ${detail}`);
|
|
15417
15509
|
}
|
|
15418
15510
|
return policy.data;
|
|
15419
15511
|
}, resolveRuntimeWorkspacePolicy = (cwd, kind, requestedProfile) => {
|
|
@@ -15423,10 +15515,10 @@ var nonBlank, branchPreset, commitPreset, workspaceBranchPolicySchema, workspace
|
|
|
15423
15515
|
const key = kind === "branch" ? "branchPolicy" : "commitPolicy";
|
|
15424
15516
|
const profileName = requestedProfile?.trim() || candidate.defaultProfile;
|
|
15425
15517
|
if (candidate.defaultProfile && !Object.hasOwn(candidate.profiles ?? {}, candidate.defaultProfile))
|
|
15426
|
-
throw new
|
|
15518
|
+
throw new WorkspaceConfigError(`workspace ${JSON.stringify(candidate.name)} has no profile ${JSON.stringify(candidate.defaultProfile)}`);
|
|
15427
15519
|
const profile = profileName ? candidate.profiles?.[profileName] : undefined;
|
|
15428
15520
|
if (profileName && !profile)
|
|
15429
|
-
throw new
|
|
15521
|
+
throw new WorkspaceConfigError(`workspace ${JSON.stringify(candidate.name)} has no profile ${JSON.stringify(profileName)}`);
|
|
15430
15522
|
const profilePolicy = profile?.[key];
|
|
15431
15523
|
const workspacePolicy = candidate[key];
|
|
15432
15524
|
return {
|
|
@@ -15456,7 +15548,7 @@ var nonBlank, branchPreset, commitPreset, workspaceBranchPolicySchema, workspace
|
|
|
15456
15548
|
commitPolicy: profile?.commitPolicy ? `profile:${profileName}` : workspace.commitPolicy ? `workspace:${workspace.name}` : "user-default"
|
|
15457
15549
|
}
|
|
15458
15550
|
};
|
|
15459
|
-
}, resolveWorkspacePolicy = (config, workspace, profileName) => {
|
|
15551
|
+
}, resolveWorkspacePolicy = (config, workspace, profileName, repoPreset) => {
|
|
15460
15552
|
const selected = workspace ? resolveWorkspaceProfile(workspace, profileName) : {
|
|
15461
15553
|
status: "resolved",
|
|
15462
15554
|
profileName: null,
|
|
@@ -15469,7 +15561,7 @@ var nonBlank, branchPreset, commitPreset, workspaceBranchPolicySchema, workspace
|
|
|
15469
15561
|
status: "resolved",
|
|
15470
15562
|
workspace: selected.workspace,
|
|
15471
15563
|
profileName: selected.profileName,
|
|
15472
|
-
branchPolicy: resolveBranchPolicy(config, selected.workspace),
|
|
15564
|
+
branchPolicy: resolveBranchPolicy(config, selected.workspace, repoPreset),
|
|
15473
15565
|
commitPolicy: resolveCommitPolicy(config, selected.workspace),
|
|
15474
15566
|
provenance: selected.provenance
|
|
15475
15567
|
};
|
|
@@ -15477,6 +15569,7 @@ var nonBlank, branchPreset, commitPreset, workspaceBranchPolicySchema, workspace
|
|
|
15477
15569
|
var init_workspaces = __esm(() => {
|
|
15478
15570
|
init_zod();
|
|
15479
15571
|
init_config();
|
|
15572
|
+
init_branch_policy();
|
|
15480
15573
|
nonBlank = string2().refine((value) => value.trim().length > 0, "must not be blank");
|
|
15481
15574
|
branchPreset = custom((value) => typeof value === "string" && Object.hasOwn(PRESETS, value), "unsupported branch preset");
|
|
15482
15575
|
commitPreset = custom((value) => typeof value === "string" && COMMIT_PRESETS.includes(value), "unsupported commit preset");
|
|
@@ -15596,6 +15689,10 @@ var init_workspaces = __esm(() => {
|
|
|
15596
15689
|
});
|
|
15597
15690
|
UNSUPPORTED_GLOB = /[?[\]{}]/;
|
|
15598
15691
|
UNSUPPORTED_EXTGLOB = /[@+*]\(|!/;
|
|
15692
|
+
WorkspaceConfigError = class WorkspaceConfigError extends Error {
|
|
15693
|
+
name = "WorkspaceConfigError";
|
|
15694
|
+
};
|
|
15695
|
+
mainCheckouts = new Map;
|
|
15599
15696
|
runtimeWorkspaceIndexSchema = object({
|
|
15600
15697
|
workspaces: array(object({ name: nonBlank, glob: nonBlank }).passthrough()).optional()
|
|
15601
15698
|
}).passthrough();
|
|
@@ -15603,7 +15700,7 @@ var init_workspaces = __esm(() => {
|
|
|
15603
15700
|
|
|
15604
15701
|
// packages/workit-core/src/core/release-tracks.ts
|
|
15605
15702
|
import { createHash as createHash9 } from "node:crypto";
|
|
15606
|
-
import { spawnSync as
|
|
15703
|
+
import { spawnSync as spawnSync9 } from "node:child_process";
|
|
15607
15704
|
import { readFileSync as readFileSync5 } from "node:fs";
|
|
15608
15705
|
function readReleaseTracks(raw) {
|
|
15609
15706
|
const out = { tracks: [], issues: [], error: null, protectedBranches: [] };
|
|
@@ -16005,7 +16102,7 @@ var ReleaseTrackError, KNOWN_FIELDS, BRANCH_FIELDS, PLACEHOLDER, HAS_PLACEHOLDER
|
|
|
16005
16102
|
const remote = remotes.find((name) => bare.startsWith(`${name}/`));
|
|
16006
16103
|
return remote ? bare.slice(remote.length + 1) : bare;
|
|
16007
16104
|
}, MAX_RECORDED_HOPS = 10, git4 = (cwd, args) => {
|
|
16008
|
-
const run =
|
|
16105
|
+
const run = spawnSync9("git", args, {
|
|
16009
16106
|
cwd,
|
|
16010
16107
|
encoding: "utf8",
|
|
16011
16108
|
timeout: 20000,
|
|
@@ -16066,7 +16163,7 @@ var init_release_tracks = __esm(() => {
|
|
|
16066
16163
|
// packages/workit-core/src/core/vcs-config.ts
|
|
16067
16164
|
import fs12 from "node:fs";
|
|
16068
16165
|
import path15 from "node:path";
|
|
16069
|
-
import { spawnSync as
|
|
16166
|
+
import { spawnSync as spawnSync10 } from "node:child_process";
|
|
16070
16167
|
function vcsConfig(mode, cwd, options = {}) {
|
|
16071
16168
|
const ws = resolveRuntimeWorkspaceVcs(vcsCwd(cwd));
|
|
16072
16169
|
const wsVcs = ws?.vcs ?? {};
|
|
@@ -16149,7 +16246,7 @@ function vcsConfig(mode, cwd, options = {}) {
|
|
|
16149
16246
|
return out;
|
|
16150
16247
|
}
|
|
16151
16248
|
var workspacesPath = () => path15.join(configDir(), "workspaces.json"), vcsCwd = (cwd) => cwd ?? process.env.WORKFLOW_WORKSPACE_ROOT ?? process.cwd(), remoteProvider = (cwd) => {
|
|
16152
|
-
const r =
|
|
16249
|
+
const r = spawnSync10("git", ["remote", "get-url", "origin"], { cwd, encoding: "utf8" });
|
|
16153
16250
|
if (r.status !== 0)
|
|
16154
16251
|
return null;
|
|
16155
16252
|
const url = (r.stdout ?? "").trim();
|
|
@@ -16199,7 +16296,7 @@ function effectiveWorkspacePolicy(workspaceRoot, kind, profileName) {
|
|
|
16199
16296
|
const selected = resolveRuntimeWorkspacePolicy(workspaceRoot, kind, profileName?.trim() || process.env.WORKFLOW_PROFILE?.trim() || undefined);
|
|
16200
16297
|
const config = readConfig();
|
|
16201
16298
|
return kind === "branch" ? {
|
|
16202
|
-
branchPolicy: withReleaseTracks(workspaceRoot, resolveBranchPolicy(config, selected.policy ? { branchPolicy: selected.policy } : null)),
|
|
16299
|
+
branchPolicy: withReleaseTracks(workspaceRoot, resolveBranchPolicy(config, selected.policy ? { branchPolicy: selected.policy } : null, () => repoBranchPreset(workspaceRoot))),
|
|
16203
16300
|
provenance: { branchPolicy: selected.source }
|
|
16204
16301
|
} : {
|
|
16205
16302
|
commitPolicy: resolveCommitPolicy(config, selected.policy ? { commitPolicy: selected.policy } : null),
|
|
@@ -16356,6 +16453,7 @@ var withReleaseTracks = (workspaceRoot, policy) => {
|
|
|
16356
16453
|
return dirty ? "carry" : "clean";
|
|
16357
16454
|
};
|
|
16358
16455
|
var init_branch = __esm(() => {
|
|
16456
|
+
init_branch_policy();
|
|
16359
16457
|
init_commit_flavors();
|
|
16360
16458
|
init_git();
|
|
16361
16459
|
init_config();
|
|
@@ -18464,7 +18562,7 @@ var init_pr_body = __esm(() => {
|
|
|
18464
18562
|
|
|
18465
18563
|
// packages/workit-core/src/core/pr-create.ts
|
|
18466
18564
|
import path19 from "node:path";
|
|
18467
|
-
import { spawnSync as
|
|
18565
|
+
import { spawnSync as spawnSync11 } from "node:child_process";
|
|
18468
18566
|
import { pathToFileURL } from "node:url";
|
|
18469
18567
|
var safePushUrl = (remote) => {
|
|
18470
18568
|
const raw = remote.trim();
|
|
@@ -18492,7 +18590,7 @@ var safePushUrl = (remote) => {
|
|
|
18492
18590
|
const target = safePushUrl(remote);
|
|
18493
18591
|
if (!target)
|
|
18494
18592
|
return false;
|
|
18495
|
-
const result =
|
|
18593
|
+
const result = spawnSync11("git", ["config", "--get-regexp", "^url\\..*\\.(insteadof|pushinsteadof)$"], { cwd, encoding: "utf8" });
|
|
18496
18594
|
if (result.status !== 0 && result.status !== 1)
|
|
18497
18595
|
return false;
|
|
18498
18596
|
const prefixes = (result.stdout ?? "").split(/\r?\n/u).map((line) => line.trim().slice(line.trim().indexOf(" ") + 1)).filter(Boolean);
|
|
@@ -62780,7 +62878,7 @@ var init_support_matrix = __esm(() => {
|
|
|
62780
62878
|
});
|
|
62781
62879
|
|
|
62782
62880
|
// packages/workit-cli/src/admin/host-install.ts
|
|
62783
|
-
import { spawnSync as
|
|
62881
|
+
import { spawnSync as spawnSync12 } from "node:child_process";
|
|
62784
62882
|
import {
|
|
62785
62883
|
accessSync,
|
|
62786
62884
|
constants as constants2,
|
|
@@ -63051,7 +63149,7 @@ function runHostCommand(step, context = {}) {
|
|
|
63051
63149
|
stderr: `Cannot safely execute batch shim ${step.command}; resolve its Node.js CLI entrypoint and retry`
|
|
63052
63150
|
};
|
|
63053
63151
|
}
|
|
63054
|
-
const result =
|
|
63152
|
+
const result = spawnSync12(step.command, step.args, {
|
|
63055
63153
|
cwd: step.cwd,
|
|
63056
63154
|
env: (() => {
|
|
63057
63155
|
const home = context.home ?? context.env?.HOME ?? os6.homedir();
|
|
@@ -63256,7 +63354,7 @@ var init_skill_manifests = __esm(() => {
|
|
|
63256
63354
|
});
|
|
63257
63355
|
|
|
63258
63356
|
// packages/workit-cli/src/admin/doctor.ts
|
|
63259
|
-
import { spawnSync as
|
|
63357
|
+
import { spawnSync as spawnSync13 } from "node:child_process";
|
|
63260
63358
|
import {
|
|
63261
63359
|
existsSync as existsSync13,
|
|
63262
63360
|
lstatSync as lstatSync4,
|
|
@@ -63340,7 +63438,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
63340
63438
|
}
|
|
63341
63439
|
return false;
|
|
63342
63440
|
}, versionOf = (bin, env, timeout) => {
|
|
63343
|
-
const r =
|
|
63441
|
+
const r = spawnSync13(bin, ["--version"], { encoding: "utf8", env, timeout });
|
|
63344
63442
|
if (r.error)
|
|
63345
63443
|
return null;
|
|
63346
63444
|
return (r.stdout ?? "").trim();
|
|
@@ -63589,7 +63687,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
63589
63687
|
`)) {
|
|
63590
63688
|
return false;
|
|
63591
63689
|
}
|
|
63592
|
-
return
|
|
63690
|
+
return spawnSync13(runtime, ["--check", entry], { encoding: "utf8", env }).status === 0;
|
|
63593
63691
|
} catch {
|
|
63594
63692
|
return false;
|
|
63595
63693
|
}
|
|
@@ -63803,7 +63901,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
63803
63901
|
if (!cmd)
|
|
63804
63902
|
return null;
|
|
63805
63903
|
try {
|
|
63806
|
-
const r =
|
|
63904
|
+
const r = spawnSync13(cmd, ["view", pkgName, "version"], {
|
|
63807
63905
|
encoding: "utf8",
|
|
63808
63906
|
timeout: 20000,
|
|
63809
63907
|
env: res.env
|
|
@@ -64087,6 +64185,16 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
64087
64185
|
detail: `malformed config: ${bad.join(", ")}`,
|
|
64088
64186
|
fix: `Repair the malformed config in ${bad[0]}`
|
|
64089
64187
|
};
|
|
64188
|
+
}, branchPresetLine = (res, workspace) => {
|
|
64189
|
+
try {
|
|
64190
|
+
const resolved = resolveWorkspacePolicy(readConfigFromDir(res.configDir), workspace, undefined, () => repoBranchPreset(res.cwd));
|
|
64191
|
+
if (resolved.status === "invalid")
|
|
64192
|
+
return [];
|
|
64193
|
+
const { preset, detected } = resolved.branchPolicy;
|
|
64194
|
+
return [`preset: ${detected ? `detected (${preset})` : preset}`];
|
|
64195
|
+
} catch {
|
|
64196
|
+
return [];
|
|
64197
|
+
}
|
|
64090
64198
|
}, checkWorkspaceMismatch = (res) => {
|
|
64091
64199
|
const result = readWorkspacesResult(res.configDir);
|
|
64092
64200
|
const file = result.path;
|
|
@@ -64094,7 +64202,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
64094
64202
|
return {
|
|
64095
64203
|
id: "workspace_mismatch",
|
|
64096
64204
|
status: "pass",
|
|
64097
|
-
detail: "no workspaces configured"
|
|
64205
|
+
detail: ["no workspaces configured", ...branchPresetLine(res, null)].join("; ")
|
|
64098
64206
|
};
|
|
64099
64207
|
if (result.status === "malformed" || result.status === "invalid")
|
|
64100
64208
|
return {
|
|
@@ -64118,7 +64226,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
64118
64226
|
const matched = `current directory matches workspace "${match.name}"`;
|
|
64119
64227
|
const tracks = releaseTracksReport(res.cwd);
|
|
64120
64228
|
const lines = describeReleaseTracks(tracks);
|
|
64121
|
-
const detail = [matched, ...lines].join("; ");
|
|
64229
|
+
const detail = [matched, ...branchPresetLine(res, match), ...lines].join("; ");
|
|
64122
64230
|
if (tracks.error)
|
|
64123
64231
|
return {
|
|
64124
64232
|
id: "workspace_mismatch",
|
|
@@ -64232,7 +64340,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
64232
64340
|
}, defaultIdentityProbes = (env) => ({
|
|
64233
64341
|
origin: (cwd) => {
|
|
64234
64342
|
try {
|
|
64235
|
-
const out =
|
|
64343
|
+
const out = spawnSync13("git", ["remote", "get-url", "origin"], {
|
|
64236
64344
|
cwd,
|
|
64237
64345
|
encoding: "utf8",
|
|
64238
64346
|
env
|
|
@@ -64244,7 +64352,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
64244
64352
|
},
|
|
64245
64353
|
ghLogin: (e) => {
|
|
64246
64354
|
try {
|
|
64247
|
-
const out =
|
|
64355
|
+
const out = spawnSync13("gh", ["api", "user", "--jq", ".login"], { encoding: "utf8", env: e });
|
|
64248
64356
|
const login = (out.stdout ?? "").trim();
|
|
64249
64357
|
return out.status === 0 && login ? login : null;
|
|
64250
64358
|
} catch {
|
|
@@ -64253,7 +64361,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
64253
64361
|
},
|
|
64254
64362
|
glabLogin: (e) => {
|
|
64255
64363
|
try {
|
|
64256
|
-
const out =
|
|
64364
|
+
const out = spawnSync13("glab", ["api", "user", "--jq", ".username"], {
|
|
64257
64365
|
encoding: "utf8",
|
|
64258
64366
|
env: e
|
|
64259
64367
|
});
|
|
@@ -64265,7 +64373,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
64265
64373
|
},
|
|
64266
64374
|
sshLogin: (host) => {
|
|
64267
64375
|
try {
|
|
64268
|
-
const out =
|
|
64376
|
+
const out = spawnSync13("ssh", ["-o", "BatchMode=yes", "-o", "ConnectTimeout=5", "-T", `git@${host}`], {
|
|
64269
64377
|
encoding: "utf8",
|
|
64270
64378
|
env
|
|
64271
64379
|
});
|
|
@@ -64460,6 +64568,7 @@ var init_doctor = __esm(() => {
|
|
|
64460
64568
|
init_runtime_identity();
|
|
64461
64569
|
init_boundary();
|
|
64462
64570
|
init_release_tracks();
|
|
64571
|
+
init_branch_policy();
|
|
64463
64572
|
init_config();
|
|
64464
64573
|
init_package_root();
|
|
64465
64574
|
init_registration();
|
|
@@ -65473,69 +65582,6 @@ var init_setup = __esm(() => {
|
|
|
65473
65582
|
SETUP_OVERRIDES = [...YT_OVERRIDES, ...VCS_OVERRIDES];
|
|
65474
65583
|
});
|
|
65475
65584
|
|
|
65476
|
-
// packages/workit-core/src/core/branch-policy.ts
|
|
65477
|
-
import { spawnSync as spawnSync12 } from "node:child_process";
|
|
65478
|
-
var PREFIXES, detectBranchPolicy = (workspaceRoot) => {
|
|
65479
|
-
const branchExists = (name) => {
|
|
65480
|
-
const r = spawnSync12("git", ["branch", "--list", name], {
|
|
65481
|
-
cwd: workspaceRoot,
|
|
65482
|
-
encoding: "utf8"
|
|
65483
|
-
});
|
|
65484
|
-
return r.status === 0 && (r.stdout ?? "").trim() !== "";
|
|
65485
|
-
};
|
|
65486
|
-
const develop = branchExists("develop");
|
|
65487
|
-
const main = branchExists("main");
|
|
65488
|
-
const master = branchExists("master");
|
|
65489
|
-
const root = main ? "main" : master ? "master" : null;
|
|
65490
|
-
if (develop) {
|
|
65491
|
-
return {
|
|
65492
|
-
preset: "gitflow",
|
|
65493
|
-
developBranch: "develop",
|
|
65494
|
-
integration: "merge",
|
|
65495
|
-
protected: root ? [root, "develop"] : ["develop"],
|
|
65496
|
-
allowed: [...PRESETS.gitflow.allowed],
|
|
65497
|
-
prefixes: PREFIXES
|
|
65498
|
-
};
|
|
65499
|
-
}
|
|
65500
|
-
if (root === "main") {
|
|
65501
|
-
return {
|
|
65502
|
-
preset: "github-flow",
|
|
65503
|
-
developBranch: null,
|
|
65504
|
-
integration: "pr",
|
|
65505
|
-
protected: [root],
|
|
65506
|
-
allowed: ["*"],
|
|
65507
|
-
prefixes: PREFIXES
|
|
65508
|
-
};
|
|
65509
|
-
}
|
|
65510
|
-
if (root === "master") {
|
|
65511
|
-
return {
|
|
65512
|
-
preset: "trunk-based",
|
|
65513
|
-
developBranch: null,
|
|
65514
|
-
integration: "pr",
|
|
65515
|
-
protected: [root],
|
|
65516
|
-
allowed: ["*"],
|
|
65517
|
-
prefixes: PREFIXES
|
|
65518
|
-
};
|
|
65519
|
-
}
|
|
65520
|
-
return {
|
|
65521
|
-
preset: "gitflow",
|
|
65522
|
-
developBranch: null,
|
|
65523
|
-
integration: "merge",
|
|
65524
|
-
protected: [],
|
|
65525
|
-
allowed: [],
|
|
65526
|
-
prefixes: PREFIXES
|
|
65527
|
-
};
|
|
65528
|
-
};
|
|
65529
|
-
var init_branch_policy = __esm(() => {
|
|
65530
|
-
init_config();
|
|
65531
|
-
PREFIXES = {
|
|
65532
|
-
feature: "feature/*",
|
|
65533
|
-
bugfix: "bugfix/*",
|
|
65534
|
-
release: "release/*",
|
|
65535
|
-
hotfix: "hotfix/*"
|
|
65536
|
-
};
|
|
65537
|
-
});
|
|
65538
|
-
|
|
65539
65585
|
// packages/workit-core/src/core/workspace-branch-policy.ts
|
|
65540
65586
|
import { mkdirSync as mkdirSync6, readFileSync as readFileSync15, renameSync as renameSync4, rmSync as rmSync4, writeFileSync as writeFileSync8 } from "node:fs";
|
|
65541
65587
|
import path31 from "node:path";
|
|
@@ -68624,7 +68670,7 @@ function Screen({
|
|
|
68624
68670
|
};
|
|
68625
68671
|
const checkout = resolveBasePath(draft.values);
|
|
68626
68672
|
const workspace = resolveWorkspaceFromEntries(checkout, draft.values.workspaces);
|
|
68627
|
-
const resolved = resolveWorkspacePolicy(config, workspace);
|
|
68673
|
+
const resolved = resolveWorkspacePolicy(config, workspace, undefined, () => repoBranchPreset(checkout));
|
|
68628
68674
|
if (resolved.status === "invalid")
|
|
68629
68675
|
return { error: resolved.error };
|
|
68630
68676
|
return {
|
|
@@ -69485,7 +69531,7 @@ var init_init = __esm(() => {
|
|
|
69485
69531
|
});
|
|
69486
69532
|
|
|
69487
69533
|
// packages/workit-cli/src/upgrade.ts
|
|
69488
|
-
import { spawnSync as
|
|
69534
|
+
import { spawnSync as spawnSync14 } from "node:child_process";
|
|
69489
69535
|
import {
|
|
69490
69536
|
existsSync as existsSync17,
|
|
69491
69537
|
mkdtempSync as mkdtempSync3,
|
|
@@ -69841,7 +69887,7 @@ async function runLaunchCommand(argv, deps = {}) {
|
|
|
69841
69887
|
}
|
|
69842
69888
|
if (deps.run)
|
|
69843
69889
|
return deps.run(host, hostArgs).status ?? 1;
|
|
69844
|
-
return
|
|
69890
|
+
return spawnSync14(launch.command, launch.args, {
|
|
69845
69891
|
stdio: "inherit",
|
|
69846
69892
|
env
|
|
69847
69893
|
}).status ?? 1;
|
|
@@ -70023,7 +70069,7 @@ var init_launch = __esm(() => {
|
|
|
70023
70069
|
});
|
|
70024
70070
|
|
|
70025
70071
|
// packages/workit-core/src/knowledge.ts
|
|
70026
|
-
import { spawnSync as
|
|
70072
|
+
import { spawnSync as spawnSync15 } from "node:child_process";
|
|
70027
70073
|
import fs17 from "node:fs";
|
|
70028
70074
|
import path36 from "node:path";
|
|
70029
70075
|
function lintKnowledge(root) {
|
|
@@ -70144,7 +70190,7 @@ var AGENTS_BYTE_BUDGET, STEERING_FILES, NEED_BASED_FILES, STANDARDS_FILE = "CODI
|
|
|
70144
70190
|
return "unreadable";
|
|
70145
70191
|
}
|
|
70146
70192
|
}, trackedTopLevel = (root) => {
|
|
70147
|
-
const run =
|
|
70193
|
+
const run = spawnSync15("git", ["ls-files", "-z", "--cached"], {
|
|
70148
70194
|
cwd: root,
|
|
70149
70195
|
encoding: "utf8",
|
|
70150
70196
|
maxBuffer: 256 * 1024 * 1024,
|
|
@@ -70523,7 +70569,7 @@ var init_redact = __esm(() => {
|
|
|
70523
70569
|
});
|
|
70524
70570
|
|
|
70525
70571
|
// packages/workit-core/src/checks.ts
|
|
70526
|
-
import { spawn, spawnSync as
|
|
70572
|
+
import { spawn, spawnSync as spawnSync16 } from "node:child_process";
|
|
70527
70573
|
import { createHash as createHash14 } from "node:crypto";
|
|
70528
70574
|
import fs18 from "node:fs";
|
|
70529
70575
|
import os13 from "node:os";
|
|
@@ -70702,7 +70748,7 @@ function runCheckCommand(argv, options) {
|
|
|
70702
70748
|
if (!child?.pid)
|
|
70703
70749
|
return;
|
|
70704
70750
|
if (windows)
|
|
70705
|
-
|
|
70751
|
+
spawnSync16("taskkill", ["/pid", String(child.pid), "/T", "/F"], {
|
|
70706
70752
|
stdio: "ignore",
|
|
70707
70753
|
windowsHide: true
|
|
70708
70754
|
});
|
|
@@ -71424,7 +71470,47 @@ var init_check = __esm(() => {
|
|
|
71424
71470
|
var success2 = (data) => ({ ok: true, data }), failure3 = (code, error, unblock) => ({ ok: false, code, error, ...unblock ? { unblock } : {} });
|
|
71425
71471
|
|
|
71426
71472
|
// packages/workit-core/src/git/ops.ts
|
|
71427
|
-
import { spawnSync as
|
|
71473
|
+
import { spawnSync as spawnSync17 } from "node:child_process";
|
|
71474
|
+
import { existsSync as existsSync18, readFileSync as readFileSync18 } from "node:fs";
|
|
71475
|
+
import path39 from "node:path";
|
|
71476
|
+
function operationInProgress(cwd) {
|
|
71477
|
+
const markers = [
|
|
71478
|
+
["rebase-merge", "rebase"],
|
|
71479
|
+
["rebase-apply", "rebase"],
|
|
71480
|
+
["MERGE_HEAD", "merge"],
|
|
71481
|
+
["CHERRY_PICK_HEAD", "cherry-pick"],
|
|
71482
|
+
["REVERT_HEAD", "revert"]
|
|
71483
|
+
];
|
|
71484
|
+
const run = git5(cwd, ["rev-parse", ...markers.flatMap(([name]) => ["--git-path", name])]);
|
|
71485
|
+
if (!run.ok)
|
|
71486
|
+
return null;
|
|
71487
|
+
const found = run.stdout.split(`
|
|
71488
|
+
`).map((line) => path39.resolve(cwd, line.trim()));
|
|
71489
|
+
const conflicted = () => git5(cwd, ["ls-files", "-u"]).stdout.trim() !== "";
|
|
71490
|
+
for (const [index, [, operation]] of markers.entries()) {
|
|
71491
|
+
const marker = found[index];
|
|
71492
|
+
if (!marker || !existsSync18(marker))
|
|
71493
|
+
continue;
|
|
71494
|
+
if (index === 1 && existsSync18(path39.join(marker, "applying")))
|
|
71495
|
+
return { operation: "am", commitAllowed: false, branch: null };
|
|
71496
|
+
if (index === 0 && existsSync18(path39.join(marker, "amend")) && !conflicted()) {
|
|
71497
|
+
let headName = "";
|
|
71498
|
+
try {
|
|
71499
|
+
headName = readFileSync18(path39.join(marker, "head-name"), "utf8").trim();
|
|
71500
|
+
} catch {}
|
|
71501
|
+
const branch = headName.startsWith("refs/heads/") ? headName.slice("refs/heads/".length) : null;
|
|
71502
|
+
return { operation, commitAllowed: branch !== null, branch };
|
|
71503
|
+
}
|
|
71504
|
+
if ((operation === "revert" || operation === "merge") && !conflicted())
|
|
71505
|
+
return { operation, commitAllowed: true, branch: null };
|
|
71506
|
+
return { operation, commitAllowed: false, branch: null };
|
|
71507
|
+
}
|
|
71508
|
+
return null;
|
|
71509
|
+
}
|
|
71510
|
+
function inProgressFailure(operation, verb) {
|
|
71511
|
+
const command = operation === "am" ? "git am" : `git ${operation}`;
|
|
71512
|
+
return failure3("blocked", `${operation.replace("-", "_")}_in_progress: a ${operation === "am" ? "git am" : operation} is in progress; finish or abort it before workit can ${verb}`, `resolve the conflicts, git add <files>, then ${command} --continue # or ${command} --abort to return to where you started`);
|
|
71513
|
+
}
|
|
71428
71514
|
function dirtEntries(cwd) {
|
|
71429
71515
|
const run = git5(cwd, ["status", "--porcelain=v1", "-z", "--untracked-files=all"]);
|
|
71430
71516
|
if (!run.ok)
|
|
@@ -71444,6 +71530,9 @@ function dirtEntries(cwd) {
|
|
|
71444
71530
|
function gitBranch(cwd, input) {
|
|
71445
71531
|
if (!headSha(cwd) && currentBranch(cwd) === null)
|
|
71446
71532
|
return failure3("invalid_input", "not inside a git repository with a commit");
|
|
71533
|
+
const progress = operationInProgress(cwd);
|
|
71534
|
+
if (progress)
|
|
71535
|
+
return inProgressFailure(progress.operation, "create a branch");
|
|
71447
71536
|
const explicitBase = input.base?.trim() || null;
|
|
71448
71537
|
const resolved = vcsConfig("resolve", cwd, {
|
|
71449
71538
|
track: input.track ?? null,
|
|
@@ -71548,53 +71637,156 @@ function lintCommitMessage(cwd, message) {
|
|
|
71548
71637
|
correction: `use a ${detected} commit subject`
|
|
71549
71638
|
};
|
|
71550
71639
|
}
|
|
71640
|
+
function messageTrailers(cwd, message) {
|
|
71641
|
+
const run = git5(cwd, [...TRAILER_CONFIG, "interpret-trailers", "--parse", "--no-divider"], {
|
|
71642
|
+
input: `${message}
|
|
71643
|
+
`
|
|
71644
|
+
});
|
|
71645
|
+
return run.ok ? run.stdout.split(`
|
|
71646
|
+
`).filter((line) => line.trim()) : [];
|
|
71647
|
+
}
|
|
71648
|
+
function withTrailers(cwd, message, trailers) {
|
|
71649
|
+
if (!trailers.length)
|
|
71650
|
+
return message;
|
|
71651
|
+
const run = git5(cwd, [
|
|
71652
|
+
...TRAILER_CONFIG,
|
|
71653
|
+
"interpret-trailers",
|
|
71654
|
+
"--no-divider",
|
|
71655
|
+
"--if-exists",
|
|
71656
|
+
"addIfDifferent",
|
|
71657
|
+
...trailers.flatMap((trailer) => ["--trailer", trailer])
|
|
71658
|
+
], { input: `${message}
|
|
71659
|
+
` });
|
|
71660
|
+
return run.ok ? run.stdout.trim() : `${message}
|
|
71661
|
+
|
|
71662
|
+
${trailers.join(`
|
|
71663
|
+
`)}`;
|
|
71664
|
+
}
|
|
71665
|
+
function branchesContaining(cwd, sha) {
|
|
71666
|
+
const run = git5(cwd, [
|
|
71667
|
+
"for-each-ref",
|
|
71668
|
+
"--format=%(refname)",
|
|
71669
|
+
"--contains",
|
|
71670
|
+
sha,
|
|
71671
|
+
"refs/heads",
|
|
71672
|
+
"refs/remotes"
|
|
71673
|
+
]);
|
|
71674
|
+
const names = new Set;
|
|
71675
|
+
for (const ref of run.ok ? run.stdout.split(`
|
|
71676
|
+
`) : []) {
|
|
71677
|
+
if (ref.startsWith("refs/heads/"))
|
|
71678
|
+
names.add(ref.slice("refs/heads/".length));
|
|
71679
|
+
else if (ref.startsWith("refs/remotes/")) {
|
|
71680
|
+
const name = ref.split("/").slice(3).join("/");
|
|
71681
|
+
if (name && name !== "HEAD")
|
|
71682
|
+
names.add(name);
|
|
71683
|
+
}
|
|
71684
|
+
}
|
|
71685
|
+
return names;
|
|
71686
|
+
}
|
|
71687
|
+
function sharedBranchContaining(cwd, sha, branch) {
|
|
71688
|
+
const resolved = vcsConfig("resolve", cwd);
|
|
71689
|
+
const defaults = new Set(resolved.ok === false ? [] : [resolved.defaultTargetBranch, resolved.baseBranch].map((value) => String(value ?? "")).filter(Boolean));
|
|
71690
|
+
for (const name of branchesContaining(cwd, sha))
|
|
71691
|
+
if (name !== branch && (defaults.has(name) || isProtectedTarget(cwd, name)))
|
|
71692
|
+
return name;
|
|
71693
|
+
return null;
|
|
71694
|
+
}
|
|
71551
71695
|
function gitCommit(cwd, input) {
|
|
71552
|
-
const
|
|
71553
|
-
if (!
|
|
71554
|
-
return failure3("invalid_input", "a commit message is required (-m <msg>)");
|
|
71696
|
+
const given = input.message?.trim() ?? "";
|
|
71697
|
+
if (!given && !input.amend)
|
|
71698
|
+
return failure3("invalid_input", "a commit message is required (-m <msg> or -F <file>)");
|
|
71555
71699
|
const paths = input.paths ?? [];
|
|
71556
71700
|
if (input.all && paths.length)
|
|
71557
71701
|
return failure3("invalid_input", "pass --all or paths, not both");
|
|
71558
|
-
const
|
|
71702
|
+
const progress = operationInProgress(cwd);
|
|
71703
|
+
if (progress && !progress.commitAllowed)
|
|
71704
|
+
return inProgressFailure(progress.operation, "commit");
|
|
71705
|
+
const branch = progress?.branch ?? currentBranch(cwd);
|
|
71559
71706
|
if (!branch)
|
|
71560
71707
|
return failure3("invalid_input", "HEAD is detached; commit on a branch (workit git branch <name>)");
|
|
71561
71708
|
if (isProtectedTarget(cwd, branch))
|
|
71562
71709
|
return failure3("blocked", `protected_branch: ${branch} is protected by the workspace branch policy; workit never commits to it`, "workit git branch <feature/name> # then commit there");
|
|
71563
|
-
const
|
|
71564
|
-
if (!
|
|
71565
|
-
return failure3("
|
|
71710
|
+
const previous = input.amend ? headSha(cwd) : null;
|
|
71711
|
+
if (input.amend && !previous)
|
|
71712
|
+
return failure3("invalid_input", `nothing to amend: ${branch} has no commit yet`);
|
|
71713
|
+
if (previous) {
|
|
71714
|
+
const shared = sharedBranchContaining(cwd, previous, branch);
|
|
71715
|
+
if (shared)
|
|
71716
|
+
return failure3("blocked", `amend_shared_commit: ${previous.slice(0, 12)} is already on ${shared}, so it is not ${branch}'s own commit; workit never rewrites it`, "workit git commit -m '<msg>' -- <paths> # add a new commit instead");
|
|
71717
|
+
}
|
|
71718
|
+
let message = given;
|
|
71719
|
+
let carried = [];
|
|
71720
|
+
if (given) {
|
|
71721
|
+
const lint = lintCommitMessage(cwd, given);
|
|
71722
|
+
if (!lint.ok)
|
|
71723
|
+
return failure3("blocked", lint.error, lint.correction);
|
|
71724
|
+
}
|
|
71725
|
+
if (previous) {
|
|
71726
|
+
const kept = git5(cwd, ["log", "-1", "--format=%B", previous]);
|
|
71727
|
+
if (!kept.ok)
|
|
71728
|
+
return failure3("failed", `could not read the message of ${branch}: ${gitError(kept)}`);
|
|
71729
|
+
if (given)
|
|
71730
|
+
carried = messageTrailers(cwd, kept.stdout.trim());
|
|
71731
|
+
else
|
|
71732
|
+
message = kept.stdout.trim();
|
|
71733
|
+
}
|
|
71566
71734
|
const session = input.actor.session;
|
|
71567
71735
|
if (session && !SESSION_SAFE.test(session))
|
|
71568
71736
|
return failure3("invalid_input", "WORKIT_SESSION_ID must be 1-128 characters of [A-Za-z0-9_.:@/+-]");
|
|
71569
|
-
const
|
|
71570
|
-
|
|
71571
|
-
|
|
71737
|
+
const changeRequired = !input.amend && !input.allowEmpty;
|
|
71738
|
+
const emptyAmend = previous !== null && git5(cwd, ["rev-parse", "--verify", "-q", `${previous}^{tree}`]).stdout.trim() === git5(cwd, ["rev-parse", "--verify", "-q", `${previous}~1^{tree}`]).stdout.trim();
|
|
71739
|
+
const args = ["commit", "--no-edit"];
|
|
71740
|
+
if (input.amend)
|
|
71741
|
+
args.push("--amend");
|
|
71742
|
+
if (input.allowEmpty || emptyAmend)
|
|
71743
|
+
args.push("--allow-empty");
|
|
71744
|
+
args.push("-m", withTrailers(cwd, message, [...carried, ...session ? [`Workit-Session: ${session}`] : []]));
|
|
71572
71745
|
if (paths.length) {
|
|
71573
71746
|
if (paths.some((value) => value.startsWith("-") || !value.trim()))
|
|
71574
71747
|
return failure3("invalid_input", "paths must not be empty or start with -");
|
|
71575
|
-
const
|
|
71576
|
-
|
|
71577
|
-
|
|
71748
|
+
const tracked = new Set(nulList(git5(cwd, ["ls-files", "-z", "--", ...paths]).stdout));
|
|
71749
|
+
const addable = paths.filter((value) => existsSync18(path39.resolve(cwd, value)) || [...tracked].some((file) => file === value || file.startsWith(`${value}/`)));
|
|
71750
|
+
if (addable.length) {
|
|
71751
|
+
const added = git5(cwd, ["add", "-A", "--", ...addable]);
|
|
71752
|
+
if (!added.ok)
|
|
71753
|
+
return failure3("failed", `git add failed: ${gitError(added)}`);
|
|
71754
|
+
}
|
|
71755
|
+
if (changeRequired && git5(cwd, ["diff", "--cached", "--quiet", "HEAD", "--", ...paths]).ok)
|
|
71756
|
+
return nothingToCommit(`${paths.join(", ")} has no change to commit`);
|
|
71578
71757
|
args.push("--", ...paths);
|
|
71579
71758
|
} else if (input.all) {
|
|
71580
71759
|
const added = git5(cwd, ["add", "-A"]);
|
|
71581
71760
|
if (!added.ok)
|
|
71582
71761
|
return failure3("failed", `git add failed: ${gitError(added)}`);
|
|
71583
|
-
|
|
71762
|
+
if (changeRequired && git5(cwd, ["diff", "--cached", "--quiet"]).ok)
|
|
71763
|
+
return nothingToCommit("the working tree is clean");
|
|
71764
|
+
} else if (changeRequired) {
|
|
71584
71765
|
const staged = git5(cwd, ["diff", "--cached", "--name-only", "-z"]);
|
|
71585
71766
|
if (staged.ok && !staged.stdout.replaceAll("\x00", "")) {
|
|
71586
71767
|
const dirt = dirtEntries(cwd);
|
|
71587
|
-
|
|
71768
|
+
if (!dirt.length)
|
|
71769
|
+
return nothingToCommit("the working tree is clean");
|
|
71770
|
+
return failure3("blocked", `nothing_staged: nothing is staged and workit never commits unrelated changes implicitly (${dirt.length} changed: ${dirt.slice(0, 5).map((entry) => entry.slice(3)).join(", ")}${dirt.length > 5 ? ", …" : ""})`, `workit git commit -m '<msg>' -- <paths> # or --all to commit every change`);
|
|
71588
71771
|
}
|
|
71589
71772
|
}
|
|
71590
|
-
const committed = git5(cwd, args, {
|
|
71773
|
+
const committed = git5(cwd, args, {
|
|
71774
|
+
timeoutMs: COMMIT_TIMEOUT_MS,
|
|
71775
|
+
env: input.env
|
|
71776
|
+
});
|
|
71591
71777
|
if (!committed.ok)
|
|
71592
|
-
return failure3("failed", `git commit failed
|
|
71778
|
+
return failure3("failed", `git commit failed (a hook or git refused it):
|
|
71779
|
+
${gitOutput(committed)}`);
|
|
71593
71780
|
const sha = headSha(cwd);
|
|
71594
71781
|
if (!sha)
|
|
71595
71782
|
return failure3("failed", "the commit did not resolve");
|
|
71596
|
-
const
|
|
71783
|
+
const merge = git5(cwd, ["rev-parse", "-q", "--verify", `${sha}^2`]).ok;
|
|
71784
|
+
const files = nulList(git5(cwd, merge ? ["diff", "--name-only", "-z", `${sha}^1`, sha] : ["diff-tree", "--root", "--no-commit-id", "--name-only", "-r", "-z", sha]).stdout);
|
|
71597
71785
|
const leftDirty = dirtEntries(cwd).length;
|
|
71786
|
+
const notes = [];
|
|
71787
|
+
const upstream = `refs/remotes/${pushRemoteName(cwd, branch) ?? "origin"}/${branch}`;
|
|
71788
|
+
if (previous && git5(cwd, ["merge-base", "--is-ancestor", previous, upstream]).ok)
|
|
71789
|
+
notes.push(`the amended commit ${previous.slice(0, 12)} was already pushed (${upstream.slice("refs/remotes/".length)}); publish the rewrite with: workit git push --force-with-lease`);
|
|
71598
71790
|
const listed = [];
|
|
71599
71791
|
let size = 0;
|
|
71600
71792
|
for (const file of files) {
|
|
@@ -71614,7 +71806,8 @@ function gitCommit(cwd, input) {
|
|
|
71614
71806
|
subject: (message.split(`
|
|
71615
71807
|
`, 1)[0] ?? "").slice(0, 200),
|
|
71616
71808
|
files: listed,
|
|
71617
|
-
fileCount: files.length
|
|
71809
|
+
fileCount: files.length,
|
|
71810
|
+
...previous ? { amended: previous } : {}
|
|
71618
71811
|
});
|
|
71619
71812
|
return success2({
|
|
71620
71813
|
sha,
|
|
@@ -71623,10 +71816,15 @@ function gitCommit(cwd, input) {
|
|
|
71623
71816
|
files,
|
|
71624
71817
|
leftDirty,
|
|
71625
71818
|
session,
|
|
71819
|
+
amended: previous,
|
|
71820
|
+
notes,
|
|
71626
71821
|
recorded: row.ok ? { id: String(row.value.id) } : { error: row.error }
|
|
71627
71822
|
});
|
|
71628
71823
|
}
|
|
71629
71824
|
function pushPreflight(cwd, options = {}) {
|
|
71825
|
+
const progress = operationInProgress(cwd);
|
|
71826
|
+
if (progress)
|
|
71827
|
+
return inProgressFailure(progress.operation, "push");
|
|
71630
71828
|
const branch = options.branch ?? currentBranch(cwd);
|
|
71631
71829
|
if (!branch)
|
|
71632
71830
|
return failure3("invalid_input", "HEAD is detached; push a branch");
|
|
@@ -71665,11 +71863,35 @@ function recordedRemoteTip(cwd, plan) {
|
|
|
71665
71863
|
return;
|
|
71666
71864
|
}
|
|
71667
71865
|
function includesRemoteTip(cwd, branch, tip) {
|
|
71668
|
-
|
|
71866
|
+
const ref = `refs/heads/${branch}`;
|
|
71867
|
+
if (git5(cwd, ["merge-base", "--is-ancestor", tip, ref]).ok)
|
|
71868
|
+
return true;
|
|
71869
|
+
const reflog = git5(cwd, ["reflog", "show", "--format=%H", ref, "--"]);
|
|
71870
|
+
const former = [
|
|
71871
|
+
...new Set((reflog.ok ? reflog.stdout : "").split(`
|
|
71872
|
+
`).map((line) => line.trim()).filter(Boolean))
|
|
71873
|
+
];
|
|
71874
|
+
if (former.includes(tip))
|
|
71669
71875
|
return true;
|
|
71670
|
-
|
|
71671
|
-
|
|
71672
|
-
|
|
71876
|
+
if (!former.length)
|
|
71877
|
+
return false;
|
|
71878
|
+
const outside = git5(cwd, ["rev-list", "-n", "1", "--stdin"], {
|
|
71879
|
+
input: `${tip}
|
|
71880
|
+
${former.map((sha) => `^${sha}`).join(`
|
|
71881
|
+
`)}
|
|
71882
|
+
`
|
|
71883
|
+
});
|
|
71884
|
+
return outside.ok && !outside.stdout.trim();
|
|
71885
|
+
}
|
|
71886
|
+
function patchEquivalent(cwd, branch, tip) {
|
|
71887
|
+
const ref = `refs/heads/${branch}`;
|
|
71888
|
+
const merges = git5(cwd, ["rev-list", "--count", "--merges", `${ref}..${tip}`]);
|
|
71889
|
+
if (!merges.ok || merges.stdout.trim() !== "0")
|
|
71890
|
+
return false;
|
|
71891
|
+
const cherry = git5(cwd, ["cherry", ref, tip]);
|
|
71892
|
+
const lines = cherry.ok ? cherry.stdout.split(`
|
|
71893
|
+
`).filter(Boolean) : [];
|
|
71894
|
+
return lines.length > 0 && lines.every((line) => line.startsWith("-"));
|
|
71673
71895
|
}
|
|
71674
71896
|
function executePush(cwd, plan, input) {
|
|
71675
71897
|
const before = remoteRefTip(cwd, plan.rawUrl, `refs/heads/${plan.branch}`);
|
|
@@ -71704,9 +71926,17 @@ ${run.stdout}`;
|
|
|
71704
71926
|
return failure3("unavailable", `git push to ${plan.url} timed out`);
|
|
71705
71927
|
if (/stale info/iu.test(text))
|
|
71706
71928
|
return failure3("blocked", `lease_mismatch: ${plan.remote}/${plan.branch} moved while pushing`, `git fetch ${plan.remote} ${plan.branch} && git rebase ${plan.remote}/${plan.branch} # or merge it; then: workit git push`);
|
|
71929
|
+
const rejected = /non-fast-forward|fetch first|\[rejected\]/iu.test(text);
|
|
71930
|
+
const integrated = rejected && before.sha !== null && includesRemoteTip(cwd, plan.branch, before.sha);
|
|
71931
|
+
const equivalent = rejected && !integrated && before.sha !== null && patchEquivalent(cwd, plan.branch, before.sha);
|
|
71932
|
+
if (before.sha !== null && (integrated || equivalent)) {
|
|
71933
|
+
const lease = recordedRemoteTip(cwd, plan) === before.sha ? "workit git push --force-with-lease" : `workit git push --force-with-lease --expect ${before.sha}`;
|
|
71934
|
+
return failure3("failed", integrated ? `non_fast_forward: local ${plan.branch} was rewritten (amend or rebase) after ${plan.remote}/${plan.branch} (${before.sha.slice(0, 12)}) was pushed; that tip is already part of the local history, so do not fetch and rebase` : `non_fast_forward: every commit on ${plan.remote}/${plan.branch} (${before.sha.slice(0, 12)}) has a patch-equivalent commit in local ${plan.branch} (rebased elsewhere); fetching and rebasing would apply them twice`, integrated ? lease : `${lease} --overwrite-unintegrated # after checking ${plan.remote}/${plan.branch} holds nothing else`);
|
|
71935
|
+
}
|
|
71707
71936
|
if (/non-fast-forward|fetch first|\[rejected\]/iu.test(text))
|
|
71708
71937
|
return failure3("failed", `non_fast_forward: ${plan.remote}/${plan.branch} has commits this branch does not`, `git fetch ${plan.remote} && git rebase ${plan.remote}/${plan.branch} # or, if the rewrite is intended: workit git push --force-with-lease`);
|
|
71709
|
-
return failure3("failed", `git push to ${plan.url} failed:
|
|
71938
|
+
return failure3("failed", `git push to ${plan.url} failed:
|
|
71939
|
+
${gitOutput(run)}`);
|
|
71710
71940
|
}
|
|
71711
71941
|
pushed = true;
|
|
71712
71942
|
}
|
|
@@ -71728,7 +71958,8 @@ ${run.stdout}`;
|
|
|
71728
71958
|
url: plan.url,
|
|
71729
71959
|
previous: before.sha,
|
|
71730
71960
|
pushed,
|
|
71731
|
-
forced
|
|
71961
|
+
forced,
|
|
71962
|
+
...input.identity ? { identity: input.identity } : {}
|
|
71732
71963
|
});
|
|
71733
71964
|
return success2({
|
|
71734
71965
|
remote: plan.remote,
|
|
@@ -71765,10 +71996,14 @@ function deleteRemoteBranch(cwd, target, branch, expectedTip, options = {}) {
|
|
|
71765
71996
|
return { ok: true };
|
|
71766
71997
|
const text = `${run.stderr}
|
|
71767
71998
|
${run.stdout}`;
|
|
71768
|
-
return /stale info|cannot lock ref|fetch first|non-fast-forward/iu.test(text) ? {
|
|
71999
|
+
return /stale info|cannot lock ref|fetch first|non-fast-forward/iu.test(text) ? {
|
|
72000
|
+
ok: false,
|
|
72001
|
+
lease: true,
|
|
72002
|
+
error: "the remote branch tip changed before deletion"
|
|
72003
|
+
} : { ok: false, lease: false, error: gitError(run) };
|
|
71769
72004
|
}
|
|
71770
72005
|
var COMMIT_TIMEOUT_MS, PUSH_TIMEOUT_MS = 120000, git5 = (cwd, args, options = {}) => {
|
|
71771
|
-
const result =
|
|
72006
|
+
const result = spawnSync17("git", args, {
|
|
71772
72007
|
cwd,
|
|
71773
72008
|
encoding: "utf8",
|
|
71774
72009
|
env: { ...options.env ?? process.env, GIT_TERMINAL_PROMPT: "0" },
|
|
@@ -71776,7 +72011,7 @@ var COMMIT_TIMEOUT_MS, PUSH_TIMEOUT_MS = 120000, git5 = (cwd, args, options = {}
|
|
|
71776
72011
|
timeout: options.timeoutMs ?? GIT_TIMEOUTS.local,
|
|
71777
72012
|
killSignal: "SIGKILL",
|
|
71778
72013
|
windowsHide: true,
|
|
71779
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
72014
|
+
...options.input === undefined ? { stdio: ["ignore", "pipe", "pipe"] } : { input: options.input, stdio: ["pipe", "pipe", "pipe"] }
|
|
71780
72015
|
});
|
|
71781
72016
|
return {
|
|
71782
72017
|
ok: result.status === 0,
|
|
@@ -71788,7 +72023,22 @@ var COMMIT_TIMEOUT_MS, PUSH_TIMEOUT_MS = 120000, git5 = (cwd, args, options = {}
|
|
|
71788
72023
|
const lines = redactText(`${run.stderr}
|
|
71789
72024
|
${run.stdout ?? ""}`).split(/\r?\n/u).map((value) => value.trim()).filter((value) => value && !value.startsWith("hint:"));
|
|
71790
72025
|
return lines.slice(-max).join(" / ").slice(0, 400) || "git failed";
|
|
71791
|
-
},
|
|
72026
|
+
}, OUTPUT_LINES = 200, OUTPUT_LINE_CHARS = 1000, gitOutput = (run) => {
|
|
72027
|
+
const lines = redactText(`${run.stderr}
|
|
72028
|
+
${run.stdout ?? ""}`).split(/\r?\n/u).map((value) => value.trimEnd()).filter((value) => value.trim() && !value.trimStart().startsWith("hint:")).map((value) => value.length > OUTPUT_LINE_CHARS ? `${value.slice(0, OUTPUT_LINE_CHARS)}…` : value);
|
|
72029
|
+
if (!lines.length)
|
|
72030
|
+
return "git failed";
|
|
72031
|
+
if (lines.length <= OUTPUT_LINES)
|
|
72032
|
+
return lines.join(`
|
|
72033
|
+
`);
|
|
72034
|
+
const half = OUTPUT_LINES / 2;
|
|
72035
|
+
return [
|
|
72036
|
+
...lines.slice(0, half),
|
|
72037
|
+
`… ${lines.length - OUTPUT_LINES} lines omitted …`,
|
|
72038
|
+
...lines.slice(-half)
|
|
72039
|
+
].join(`
|
|
72040
|
+
`);
|
|
72041
|
+
}, validRefName = (cwd, name) => !name.startsWith("-") && git5(cwd, ["check-ref-format", "--branch", name]).ok, nulList = (text) => text.split("\x00").filter(Boolean), SESSION_SAFE, TRAILER_CONFIG, nothingToCommit = (what) => failure3("failed", `nothing_to_commit: ${what}`, "workit git commit --allow-empty -m '<msg>' # only to record an empty commit on purpose"), SHA;
|
|
71792
72042
|
var init_ops = __esm(() => {
|
|
71793
72043
|
init_branch();
|
|
71794
72044
|
init_commit_flavors();
|
|
@@ -71800,15 +72050,16 @@ var init_ops = __esm(() => {
|
|
|
71800
72050
|
init_rev();
|
|
71801
72051
|
COMMIT_TIMEOUT_MS = 10 * 60000;
|
|
71802
72052
|
SESSION_SAFE = /^[\w.:@/+-]{1,128}$/u;
|
|
72053
|
+
TRAILER_CONFIG = ["-c", "trailer.separators=:", "-c", "trailer.where=end"];
|
|
71803
72054
|
SHA = /^[0-9a-f]{40,64}$/u;
|
|
71804
72055
|
});
|
|
71805
72056
|
|
|
71806
72057
|
// packages/workit-core/src/forge/reruns.ts
|
|
71807
72058
|
import fs19 from "node:fs";
|
|
71808
|
-
import
|
|
72059
|
+
import path40 from "node:path";
|
|
71809
72060
|
function rerunLogPath(cwd) {
|
|
71810
72061
|
const common = gitCommonDir(cwd);
|
|
71811
|
-
return common ?
|
|
72062
|
+
return common ? path40.join(common, "workit", "ci-reruns.jsonl") : null;
|
|
71812
72063
|
}
|
|
71813
72064
|
function rerunCounts(cwd, key) {
|
|
71814
72065
|
const counts = new Map;
|
|
@@ -71843,7 +72094,7 @@ function recordReruns(cwd, rows) {
|
|
|
71843
72094
|
if (!file)
|
|
71844
72095
|
return false;
|
|
71845
72096
|
try {
|
|
71846
|
-
fs19.mkdirSync(
|
|
72097
|
+
fs19.mkdirSync(path40.dirname(file), { recursive: true });
|
|
71847
72098
|
fs19.appendFileSync(file, rows.map((row) => `${JSON.stringify(row)}
|
|
71848
72099
|
`).join(""));
|
|
71849
72100
|
return true;
|
|
@@ -71856,7 +72107,7 @@ function withRerunLock(cwd, fn) {
|
|
|
71856
72107
|
if (!file)
|
|
71857
72108
|
return fn();
|
|
71858
72109
|
const lock = `${file.slice(0, -".jsonl".length)}.lock`;
|
|
71859
|
-
fs19.mkdirSync(
|
|
72110
|
+
fs19.mkdirSync(path40.dirname(lock), { recursive: true });
|
|
71860
72111
|
const until = Date.now() + LOCK_WAIT_MS2;
|
|
71861
72112
|
for (;; ) {
|
|
71862
72113
|
try {
|
|
@@ -72132,16 +72383,16 @@ function waitVerdict(doc, options) {
|
|
|
72132
72383
|
const checks = doc.checks.state;
|
|
72133
72384
|
if (checks === "failing")
|
|
72134
72385
|
return { state: "failed", reason: "checks_failing" };
|
|
72135
|
-
if (checks
|
|
72136
|
-
return { state: "waiting", reason: "checks_pending" };
|
|
72137
|
-
if (checks === "passing")
|
|
72138
|
-
return { state: "ready", reason: "checks_passing" };
|
|
72139
|
-
if (doc.conflicts)
|
|
72386
|
+
if (doc.conflicts && checks !== "passing")
|
|
72140
72387
|
return {
|
|
72141
72388
|
state: "blocked",
|
|
72142
72389
|
reason: "conflicts",
|
|
72143
72390
|
unblock: `rebase onto ${doc.base} and push; CI does not run on a conflicting PR`
|
|
72144
72391
|
};
|
|
72392
|
+
if (checks === "pending")
|
|
72393
|
+
return { state: "waiting", reason: "checks_pending" };
|
|
72394
|
+
if (checks === "passing")
|
|
72395
|
+
return { state: "ready", reason: "checks_passing" };
|
|
72145
72396
|
const ciRequired = doc.checks.missingRequired === null || doc.mergeState === "ci_must_pass" || doc.mergeState === "ci_still_running";
|
|
72146
72397
|
if (ciRequired)
|
|
72147
72398
|
return { state: "waiting", reason: "no_checks_yet" };
|
|
@@ -72249,7 +72500,7 @@ var init_report = __esm(() => {
|
|
|
72249
72500
|
});
|
|
72250
72501
|
|
|
72251
72502
|
// packages/workit-core/src/forge/pr-ops.ts
|
|
72252
|
-
import { spawnSync as
|
|
72503
|
+
import { spawnSync as spawnSync18 } from "node:child_process";
|
|
72253
72504
|
function fillFromCommits(cwd, resolved, base) {
|
|
72254
72505
|
const tracking = `refs/remotes/${resolved.baseRemote}/${base}`;
|
|
72255
72506
|
if (!base.startsWith("-"))
|
|
@@ -72257,7 +72508,7 @@ function fillFromCommits(cwd, resolved, base) {
|
|
|
72257
72508
|
const from = resolveRef(cwd, tracking) ?? resolveRef(cwd, `refs/heads/${base}`);
|
|
72258
72509
|
if (!from)
|
|
72259
72510
|
return failure3("not_found", `base ${base} is not available locally`, `git fetch ${resolved.baseRemote} ${base}`);
|
|
72260
|
-
const log =
|
|
72511
|
+
const log = spawnSync18("git", ["log", "--reverse", "--no-merges", "--format=%s%x1f%b%x1e", `${from}..HEAD`], {
|
|
72261
72512
|
cwd,
|
|
72262
72513
|
encoding: "utf8",
|
|
72263
72514
|
timeout: GIT_TIMEOUTS.local,
|
|
@@ -72562,15 +72813,15 @@ var init_pr_ops = __esm(() => {
|
|
|
72562
72813
|
});
|
|
72563
72814
|
|
|
72564
72815
|
// packages/workit-core/src/forge/exec.ts
|
|
72565
|
-
import { spawnSync as
|
|
72816
|
+
import { spawnSync as spawnSync19 } from "node:child_process";
|
|
72566
72817
|
import fs20 from "node:fs";
|
|
72567
|
-
import
|
|
72818
|
+
import path41 from "node:path";
|
|
72568
72819
|
function findExecutable(bin, env) {
|
|
72569
|
-
const dirs = (env.PATH ?? env.Path ?? "").split(
|
|
72820
|
+
const dirs = (env.PATH ?? env.Path ?? "").split(path41.delimiter).filter(Boolean);
|
|
72570
72821
|
const exts = process.platform === "win32" ? [".exe"] : [""];
|
|
72571
72822
|
for (const dir of dirs)
|
|
72572
72823
|
for (const ext of exts) {
|
|
72573
|
-
const candidate =
|
|
72824
|
+
const candidate = path41.join(dir, `${bin}${ext}`);
|
|
72574
72825
|
try {
|
|
72575
72826
|
const stat = fs20.statSync(candidate);
|
|
72576
72827
|
if (stat.isFile())
|
|
@@ -72597,7 +72848,7 @@ function systemRunner(kind, apiHost, env = process.env) {
|
|
|
72597
72848
|
if (!exe)
|
|
72598
72849
|
return { status: null, stdout: "", stderr: "", timedOut: false, missing: true };
|
|
72599
72850
|
const token = options.token ? kind === "github" ? { GH_TOKEN: options.token, GH_ENTERPRISE_TOKEN: options.token } : { GITLAB_TOKEN: options.token } : {};
|
|
72600
|
-
const result =
|
|
72851
|
+
const result = spawnSync19(exe, [...args], {
|
|
72601
72852
|
encoding: "utf8",
|
|
72602
72853
|
env: { ...childEnv, ...token },
|
|
72603
72854
|
maxBuffer: MAX_BUFFER2,
|
|
@@ -72616,17 +72867,45 @@ function systemRunner(kind, apiHost, env = process.env) {
|
|
|
72616
72867
|
};
|
|
72617
72868
|
};
|
|
72618
72869
|
}
|
|
72870
|
+
function forgeReason(run) {
|
|
72871
|
+
const parts = [];
|
|
72872
|
+
try {
|
|
72873
|
+
const body = JSON.parse(run.stdout);
|
|
72874
|
+
const add = (value) => {
|
|
72875
|
+
if (typeof value === "string" && value.trim())
|
|
72876
|
+
parts.push(value.trim());
|
|
72877
|
+
else if (Array.isArray(value))
|
|
72878
|
+
value.forEach(add);
|
|
72879
|
+
else if (value && typeof value === "object") {
|
|
72880
|
+
const record = value;
|
|
72881
|
+
if (typeof record.message === "string")
|
|
72882
|
+
add(record.message);
|
|
72883
|
+
else if (!("code" in record))
|
|
72884
|
+
Object.values(record).forEach(add);
|
|
72885
|
+
}
|
|
72886
|
+
};
|
|
72887
|
+
add(body.message);
|
|
72888
|
+
add(body.error);
|
|
72889
|
+
add(body.errors);
|
|
72890
|
+
} catch {}
|
|
72891
|
+
const stderr = (run.stderr.split(/\r?\n/u).find((value) => value.trim()) ?? "").trim();
|
|
72892
|
+
const unique = [...new Set(parts)];
|
|
72893
|
+
const text = unique.length ? `${unique.join("; ")}${stderr ? ` (${stderr.replace(/^gh: |^glab: /u, "")})` : ""}` : stderr || (run.stdout.split(/\r?\n/u).find((value) => value.trim()) ?? "");
|
|
72894
|
+
return redactText(text).slice(0, 300);
|
|
72895
|
+
}
|
|
72619
72896
|
function cliFailure(bin, apiHost, run, what, timeoutMs) {
|
|
72620
72897
|
if (run.missing)
|
|
72621
72898
|
return failure3("unavailable", `${bin} is not installed`, installHint(bin));
|
|
72622
72899
|
if (run.timedOut)
|
|
72623
|
-
return failure3("unavailable", `${bin} api ${what} timed out after ${timeoutMs} ms`);
|
|
72900
|
+
return failure3("unavailable", run.timeoutMs === 0 ? `${bin} api ${what} not attempted: the command's time budget ran out` : `${bin} api ${what} timed out after ${run.timeoutMs ?? timeoutMs} ms`, `check the network to ${apiHost}, then retry`);
|
|
72624
72901
|
const stderr = redactText(run.stderr).trim();
|
|
72625
72902
|
if (/auth login|not logged in|no token|authentication required|bad credentials|\b401\b|unauthorized/iu.test(stderr))
|
|
72626
72903
|
return failure3("unavailable", `${bin} is not authenticated for ${apiHost}`, loginHint(bin, apiHost));
|
|
72627
72904
|
if (/\b404\b|not found/iu.test(stderr))
|
|
72628
72905
|
return failure3("not_found", `${what} was not found`);
|
|
72629
72906
|
const first = stderr.split(/\r?\n/u).find((value) => value.trim()) ?? `exit ${run.status}`;
|
|
72907
|
+
if (isNetworkError(stderr))
|
|
72908
|
+
return failure3("unavailable", `${bin} api ${what} failed (network): ${first.slice(0, 200)}`, `check the network to ${apiHost}, then retry`);
|
|
72630
72909
|
return failure3("failed", `${bin} api ${what} failed: ${first.slice(0, 200)}`);
|
|
72631
72910
|
}
|
|
72632
72911
|
function apiJson(runner, bin, apiHost, args, what, timeoutMs = FORGE_TIMEOUTS.api, options = {}) {
|
|
@@ -72657,12 +72936,15 @@ function apiWrite(runner, bin, apiHost, args, what, timeoutMs = FORGE_TIMEOUTS.a
|
|
|
72657
72936
|
if (run.status !== 0) {
|
|
72658
72937
|
const text = redactText(`${run.stderr}
|
|
72659
72938
|
${run.stdout}`).trim();
|
|
72660
|
-
const reason = (
|
|
72939
|
+
const reason = forgeReason(run);
|
|
72661
72940
|
if (!run.missing && !run.timedOut && /\b409\b|head branch was modified|sha does not match|sha.*mismatch/iu.test(text))
|
|
72662
72941
|
return failure3("blocked", `head_moved: ${what} refused because the head changed (${reason})`, "workit pr status # re-check the new head, then verify it again");
|
|
72663
72942
|
if (!run.missing && !run.timedOut && /\b(405|406|422)\b/u.test(text))
|
|
72664
72943
|
return failure3("blocked", `${what} refused by the forge: ${reason}`);
|
|
72665
|
-
|
|
72944
|
+
const failed = cliFailure(bin, apiHost, run, what, timeoutMs);
|
|
72945
|
+
if (!failed.ok && failed.code === "unavailable" && !run.missing && !/ is not authenticated for /u.test(failed.error))
|
|
72946
|
+
return failure3("unavailable", failed.error, "workit pr status # the write may have gone through; check it first, then retry");
|
|
72947
|
+
return failed;
|
|
72666
72948
|
}
|
|
72667
72949
|
if (!run.stdout.trim())
|
|
72668
72950
|
return { ok: true, data: {} };
|
|
@@ -72672,11 +72954,12 @@ ${run.stdout}`).trim();
|
|
|
72672
72954
|
return failure3("failed", `${bin} api ${what} returned invalid JSON`);
|
|
72673
72955
|
}
|
|
72674
72956
|
}
|
|
72675
|
-
var FORGE_TIMEOUTS, MAX_BUFFER2, installHint = (bin) => bin === "gh" ? "install the GitHub CLI (https://cli.github.com) and run: gh auth login" : "install glab (https://gitlab.com/gitlab-org/cli) and run: glab auth login", loginHint = (bin, apiHost) => `${bin} auth login --hostname ${apiHost}
|
|
72957
|
+
var FORGE_TIMEOUTS, MAX_BUFFER2, installHint = (bin) => bin === "gh" ? "install the GitHub CLI (https://cli.github.com) and run: gh auth login" : "install glab (https://gitlab.com/gitlab-org/cli) and run: glab auth login", loginHint = (bin, apiHost) => `${bin} auth login --hostname ${apiHost}`, NETWORK, isNetworkError = (text) => NETWORK.test(text);
|
|
72676
72958
|
var init_exec = __esm(() => {
|
|
72677
72959
|
init_redact();
|
|
72678
72960
|
FORGE_TIMEOUTS = { api: 20000, log: 30000 };
|
|
72679
72961
|
MAX_BUFFER2 = 64 * 1024 * 1024;
|
|
72962
|
+
NETWORK = /error connecting|could not resolve|no such host|connection (refused|reset|timed out)|network is unreachable|i\/o timeout|tls handshake|unexpected eof|\bHTTP 5\d\d\b|bad gateway|service unavailable|gateway time-?out/iu;
|
|
72680
72963
|
});
|
|
72681
72964
|
|
|
72682
72965
|
// packages/workit-core/src/forge/github.ts
|
|
@@ -73268,6 +73551,82 @@ var init_gitlab = __esm(() => {
|
|
|
73268
73551
|
|
|
73269
73552
|
// packages/workit-core/src/forge/resolve.ts
|
|
73270
73553
|
import fs21 from "node:fs";
|
|
73554
|
+
function memo4(scope, key, compute, now = Date.now) {
|
|
73555
|
+
let entries = memoScopes.get(scope.owner);
|
|
73556
|
+
if (!entries)
|
|
73557
|
+
memoScopes.set(scope.owner, entries = new Map);
|
|
73558
|
+
const full = `${scope.key} ${key}`;
|
|
73559
|
+
const hit = entries.get(full);
|
|
73560
|
+
if (hit && now() - hit.at < MEMO_TTL_MS)
|
|
73561
|
+
return hit.value;
|
|
73562
|
+
const { keep, value } = compute();
|
|
73563
|
+
if (keep)
|
|
73564
|
+
entries.set(full, { at: now(), value });
|
|
73565
|
+
return value;
|
|
73566
|
+
}
|
|
73567
|
+
function clampedRunner(base, limits, now, token) {
|
|
73568
|
+
return (bin, args, callOptions) => {
|
|
73569
|
+
let timeoutMs = callOptions.timeoutMs;
|
|
73570
|
+
if (limits.deadline !== null) {
|
|
73571
|
+
const remaining = limits.deadline - now();
|
|
73572
|
+
if (remaining <= 0)
|
|
73573
|
+
return {
|
|
73574
|
+
status: null,
|
|
73575
|
+
stdout: "",
|
|
73576
|
+
stderr: "",
|
|
73577
|
+
timedOut: true,
|
|
73578
|
+
missing: false,
|
|
73579
|
+
timeoutMs: 0
|
|
73580
|
+
};
|
|
73581
|
+
timeoutMs = Math.min(timeoutMs, remaining);
|
|
73582
|
+
}
|
|
73583
|
+
const run = base(bin, args, { ...callOptions, timeoutMs, ...token ? { token } : {} });
|
|
73584
|
+
return run.timedOut && run.timeoutMs === undefined ? { ...run, timeoutMs } : run;
|
|
73585
|
+
};
|
|
73586
|
+
}
|
|
73587
|
+
function resolveCredential(workspace, derived, run, scope) {
|
|
73588
|
+
const { kind, apiHost } = derived;
|
|
73589
|
+
const expectedAccount = workspace?.vcs?.account ?? null;
|
|
73590
|
+
const tokenFile = workspace?.vcs?.tokenFile;
|
|
73591
|
+
if (typeof tokenFile === "string" && tokenFile.trim()) {
|
|
73592
|
+
const workspaceHost = workspace?.vcs?.host;
|
|
73593
|
+
const explicit = typeof workspaceHost === "string" && workspaceHost.trim().toLowerCase().replace(/^https?:\/\//u, "").replace(/\/.*$/u, "") === apiHost;
|
|
73594
|
+
if (derived.via === "host_name" && !explicit)
|
|
73595
|
+
return failure3("blocked", `token_host_unverified: push host ${derived.host} only looks like ${kind}; workspace ${workspace?.name ?? "?"} vcs.tokenFile is not sent to it`, `if ${apiHost} is your ${kind} server, set ${kind}.host in ~/.config/workit/vcs.json (or vcs.host for the workspace); otherwise fix the push remote`);
|
|
73596
|
+
let token = "";
|
|
73597
|
+
try {
|
|
73598
|
+
token = fs21.readFileSync(tokenFile.trim(), "utf8").trim();
|
|
73599
|
+
} catch {
|
|
73600
|
+
return failure3("blocked", `workspace ${workspace?.name ?? "?"} vcs.tokenFile cannot be read`, "fix vcs.tokenFile in ~/.config/workit/workspaces.json");
|
|
73601
|
+
}
|
|
73602
|
+
if (!token)
|
|
73603
|
+
return failure3("blocked", `workspace ${workspace?.name ?? "?"} vcs.tokenFile is empty`, "fix vcs.tokenFile in ~/.config/workit/workspaces.json");
|
|
73604
|
+
return success2({ token, credential: "workspace_token_file" });
|
|
73605
|
+
}
|
|
73606
|
+
if (!expectedAccount || kind !== "github")
|
|
73607
|
+
return success2({ token: undefined, credential: "cli_login" });
|
|
73608
|
+
return memo4(scope, `token ${apiHost} ${expectedAccount}`, () => {
|
|
73609
|
+
const issued = run("gh", ["auth", "token", "--hostname", apiHost, "--user", expectedAccount], {
|
|
73610
|
+
timeoutMs: FORGE_TIMEOUTS.api
|
|
73611
|
+
});
|
|
73612
|
+
const value = authTokenResult(issued, apiHost, expectedAccount, workspace?.name ?? "?");
|
|
73613
|
+
return { keep: value.ok, value };
|
|
73614
|
+
});
|
|
73615
|
+
}
|
|
73616
|
+
function authTokenResult(issued, apiHost, account, workspace) {
|
|
73617
|
+
if (issued.missing)
|
|
73618
|
+
return failure3("unavailable", "gh is not installed", "install the GitHub CLI (https://cli.github.com) and run: gh auth login");
|
|
73619
|
+
if (issued.timedOut)
|
|
73620
|
+
return failure3("unavailable", `gh auth token ${issued.timeoutMs === 0 ? "not attempted: the time budget ran out" : `timed out after ${issued.timeoutMs ?? FORGE_TIMEOUTS.api} ms`} (looking up the ${account} login on ${apiHost})`, "gh auth status # check gh and the network, then retry");
|
|
73621
|
+
const token = issued.status === 0 ? issued.stdout.trim() : "";
|
|
73622
|
+
if (token)
|
|
73623
|
+
return success2({ token, credential: "gh_account_token" });
|
|
73624
|
+
const stderr = redactText(issued.stderr).trim();
|
|
73625
|
+
const first = stderr.split(/\r?\n/u).find((line) => line.trim()) ?? "";
|
|
73626
|
+
if (issued.status !== 0 && first && !/no oauth token|not logged in|no account/iu.test(stderr))
|
|
73627
|
+
return failure3("unavailable", `gh auth token failed${isNetworkError(stderr) ? " (network)" : ""}: ${first.slice(0, 200)}`, "gh auth status # check gh and the network, then retry");
|
|
73628
|
+
return failure3("blocked", `identity_unavailable: gh has no login for ${account} on ${apiHost} (workspace ${workspace})`, `gh auth login --hostname ${apiHost} # sign in as ${account}; workit passes that account's token per call and never switches the active account`);
|
|
73629
|
+
}
|
|
73271
73630
|
function resolveForge(cwd, options = {}) {
|
|
73272
73631
|
let workspace;
|
|
73273
73632
|
try {
|
|
@@ -73285,48 +73644,16 @@ function resolveForge(cwd, options = {}) {
|
|
|
73285
73644
|
const derived = pushed.forge;
|
|
73286
73645
|
const { kind, apiHost } = derived;
|
|
73287
73646
|
const base = options.runner ?? systemRunner(kind, apiHost, options.env ?? process.env);
|
|
73647
|
+
const scope = memoScope(options.runner, options.env ?? process.env);
|
|
73288
73648
|
const now = options.now ?? Date.now;
|
|
73289
73649
|
const expectedAccount = workspace?.vcs?.account ?? null;
|
|
73290
|
-
|
|
73291
|
-
|
|
73292
|
-
|
|
73293
|
-
|
|
73294
|
-
|
|
73295
|
-
const explicit = typeof workspaceHost === "string" && workspaceHost.trim().toLowerCase().replace(/^https?:\/\//u, "").replace(/\/.*$/u, "") === apiHost;
|
|
73296
|
-
if (derived.via === "host_name" && !explicit)
|
|
73297
|
-
return failure3("blocked", `token_host_unverified: push host ${derived.host} only looks like ${kind}; workspace ${workspace?.name ?? "?"} vcs.tokenFile is not sent to it`, `if ${apiHost} is your ${kind} server, set ${kind}.host in ~/.config/workit/vcs.json (or vcs.host for the workspace); otherwise fix the push remote`);
|
|
73298
|
-
try {
|
|
73299
|
-
token = fs21.readFileSync(tokenFile.trim(), "utf8").trim();
|
|
73300
|
-
} catch {
|
|
73301
|
-
return failure3("blocked", `workspace ${workspace?.name ?? "?"} vcs.tokenFile cannot be read`, "fix vcs.tokenFile in ~/.config/workit/workspaces.json");
|
|
73302
|
-
}
|
|
73303
|
-
if (!token)
|
|
73304
|
-
return failure3("blocked", `workspace ${workspace?.name ?? "?"} vcs.tokenFile is empty`, "fix vcs.tokenFile in ~/.config/workit/workspaces.json");
|
|
73305
|
-
credential = "workspace_token_file";
|
|
73306
|
-
} else if (expectedAccount && kind === "github") {
|
|
73307
|
-
const issued = base("gh", ["auth", "token", "--hostname", apiHost, "--user", expectedAccount], {
|
|
73308
|
-
timeoutMs: FORGE_TIMEOUTS.api
|
|
73309
|
-
});
|
|
73310
|
-
if (issued.missing)
|
|
73311
|
-
return failure3("unavailable", "gh is not installed", "install the GitHub CLI (https://cli.github.com) and run: gh auth login");
|
|
73312
|
-
token = issued.status === 0 ? issued.stdout.trim() : "";
|
|
73313
|
-
if (!token)
|
|
73314
|
-
return failure3("blocked", `identity_unavailable: gh has no login for ${expectedAccount} on ${apiHost} (workspace ${workspace?.name ?? "?"})`, `gh auth login --hostname ${apiHost} # sign in as ${expectedAccount}; workit passes that account's token per call and never switches the active account`);
|
|
73315
|
-
credential = "gh_account_token";
|
|
73316
|
-
}
|
|
73317
|
-
const limits = { deadline: null };
|
|
73318
|
-
const runner = (bin, args, callOptions) => {
|
|
73319
|
-
let timeoutMs = callOptions.timeoutMs;
|
|
73320
|
-
if (limits.deadline !== null) {
|
|
73321
|
-
const remaining = limits.deadline - now();
|
|
73322
|
-
if (remaining <= 0)
|
|
73323
|
-
return { status: null, stdout: "", stderr: "", timedOut: true, missing: false };
|
|
73324
|
-
timeoutMs = Math.min(timeoutMs, remaining);
|
|
73325
|
-
}
|
|
73326
|
-
return base(bin, args, { ...callOptions, timeoutMs, ...token ? { token } : {} });
|
|
73327
|
-
};
|
|
73650
|
+
const limits = { deadline: options.deadline ?? null };
|
|
73651
|
+
const credential = resolveCredential(workspace, derived, clampedRunner(base, limits, now), scope);
|
|
73652
|
+
if (!credential.ok)
|
|
73653
|
+
return credential;
|
|
73654
|
+
const runner = clampedRunner(base, limits, now, credential.data.token);
|
|
73328
73655
|
const headRepo = derived.repo;
|
|
73329
|
-
const probe = createForge(derived, headRepo, runner);
|
|
73656
|
+
const probe = memoizedIdentity(createForge(derived, headRepo, runner), scope, credential.data);
|
|
73330
73657
|
const info = probe.repoInfo(headRepo);
|
|
73331
73658
|
if (!info.ok && info.code !== "not_found")
|
|
73332
73659
|
return info;
|
|
@@ -73341,13 +73668,13 @@ function resolveForge(cwd, options = {}) {
|
|
|
73341
73668
|
const baseRepo = upstream && upstream !== headRepo ? upstream : info.ok && info.data.parent ? info.data.parent : headRepo;
|
|
73342
73669
|
const baseRemote = baseRepo === headRepo ? pushed.remote : remotes.find((name) => repoOfRemote(name) === baseRepo) ?? pushed.remote;
|
|
73343
73670
|
return success2({
|
|
73344
|
-
forge: baseRepo === headRepo ? probe : createForge(derived, baseRepo, runner),
|
|
73671
|
+
forge: baseRepo === headRepo ? probe : memoizedIdentity(createForge(derived, baseRepo, runner), scope, credential.data),
|
|
73345
73672
|
remote: pushed.remote,
|
|
73346
73673
|
url: pushed.url,
|
|
73347
73674
|
derived,
|
|
73348
73675
|
workspace: workspace?.name ?? null,
|
|
73349
73676
|
expectedAccount,
|
|
73350
|
-
credential,
|
|
73677
|
+
credential: credential.data.credential,
|
|
73351
73678
|
headRepo,
|
|
73352
73679
|
headProjectId,
|
|
73353
73680
|
fork: baseRepo !== headRepo,
|
|
@@ -73360,14 +73687,74 @@ function checkIdentity(resolved) {
|
|
|
73360
73687
|
const identity = forge.identity(expectedAccount);
|
|
73361
73688
|
if (!identity.ok)
|
|
73362
73689
|
return identity;
|
|
73363
|
-
if (identity.data.matches === false)
|
|
73364
|
-
|
|
73365
|
-
const where = `workspace ${resolved.workspace ?? "?"}`;
|
|
73366
|
-
return failure3("blocked", `identity_mismatch: the ${resolved.credential === "workspace_token_file" ? "vcs.tokenFile token" : `${bin} login`} is ${identity.data.login} but ${where} expects ${expectedAccount}`, resolved.credential === "workspace_token_file" ? `put a token for ${expectedAccount} in the ${where} vcs.tokenFile` : `${bin} auth login --hostname ${forge.apiHost} # sign in as ${expectedAccount}`);
|
|
73367
|
-
}
|
|
73690
|
+
if (identity.data.matches === false)
|
|
73691
|
+
return mismatch(resolved, identity.data.login ?? "?", resolved.workspace);
|
|
73368
73692
|
return identity;
|
|
73369
73693
|
}
|
|
73370
|
-
|
|
73694
|
+
function checkPushIdentity(cwd, options = {}) {
|
|
73695
|
+
let workspace;
|
|
73696
|
+
try {
|
|
73697
|
+
workspace = resolveRuntimeWorkspaceVcs(cwd);
|
|
73698
|
+
} catch (error) {
|
|
73699
|
+
return failure3("blocked", error instanceof Error ? error.message : String(error), "fix ~/.config/workit/workspaces.json (workit init → Workspaces)");
|
|
73700
|
+
}
|
|
73701
|
+
const account = workspace?.vcs?.account ?? null;
|
|
73702
|
+
const skipped = (forge, reason, unblock) => success2({
|
|
73703
|
+
status: "skipped",
|
|
73704
|
+
forge,
|
|
73705
|
+
account,
|
|
73706
|
+
login: null,
|
|
73707
|
+
reason,
|
|
73708
|
+
...unblock ? { unblock } : {}
|
|
73709
|
+
});
|
|
73710
|
+
const pushed = pushForge(cwd, {
|
|
73711
|
+
branch: options.branch,
|
|
73712
|
+
hosts: configuredHosts(),
|
|
73713
|
+
sshConfig: options.sshConfig
|
|
73714
|
+
});
|
|
73715
|
+
if (!pushed.ok) {
|
|
73716
|
+
const host = /^unsupported_forge: .*?"([^"]+)"/u.exec(pushed.error)?.[1];
|
|
73717
|
+
const what = /could not resolve ssh alias/u.test(pushed.error) ? `could not resolve ssh alias ${host} to GitHub or GitLab` : pushed.error.startsWith("unsupported_forge: ssh host") ? `ssh host ${host} is not a GitHub/GitLab host or an alias workit can resolve` : `${host} is not a GitHub or GitLab host workit knows`;
|
|
73718
|
+
return skipped(null, host ? `unsupported_forge: ${what}; account checks skipped, pushed with git only (no PR or CI verbs)` : pushed.error, pushed.unblock);
|
|
73719
|
+
}
|
|
73720
|
+
const derived = pushed.forge;
|
|
73721
|
+
const conflict = forgeConflict(derived, workspace?.vcs?.provider);
|
|
73722
|
+
if (conflict)
|
|
73723
|
+
return skipped(derived.kind, conflict.error, conflict.unblock);
|
|
73724
|
+
if (!account)
|
|
73725
|
+
return success2({ status: "no_account", forge: derived.kind, account, login: null });
|
|
73726
|
+
const base = options.runner ?? systemRunner(derived.kind, derived.apiHost, options.env ?? process.env);
|
|
73727
|
+
const scope = memoScope(options.runner, options.env ?? process.env);
|
|
73728
|
+
const now = options.now ?? Date.now;
|
|
73729
|
+
const limits = { deadline: now() + (options.budgetMs ?? PUSH_IDENTITY_BUDGET_MS) };
|
|
73730
|
+
const credential = resolveCredential(workspace, derived, clampedRunner(base, limits, now), scope);
|
|
73731
|
+
if (!credential.ok)
|
|
73732
|
+
return unreachable(credential) ? skipped(derived.kind, credential.error, credential.unblock) : credential;
|
|
73733
|
+
const forge = memoizedIdentity(createForge(derived, derived.repo, clampedRunner(base, limits, now, credential.data.token)), scope, credential.data);
|
|
73734
|
+
const identity = forge.identity(account);
|
|
73735
|
+
if (!identity.ok)
|
|
73736
|
+
return unreachable(identity) ? skipped(derived.kind, identity.error, identity.unblock) : identity;
|
|
73737
|
+
if (identity.data.matches === false)
|
|
73738
|
+
return mismatch({ forge, credential: credential.data.credential, expectedAccount: account }, identity.data.login ?? "?", workspace?.name ?? null);
|
|
73739
|
+
if (identity.data.matches === null)
|
|
73740
|
+
return skipped(derived.kind, identity.data.note ?? "the credential cannot read /user");
|
|
73741
|
+
return success2({ status: "verified", forge: derived.kind, account, login: identity.data.login });
|
|
73742
|
+
}
|
|
73743
|
+
var MEMO_TTL_MS, SYSTEM_SCOPE, memoScopes, memoScope = (runner, env) => ({
|
|
73744
|
+
owner: runner ?? SYSTEM_SCOPE,
|
|
73745
|
+
key: [env.GH_CONFIG_DIR, env.GLAB_CONFIG_DIR, env.XDG_CONFIG_HOME, env.HOME].map((value) => value ?? "").join("|")
|
|
73746
|
+
}), memoizedIdentity = (forge, scope, credential) => {
|
|
73747
|
+
if (credential.credential === "cli_login")
|
|
73748
|
+
return forge;
|
|
73749
|
+
const identity = (expected) => forge.identity(expected);
|
|
73750
|
+
return {
|
|
73751
|
+
...forge,
|
|
73752
|
+
identity: (expected) => memo4(scope, `identity ${forge.kind} ${forge.apiHost} ${credential.credential} ${credential.token ?? ""} ${expected ?? ""}`, () => {
|
|
73753
|
+
const value = identity(expected);
|
|
73754
|
+
return { keep: value.ok, value };
|
|
73755
|
+
})
|
|
73756
|
+
};
|
|
73757
|
+
}, configuredHosts = () => {
|
|
73371
73758
|
const vcs = readVcsConfig();
|
|
73372
73759
|
if (vcs.status !== "valid")
|
|
73373
73760
|
return {};
|
|
@@ -73376,7 +73763,13 @@ var configuredHosts = () => {
|
|
|
73376
73763
|
return typeof value === "string" && value.trim() ? [value.trim()] : [];
|
|
73377
73764
|
};
|
|
73378
73765
|
return { github: host("github"), gitlab: host("gitlab") };
|
|
73379
|
-
}, createForge = (derived, repo, runner) => derived.kind === "github" ? createGitHubForge({ apiHost: derived.apiHost, repo, runner }) : createGitLabForge({ apiHost: derived.apiHost, repo, runner })
|
|
73766
|
+
}, createForge = (derived, repo, runner) => derived.kind === "github" ? createGitHubForge({ apiHost: derived.apiHost, repo, runner }) : createGitLabForge({ apiHost: derived.apiHost, repo, runner }), mismatch = (resolved, login, workspace) => {
|
|
73767
|
+
const { forge, expectedAccount } = resolved;
|
|
73768
|
+
const bin = forge.kind === "github" ? "gh" : "glab";
|
|
73769
|
+
const where = `workspace ${workspace ?? "?"}`;
|
|
73770
|
+
const fileFix = `put a token for ${expectedAccount} in the ${where} vcs.tokenFile`;
|
|
73771
|
+
return failure3("blocked", `identity_mismatch: the ${resolved.credential === "workspace_token_file" ? "vcs.tokenFile token" : `${bin} login`} is ${login} but ${where} expects ${expectedAccount}`, resolved.credential === "workspace_token_file" ? fileFix : forge.kind === "github" ? `gh auth login --hostname ${forge.apiHost} # sign in as ${expectedAccount}` : `${fileFix} (glab keeps one login per host, so workit never switches it)`);
|
|
73772
|
+
}, unreachable = (result) => result.code === "unavailable" && !/ is not authenticated for /u.test(result.error), PUSH_IDENTITY_BUDGET_MS = 5000;
|
|
73380
73773
|
var init_resolve = __esm(() => {
|
|
73381
73774
|
init_vcs_config();
|
|
73382
73775
|
init_workspaces();
|
|
@@ -73384,6 +73777,10 @@ var init_resolve = __esm(() => {
|
|
|
73384
73777
|
init_exec();
|
|
73385
73778
|
init_github();
|
|
73386
73779
|
init_gitlab();
|
|
73780
|
+
init_redact();
|
|
73781
|
+
MEMO_TTL_MS = 5 * 60000;
|
|
73782
|
+
SYSTEM_SCOPE = {};
|
|
73783
|
+
memoScopes = new WeakMap;
|
|
73387
73784
|
});
|
|
73388
73785
|
|
|
73389
73786
|
// packages/workit-core/src/duration.ts
|
|
@@ -73476,16 +73873,19 @@ function releaseTrunk(io, track, branch) {
|
|
|
73476
73873
|
tracked: Boolean(resolved.releaseTrack)
|
|
73477
73874
|
};
|
|
73478
73875
|
}
|
|
73479
|
-
function connect(io, branch) {
|
|
73876
|
+
function connect(io, branch, options = {}) {
|
|
73877
|
+
const verbDeadline = options.deadline ?? null;
|
|
73480
73878
|
const resolved = resolveForge(io.cwd, {
|
|
73481
73879
|
branch,
|
|
73482
73880
|
env: io.env,
|
|
73483
73881
|
runner: forgeDeps.runner,
|
|
73484
|
-
now: forgeDeps.now
|
|
73882
|
+
now: forgeDeps.now,
|
|
73883
|
+
deadline: verbDeadline ?? forgeDeps.now() + CONNECT_BUDGET_MS
|
|
73485
73884
|
});
|
|
73486
73885
|
if (!resolved.ok)
|
|
73487
73886
|
return resolved;
|
|
73488
73887
|
const identity = checkIdentity(resolved.data);
|
|
73888
|
+
resolved.data.limits.deadline = verbDeadline;
|
|
73489
73889
|
if (!identity.ok)
|
|
73490
73890
|
return identity;
|
|
73491
73891
|
return {
|
|
@@ -73540,7 +73940,12 @@ function renderStatus(doc) {
|
|
|
73540
73940
|
lines.push(`next: ${doc.next}`);
|
|
73541
73941
|
return lines;
|
|
73542
73942
|
}
|
|
73543
|
-
var forgeDeps, MAX_REASON = 500, usage3 = (io, message, line) => emit(io, fail("invalid_input", message, { unblock: line })), forgeFail = (io, result, data) => emit(io, fail(result.code, result.error, { data, unblock: result.unblock })),
|
|
73943
|
+
var forgeDeps, MAX_REASON = 500, usage3 = (io, message, line) => emit(io, fail("invalid_input", message, { unblock: line })), forgeFail = (io, result, data) => emit(io, fail(result.code, result.error, { data, unblock: result.unblock })), CONNECT_BUDGET_MS = 15000, pushIdentity = (io, branch) => checkPushIdentity(io.cwd, {
|
|
73944
|
+
branch,
|
|
73945
|
+
env: io.env,
|
|
73946
|
+
runner: forgeDeps.runner,
|
|
73947
|
+
now: forgeDeps.now
|
|
73948
|
+
}), short = (sha) => sha ? sha.slice(0, 7) : "?";
|
|
73544
73949
|
var init_forge_common = __esm(() => {
|
|
73545
73950
|
init_resolve();
|
|
73546
73951
|
init_vcs_config();
|
|
@@ -73559,8 +73964,8 @@ var exports_pr = {};
|
|
|
73559
73964
|
__export(exports_pr, {
|
|
73560
73965
|
run: () => run10
|
|
73561
73966
|
});
|
|
73562
|
-
import { readFileSync as
|
|
73563
|
-
import
|
|
73967
|
+
import { readFileSync as readFileSync19 } from "node:fs";
|
|
73968
|
+
import path42 from "node:path";
|
|
73564
73969
|
async function status(argv, io) {
|
|
73565
73970
|
const flags = parseFlags(argv, { pr: "value", branch: "value", "log-lines": "value" });
|
|
73566
73971
|
if (typeof flags === "string")
|
|
@@ -73631,7 +74036,7 @@ async function create4(argv, io) {
|
|
|
73631
74036
|
let body = flags.values.body ?? null;
|
|
73632
74037
|
if (flags.values["body-file"] !== undefined) {
|
|
73633
74038
|
try {
|
|
73634
|
-
body =
|
|
74039
|
+
body = readFileSync19(path42.resolve(io.cwd, flags.values["body-file"]), "utf8");
|
|
73635
74040
|
} catch {
|
|
73636
74041
|
return usage3(io, `cannot read --body-file ${flags.values["body-file"]}`, CREATE_USAGE);
|
|
73637
74042
|
}
|
|
@@ -73790,16 +74195,17 @@ async function wait(argv, io) {
|
|
|
73790
74195
|
const head = flags.values.head ?? null;
|
|
73791
74196
|
if (head !== null && !/^[0-9a-f]{7,64}$/u.test(head))
|
|
73792
74197
|
return usage3(io, "--head must be a commit sha (7-64 hex chars)", WAIT_USAGE);
|
|
73793
|
-
const
|
|
74198
|
+
const started = forgeDeps.now();
|
|
74199
|
+
const deadline = started + timeoutMs;
|
|
74200
|
+
const connected = connect(io, flags.values.branch, {
|
|
74201
|
+
deadline: started + Math.max(timeoutMs, ONE_POLL_MS)
|
|
74202
|
+
});
|
|
73794
74203
|
if (!connected.ok)
|
|
73795
74204
|
return forgeFail(io, connected);
|
|
73796
74205
|
const resolved = connected.data;
|
|
73797
74206
|
const number = selectPr(io.cwd, resolved, { pr, branch: flags.values.branch ?? null });
|
|
73798
74207
|
if (!number.ok)
|
|
73799
74208
|
return forgeFail(io, number);
|
|
73800
|
-
const started = forgeDeps.now();
|
|
73801
|
-
const deadline = started + timeoutMs;
|
|
73802
|
-
resolved.limits.deadline = started + Math.max(timeoutMs, ONE_POLL_MS);
|
|
73803
74209
|
let polls = 0;
|
|
73804
74210
|
let errors = 0;
|
|
73805
74211
|
let status = null;
|
|
@@ -73976,6 +74382,8 @@ var exports_git = {};
|
|
|
73976
74382
|
__export(exports_git, {
|
|
73977
74383
|
run: () => run12
|
|
73978
74384
|
});
|
|
74385
|
+
import { readFileSync as readFileSync20 } from "node:fs";
|
|
74386
|
+
import path43 from "node:path";
|
|
73979
74387
|
async function branch(argv, io) {
|
|
73980
74388
|
const flags = parseFlags(argv, {
|
|
73981
74389
|
base: "value",
|
|
@@ -74012,7 +74420,15 @@ async function branch(argv, io) {
|
|
|
74012
74420
|
]);
|
|
74013
74421
|
}
|
|
74014
74422
|
function parseCommit(argv) {
|
|
74015
|
-
const out = {
|
|
74423
|
+
const out = {
|
|
74424
|
+
messages: [],
|
|
74425
|
+
files: [],
|
|
74426
|
+
all: false,
|
|
74427
|
+
amend: false,
|
|
74428
|
+
noEdit: false,
|
|
74429
|
+
allowEmpty: false,
|
|
74430
|
+
paths: []
|
|
74431
|
+
};
|
|
74016
74432
|
for (let index = 0;index < argv.length; index += 1) {
|
|
74017
74433
|
const arg = argv[index];
|
|
74018
74434
|
if (arg === "--") {
|
|
@@ -74021,23 +74437,24 @@ function parseCommit(argv) {
|
|
|
74021
74437
|
}
|
|
74022
74438
|
if (arg === "--json")
|
|
74023
74439
|
continue;
|
|
74024
|
-
|
|
74025
|
-
|
|
74026
|
-
|
|
74027
|
-
}
|
|
74028
|
-
if (arg === "-m" || arg === "--message") {
|
|
74029
|
-
const value = argv[++index];
|
|
74030
|
-
if (value === undefined)
|
|
74031
|
-
return `${arg} requires a message`;
|
|
74032
|
-
out.messages.push(value);
|
|
74033
|
-
continue;
|
|
74034
|
-
}
|
|
74035
|
-
if (arg.startsWith("--message=")) {
|
|
74036
|
-
out.messages.push(arg.slice("--message=".length));
|
|
74440
|
+
const flag = COMMIT_SWITCHES[arg];
|
|
74441
|
+
if (flag) {
|
|
74442
|
+
out[flag] = true;
|
|
74037
74443
|
continue;
|
|
74038
74444
|
}
|
|
74039
|
-
|
|
74040
|
-
out.messages
|
|
74445
|
+
const valued = [
|
|
74446
|
+
["-m", "--message", out.messages],
|
|
74447
|
+
["-F", "--file", out.files]
|
|
74448
|
+
].find(([shortName, longName]) => arg === shortName || arg === longName || arg.startsWith(`${longName}=`) || arg.startsWith(shortName) && arg.length > 2);
|
|
74449
|
+
if (valued) {
|
|
74450
|
+
const [shortName, longName, into] = valued;
|
|
74451
|
+
if (arg === shortName || arg === longName) {
|
|
74452
|
+
const value = argv[++index];
|
|
74453
|
+
if (value === undefined)
|
|
74454
|
+
return `${arg} requires a ${shortName === "-m" ? "message" : "file"}`;
|
|
74455
|
+
into.push(value);
|
|
74456
|
+
} else
|
|
74457
|
+
into.push(arg.startsWith(`${longName}=`) ? arg.slice(longName.length + 1) : arg.slice(2));
|
|
74041
74458
|
continue;
|
|
74042
74459
|
}
|
|
74043
74460
|
if (arg.startsWith("-"))
|
|
@@ -74046,20 +74463,48 @@ function parseCommit(argv) {
|
|
|
74046
74463
|
}
|
|
74047
74464
|
return out;
|
|
74048
74465
|
}
|
|
74466
|
+
function commitMessage(parsed, io) {
|
|
74467
|
+
if (parsed.messages.length && parsed.files.length)
|
|
74468
|
+
return { error: "pass -m or -F, not both" };
|
|
74469
|
+
if (parsed.files.length > 1)
|
|
74470
|
+
return { error: "-F takes one file" };
|
|
74471
|
+
const [file] = parsed.files;
|
|
74472
|
+
if (file === undefined)
|
|
74473
|
+
return parsed.messages.join(`
|
|
74474
|
+
|
|
74475
|
+
`);
|
|
74476
|
+
try {
|
|
74477
|
+
return file === "-" ? readFileSync20(0, "utf8") : readFileSync20(path43.resolve(io.cwd, file), "utf8");
|
|
74478
|
+
} catch (error) {
|
|
74479
|
+
return {
|
|
74480
|
+
error: `cannot read -F ${file}: ${error instanceof Error ? error.message : String(error)}`
|
|
74481
|
+
};
|
|
74482
|
+
}
|
|
74483
|
+
}
|
|
74049
74484
|
async function commit(argv, io) {
|
|
74050
74485
|
const parsed = parseCommit(argv);
|
|
74051
74486
|
if (typeof parsed === "string")
|
|
74052
74487
|
return usage3(io, parsed, COMMIT_USAGE);
|
|
74053
|
-
if (!parsed.messages.length)
|
|
74054
|
-
return usage3(io, "missing -m <msg>", COMMIT_USAGE);
|
|
74055
74488
|
if (parsed.all && parsed.paths.length)
|
|
74056
74489
|
return usage3(io, "pass --all or paths, not both", COMMIT_USAGE);
|
|
74490
|
+
if (parsed.noEdit && !parsed.amend)
|
|
74491
|
+
return usage3(io, "--no-edit only applies with --amend", COMMIT_USAGE);
|
|
74492
|
+
const given = parsed.messages.length + parsed.files.length > 0;
|
|
74493
|
+
if (parsed.noEdit && given)
|
|
74494
|
+
return usage3(io, "--no-edit keeps the message; drop -m/-F or --no-edit", COMMIT_USAGE);
|
|
74495
|
+
if (!given && !parsed.noEdit)
|
|
74496
|
+
return usage3(io, parsed.amend ? "--amend needs -m/-F or --no-edit" : "missing -m <msg> or -F <file>", COMMIT_USAGE);
|
|
74497
|
+
const message = given ? commitMessage(parsed, io) : null;
|
|
74498
|
+
if (message !== null && typeof message !== "string")
|
|
74499
|
+
return usage3(io, message.error, COMMIT_USAGE);
|
|
74500
|
+
if (message !== null && !message.trim())
|
|
74501
|
+
return usage3(io, "the commit message is empty", COMMIT_USAGE);
|
|
74057
74502
|
const result = gitCommit(io.cwd, {
|
|
74058
|
-
message
|
|
74059
|
-
|
|
74060
|
-
`),
|
|
74503
|
+
message,
|
|
74061
74504
|
all: parsed.all,
|
|
74062
74505
|
paths: parsed.paths,
|
|
74506
|
+
amend: parsed.amend,
|
|
74507
|
+
allowEmpty: parsed.allowEmpty,
|
|
74063
74508
|
actor: actorFromEnv(io.env),
|
|
74064
74509
|
env: io.env
|
|
74065
74510
|
});
|
|
@@ -74068,8 +74513,9 @@ async function commit(argv, io) {
|
|
|
74068
74513
|
await ensureImplicitTask(io);
|
|
74069
74514
|
return emit(io, ok(result.data), (data) => [
|
|
74070
74515
|
`${data.branch} ${short2(data.sha)} ${data.message.split(`
|
|
74071
|
-
`, 1)[0]} (${data.files.length} file${data.files.length === 1 ? "" : "s"})`,
|
|
74516
|
+
`, 1)[0]} (${data.files.length ? `${data.files.length} file${data.files.length === 1 ? "" : "s"}` : "empty commit"})${data.amended ? `, amends ${short2(data.amended)}` : ""}`,
|
|
74072
74517
|
...data.leftDirty ? [`${data.leftDirty} change(s) left uncommitted`] : [],
|
|
74518
|
+
...data.notes.map((note) => `note: ${note}`),
|
|
74073
74519
|
..."error" in data.recorded ? [`ledger: not recorded (${data.recorded.error})`] : [],
|
|
74074
74520
|
...data.session ? [] : ["note: WORKIT_SESSION_ID is unset; this commit has no session author"]
|
|
74075
74521
|
]);
|
|
@@ -74096,24 +74542,37 @@ async function push(argv, io) {
|
|
|
74096
74542
|
const plan = pushPreflight(io.cwd);
|
|
74097
74543
|
if (!plan.ok)
|
|
74098
74544
|
return forgeFail(io, plan);
|
|
74099
|
-
|
|
74100
|
-
|
|
74101
|
-
|
|
74102
|
-
|
|
74103
|
-
}
|
|
74104
|
-
const grant = requireGrant(io.cwd, "push", { forge: !plan.data.local });
|
|
74545
|
+
const identity = plan.data.local ? null : pushIdentity(io, plan.data.branch);
|
|
74546
|
+
if (identity && !identity.ok)
|
|
74547
|
+
return forgeFail(io, identity);
|
|
74548
|
+
const checked = identity?.data ?? null;
|
|
74549
|
+
const grant = requireGrant(io.cwd, "push", { forge: checked?.forge != null });
|
|
74105
74550
|
if (!grant.allowed)
|
|
74106
|
-
return emit(io, fail("blocked", grant.error, {
|
|
74551
|
+
return emit(io, fail("blocked", grant.error, {
|
|
74552
|
+
unblock: grant.unblock,
|
|
74553
|
+
data: { reason: grant.reason }
|
|
74554
|
+
}));
|
|
74107
74555
|
const result = executePush(io.cwd, plan.data, {
|
|
74108
74556
|
forceWithLease: flags.booleans.has("force-with-lease"),
|
|
74109
74557
|
overwriteUnintegrated: flags.booleans.has("overwrite-unintegrated"),
|
|
74110
74558
|
expect,
|
|
74111
74559
|
setUpstream: flags.booleans.has("set-upstream"),
|
|
74112
|
-
actor: actorFromEnv(io.env)
|
|
74560
|
+
actor: actorFromEnv(io.env),
|
|
74561
|
+
...checked ? { identity: { ...checked } } : {}
|
|
74113
74562
|
});
|
|
74114
74563
|
if (!result.ok)
|
|
74115
|
-
return forgeFail(io, result, {
|
|
74116
|
-
|
|
74564
|
+
return forgeFail(io, result, {
|
|
74565
|
+
branch: plan.data.branch,
|
|
74566
|
+
sha: plan.data.sha
|
|
74567
|
+
});
|
|
74568
|
+
const warnings = checked?.status === "skipped" ? [
|
|
74569
|
+
`identity check skipped: ${checked.reason}${checked.unblock ? ` (to restore it: ${checked.unblock})` : ""}`
|
|
74570
|
+
] : [];
|
|
74571
|
+
for (const warning of warnings)
|
|
74572
|
+
io.stderr(`warning: ${warning}
|
|
74573
|
+
`);
|
|
74574
|
+
const envelope = ok({ ...result.data, identity: checked });
|
|
74575
|
+
return emit(io, warnings.length ? { ...envelope, warnings } : envelope, (data) => [
|
|
74117
74576
|
data.pushed ? `pushed ${data.branch} ${short2(data.previous)} -> ${short2(data.sha)} to ${data.remote}${data.forced ? " (force-with-lease)" : ""}; remote tip verified` : `${data.remote}/${data.branch} is already at ${short2(data.sha)}; remote tip verified`,
|
|
74118
74577
|
...data.upstream ? [`upstream: ${data.remote}/${data.branch}`] : []
|
|
74119
74578
|
]);
|
|
@@ -74128,7 +74587,7 @@ async function run12(argv, io) {
|
|
|
74128
74587
|
return push(rest, io);
|
|
74129
74588
|
return usage3(io, sub && !sub.startsWith("-") ? `unknown git subcommand "${sub}"` : "missing git subcommand", USAGE5);
|
|
74130
74589
|
}
|
|
74131
|
-
var BRANCH_USAGE = "workit git branch <name> | --kind feature|bugfix|hotfix --slug <s> [--base <b>] [--track <t>] [--carry] [--json]", COMMIT_USAGE = "workit git commit -m <msg
|
|
74590
|
+
var BRANCH_USAGE = "workit git branch <name> | --kind feature|bugfix|hotfix --slug <s> [--base <b>] [--track <t>] [--carry] [--json]", COMMIT_USAGE = "workit git commit -m|--message <msg>… | -F|--file <file|-> [--amend [--no-edit]] [--allow-empty] [-a|--all | [--] <paths…>] [--json]", PUSH_USAGE = "workit git push [--set-upstream] [--force-with-lease [--expect <sha>] [--overwrite-unintegrated]] [--json]", USAGE5 = "workit git branch|commit|push ... (workit help git)", short2 = (sha) => sha ? sha.slice(0, 12) : "(none)", COMMIT_SWITCHES;
|
|
74132
74591
|
var init_git2 = __esm(() => {
|
|
74133
74592
|
init_autonomy();
|
|
74134
74593
|
init_ops();
|
|
@@ -74136,10 +74595,17 @@ var init_git2 = __esm(() => {
|
|
|
74136
74595
|
init_implicit_task();
|
|
74137
74596
|
init_output();
|
|
74138
74597
|
init_forge_common();
|
|
74598
|
+
COMMIT_SWITCHES = {
|
|
74599
|
+
"-a": "all",
|
|
74600
|
+
"--all": "all",
|
|
74601
|
+
"--amend": "amend",
|
|
74602
|
+
"--no-edit": "noEdit",
|
|
74603
|
+
"--allow-empty": "allowEmpty"
|
|
74604
|
+
};
|
|
74139
74605
|
});
|
|
74140
74606
|
|
|
74141
74607
|
// packages/workit-core/src/forge/verify.ts
|
|
74142
|
-
import { spawnSync as
|
|
74608
|
+
import { spawnSync as spawnSync20 } from "node:child_process";
|
|
74143
74609
|
function finish(cwd, report, actor) {
|
|
74144
74610
|
const delivered = report.observations.length > 0 && report.observations.every((entry) => entry.ok);
|
|
74145
74611
|
const out = { ...report, delivered };
|
|
@@ -74352,7 +74818,7 @@ var NPM_NAME, SEMVER, SAFE_ARG, systemNpm = (args) => {
|
|
|
74352
74818
|
};
|
|
74353
74819
|
const found = process.platform === "win32" ? findExecutable("npm.cmd", process.env) : null;
|
|
74354
74820
|
const npmCmd = found && !/["%!^]/u.test(found) ? `"${found}"` : "npm.cmd";
|
|
74355
|
-
const result = process.platform === "win32" ?
|
|
74821
|
+
const result = process.platform === "win32" ? spawnSync20(`${npmCmd} ${args.join(" ")}`, { ...options, shell: true }) : spawnSync20("npm", [...args], options);
|
|
74356
74822
|
return { status: result.status, stdout: result.stdout ?? "", stderr: result.stderr ?? "" };
|
|
74357
74823
|
}, short3 = (sha) => sha ? sha.slice(0, 12) : "(absent)", npmView = (stdout) => {
|
|
74358
74824
|
try {
|
|
@@ -74459,10 +74925,10 @@ var init_verify_delivery = __esm(() => {
|
|
|
74459
74925
|
});
|
|
74460
74926
|
|
|
74461
74927
|
// packages/workit-core/src/stack.ts
|
|
74462
|
-
import { spawnSync as
|
|
74928
|
+
import { spawnSync as spawnSync21 } from "node:child_process";
|
|
74463
74929
|
import { createHash as createHash15, randomBytes as randomBytes3 } from "node:crypto";
|
|
74464
74930
|
import fs22 from "node:fs";
|
|
74465
|
-
import
|
|
74931
|
+
import path44 from "node:path";
|
|
74466
74932
|
function worktreeOf(cwd, branch) {
|
|
74467
74933
|
const list = gitRun3(cwd, ["worktree", "list", "--porcelain"]);
|
|
74468
74934
|
if (!list.ok)
|
|
@@ -74506,7 +74972,7 @@ function gitRestack(cwd, input) {
|
|
|
74506
74972
|
if (!scratch)
|
|
74507
74973
|
return { ok: false, conflict: false, worktree: cwd, error: "no store root" };
|
|
74508
74974
|
fs22.mkdirSync(scratch, { recursive: true });
|
|
74509
|
-
dir =
|
|
74975
|
+
dir = path44.join(scratch, `${input.branch.replace(/[^A-Za-z0-9._-]+/gu, "_").slice(0, 40)}-${randomBytes3(4).toString("hex")}`);
|
|
74510
74976
|
const added = gitRun3(cwd, ["worktree", "add", "-q", dir, input.branch], GIT_TIMEOUTS.worktree);
|
|
74511
74977
|
if (!added.ok)
|
|
74512
74978
|
return { ok: false, conflict: false, worktree: cwd, error: firstLine(added) };
|
|
@@ -74547,7 +75013,7 @@ function pruneScratch(cwd) {
|
|
|
74547
75013
|
if (!scratch || !fs22.existsSync(scratch))
|
|
74548
75014
|
return;
|
|
74549
75015
|
for (const name of fs22.readdirSync(scratch)) {
|
|
74550
|
-
const dir =
|
|
75016
|
+
const dir = path44.join(scratch, name);
|
|
74551
75017
|
if (!rebaseInProgress(dir) && !trackedDirt(dir))
|
|
74552
75018
|
gitRun3(cwd, ["worktree", "remove", "--force", dir]);
|
|
74553
75019
|
}
|
|
@@ -74557,7 +75023,7 @@ function stacksDir(cwd) {
|
|
|
74557
75023
|
const root = storeRoot(cwd);
|
|
74558
75024
|
if (!root.ok)
|
|
74559
75025
|
return stackFail(root.code, root.error, root.unblock);
|
|
74560
|
-
return { ok: true, data:
|
|
75026
|
+
return { ok: true, data: path44.join(root.value.root, "stacks") };
|
|
74561
75027
|
}
|
|
74562
75028
|
function parseStack(raw) {
|
|
74563
75029
|
if (!isRecord7(raw) || !Array.isArray(raw.branches))
|
|
@@ -74609,7 +75075,7 @@ function readStack(cwd, name) {
|
|
|
74609
75075
|
const dir = stacksDir(cwd);
|
|
74610
75076
|
if (!dir.ok)
|
|
74611
75077
|
return dir;
|
|
74612
|
-
const file =
|
|
75078
|
+
const file = path44.join(dir.data, fileFor(name));
|
|
74613
75079
|
let text;
|
|
74614
75080
|
try {
|
|
74615
75081
|
text = fs22.readFileSync(file, "utf8");
|
|
@@ -74630,7 +75096,7 @@ function writeStack(cwd, stack, now = new Date) {
|
|
|
74630
75096
|
const dir = stacksDir(cwd);
|
|
74631
75097
|
if (!dir.ok)
|
|
74632
75098
|
return dir;
|
|
74633
|
-
const file =
|
|
75099
|
+
const file = path44.join(dir.data, fileFor(stack.name));
|
|
74634
75100
|
const temp = `${file}.${process.pid}.tmp`;
|
|
74635
75101
|
try {
|
|
74636
75102
|
fs22.mkdirSync(dir.data, { recursive: true });
|
|
@@ -74658,7 +75124,7 @@ function listStacks(cwd) {
|
|
|
74658
75124
|
for (const name of names) {
|
|
74659
75125
|
let parsed = null;
|
|
74660
75126
|
try {
|
|
74661
|
-
parsed = parseStack(JSON.parse(fs22.readFileSync(
|
|
75127
|
+
parsed = parseStack(JSON.parse(fs22.readFileSync(path44.join(dir.data, name), "utf8")));
|
|
74662
75128
|
} catch {
|
|
74663
75129
|
parsed = null;
|
|
74664
75130
|
}
|
|
@@ -74717,7 +75183,7 @@ function takeLock(lock) {
|
|
|
74717
75183
|
host: localLockHost(),
|
|
74718
75184
|
nonce: randomBytes3(8).toString("hex")
|
|
74719
75185
|
};
|
|
74720
|
-
fs22.writeFileSync(
|
|
75186
|
+
fs22.writeFileSync(path44.join(lock, OWNER), JSON.stringify(owner));
|
|
74721
75187
|
return owner;
|
|
74722
75188
|
}
|
|
74723
75189
|
function reapLock(lock, observed) {
|
|
@@ -74754,7 +75220,7 @@ async function withStackLock(cwd, name, fn, waitMs = LOCK_WAIT_MS3) {
|
|
|
74754
75220
|
return dir;
|
|
74755
75221
|
if (!validName(name))
|
|
74756
75222
|
return stackFail("invalid_input", "a stack name must be 1-200 printable characters");
|
|
74757
|
-
const lock =
|
|
75223
|
+
const lock = path44.join(dir.data, `${fileFor(name)}.lock`);
|
|
74758
75224
|
try {
|
|
74759
75225
|
fs22.mkdirSync(dir.data, { recursive: true });
|
|
74760
75226
|
} catch (error) {
|
|
@@ -74784,7 +75250,7 @@ async function withStackLock(cwd, name, fn, waitMs = LOCK_WAIT_MS3) {
|
|
|
74784
75250
|
const touch = () => {
|
|
74785
75251
|
try {
|
|
74786
75252
|
const now = new Date;
|
|
74787
|
-
fs22.utimesSync(
|
|
75253
|
+
fs22.utimesSync(path44.join(lock, OWNER), now, now);
|
|
74788
75254
|
} catch {}
|
|
74789
75255
|
};
|
|
74790
75256
|
const beat = setInterval(touch, HEARTBEAT_MS);
|
|
@@ -74972,7 +75438,7 @@ function commitKeys(cwd, range) {
|
|
|
74972
75438
|
const patches = new Map;
|
|
74973
75439
|
const diff = gitRun3(cwd, ["log", "--no-merges", "-p", "--format=commit %H", range]);
|
|
74974
75440
|
if (diff.ok && diff.stdout.trim()) {
|
|
74975
|
-
const ids =
|
|
75441
|
+
const ids = spawnSync21("git", ["patch-id", "--stable"], {
|
|
74976
75442
|
cwd,
|
|
74977
75443
|
input: diff.stdout,
|
|
74978
75444
|
encoding: "utf8",
|
|
@@ -75596,7 +76062,7 @@ var STACK_VERSION = 1, stackFail = (code, error, unblock, data) => ({
|
|
|
75596
76062
|
...unblock ? { unblock } : {},
|
|
75597
76063
|
...data ? { data } : {}
|
|
75598
76064
|
}), fromForge = (result) => stackFail(result.code, result.error, result.unblock), stackDeps, gitRun3 = (cwd, args, timeoutMs = GIT_TIMEOUTS.local) => {
|
|
75599
|
-
const run =
|
|
76065
|
+
const run = spawnSync21("git", args, {
|
|
75600
76066
|
cwd,
|
|
75601
76067
|
encoding: "utf8",
|
|
75602
76068
|
timeout: timeoutMs,
|
|
@@ -75624,7 +76090,7 @@ var STACK_VERSION = 1, stackFail = (code, error, unblock, data) => ({
|
|
|
75624
76090
|
return !status.ok || status.stdout.trim() !== "";
|
|
75625
76091
|
}, scratchDir = (cwd) => {
|
|
75626
76092
|
const dir = stacksDir(cwd);
|
|
75627
|
-
return dir.ok ?
|
|
76093
|
+
return dir.ok ? path44.join(dir.data, "worktrees") : null;
|
|
75628
76094
|
}, firstLine = (run) => (`${run.stderr}
|
|
75629
76095
|
${run.stdout}`.split(`
|
|
75630
76096
|
`).find((line) => line.trim()) ?? "git failed").trim().slice(0, 300), stackFileName = (name) => `${name.replace(/[^A-Za-z0-9._-]+/gu, "_").replace(/^[._]+/u, "").slice(0, 48).toLowerCase()}-${createHash15("sha256").update(name).digest("hex").slice(0, 12)}.json`, fileFor, validName = (name) => {
|
|
@@ -75639,12 +76105,12 @@ ${run.stdout}`.split(`
|
|
|
75639
76105
|
}, isRecord7 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), str3 = (value) => typeof value === "string" && value ? value : null, LOCK_WAIT_MS3 = 2000, HEARTBEAT_MS = 20000, HEARTBEAT_STALE_MS, OWNERLESS_STALE_MS = 30000, OWNER = "owner", readOwner = (lock) => {
|
|
75640
76106
|
let raw = null;
|
|
75641
76107
|
try {
|
|
75642
|
-
raw = fs22.readFileSync(
|
|
76108
|
+
raw = fs22.readFileSync(path44.join(lock, OWNER), "utf8");
|
|
75643
76109
|
} catch {
|
|
75644
76110
|
raw = null;
|
|
75645
76111
|
}
|
|
75646
76112
|
let age = null;
|
|
75647
|
-
for (const target of [
|
|
76113
|
+
for (const target of [path44.join(lock, OWNER), lock])
|
|
75648
76114
|
try {
|
|
75649
76115
|
age = Date.now() - fs22.statSync(target).mtimeMs;
|
|
75650
76116
|
break;
|
|
@@ -75891,10 +76357,10 @@ var init_stack2 = __esm(() => {
|
|
|
75891
76357
|
});
|
|
75892
76358
|
|
|
75893
76359
|
// packages/workit-core/src/fanout.ts
|
|
75894
|
-
import { spawnSync as
|
|
76360
|
+
import { spawnSync as spawnSync22 } from "node:child_process";
|
|
75895
76361
|
import { createHash as createHash16 } from "node:crypto";
|
|
75896
76362
|
import fs23 from "node:fs";
|
|
75897
|
-
import
|
|
76363
|
+
import path45 from "node:path";
|
|
75898
76364
|
function expandBraces(pattern, limit = 64) {
|
|
75899
76365
|
for (let index = 0;index < pattern.length; index += 1) {
|
|
75900
76366
|
if (pattern[index] === "\\") {
|
|
@@ -76128,7 +76594,7 @@ function parsePlan(raw, defaults, name) {
|
|
|
76128
76594
|
id,
|
|
76129
76595
|
branch,
|
|
76130
76596
|
base: optional("base") ?? "",
|
|
76131
|
-
worktree:
|
|
76597
|
+
worktree: path45.resolve(defaults.root, optional("worktree") ?? path45.join(defaults.worktreeRoot, id)),
|
|
76132
76598
|
tier,
|
|
76133
76599
|
dependsOn: [...new Set(dependsOn ?? [])],
|
|
76134
76600
|
scope,
|
|
@@ -76357,11 +76823,11 @@ function defaultTrunk(cwd) {
|
|
|
76357
76823
|
}
|
|
76358
76824
|
function mainCheckout(cwd) {
|
|
76359
76825
|
const common = gitCommonDir(cwd);
|
|
76360
|
-
return common &&
|
|
76826
|
+
return common && path45.basename(common) === ".git" ? path45.dirname(common) : path45.resolve(cwd);
|
|
76361
76827
|
}
|
|
76362
76828
|
function worktreeRoot(cwd) {
|
|
76363
76829
|
const main = mainCheckout(cwd);
|
|
76364
|
-
return
|
|
76830
|
+
return path45.join(path45.dirname(main), `${path45.basename(main)}-wt`);
|
|
76365
76831
|
}
|
|
76366
76832
|
function sliceHash(slice) {
|
|
76367
76833
|
const {
|
|
@@ -76379,7 +76845,7 @@ function fanoutsDir(cwd) {
|
|
|
76379
76845
|
const root = storeRoot(cwd);
|
|
76380
76846
|
if (!root.ok)
|
|
76381
76847
|
return fanoutFail(root.code, root.error, root.unblock);
|
|
76382
|
-
return { ok: true, data:
|
|
76848
|
+
return { ok: true, data: path45.join(root.value.root, "fanouts") };
|
|
76383
76849
|
}
|
|
76384
76850
|
function parseStored(raw) {
|
|
76385
76851
|
if (!isRecord8(raw) || typeof raw.name !== "string" || typeof raw.trunk !== "string")
|
|
@@ -76445,7 +76911,7 @@ function readFanout(cwd, name) {
|
|
|
76445
76911
|
const dir = fanoutsDir(cwd);
|
|
76446
76912
|
if (!dir.ok)
|
|
76447
76913
|
return dir;
|
|
76448
|
-
const file =
|
|
76914
|
+
const file = path45.join(dir.data, stackFileName(name));
|
|
76449
76915
|
let parsed = null;
|
|
76450
76916
|
try {
|
|
76451
76917
|
parsed = parseStored(JSON.parse(fs23.readFileSync(file, "utf8")));
|
|
@@ -76470,7 +76936,7 @@ function listFanouts(cwd) {
|
|
|
76470
76936
|
for (const entry of names) {
|
|
76471
76937
|
let parsed = null;
|
|
76472
76938
|
try {
|
|
76473
|
-
parsed = parseStored(JSON.parse(fs23.readFileSync(
|
|
76939
|
+
parsed = parseStored(JSON.parse(fs23.readFileSync(path45.join(dir.data, entry), "utf8")));
|
|
76474
76940
|
} catch {
|
|
76475
76941
|
parsed = null;
|
|
76476
76942
|
}
|
|
@@ -76500,7 +76966,7 @@ function writeFanout(cwd, plan) {
|
|
|
76500
76966
|
const dir = fanoutsDir(cwd);
|
|
76501
76967
|
if (!dir.ok)
|
|
76502
76968
|
return dir;
|
|
76503
|
-
const file =
|
|
76969
|
+
const file = path45.join(dir.data, stackFileName(plan.name));
|
|
76504
76970
|
const temp = `${file}.${process.pid}.tmp`;
|
|
76505
76971
|
try {
|
|
76506
76972
|
fs23.mkdirSync(dir.data, { recursive: true });
|
|
@@ -76664,7 +77130,7 @@ var FANOUT_VERSION = 1, TIERS, FAN_IN_MODES, fanoutFail = (code, error, unblock,
|
|
|
76664
77130
|
text: edges.map((edge) => `add "${edge.dependsOn}" to "dependsOn" of slice ${edge.slice}`).join("; ")
|
|
76665
77131
|
};
|
|
76666
77132
|
}, gitRun4 = (cwd, args, timeoutMs = GIT_TIMEOUTS.local) => {
|
|
76667
|
-
const run =
|
|
77133
|
+
const run = spawnSync22("git", args, {
|
|
76668
77134
|
cwd,
|
|
76669
77135
|
encoding: "utf8",
|
|
76670
77136
|
timeout: timeoutMs,
|
|
@@ -76704,7 +77170,7 @@ var init_fanout = __esm(() => {
|
|
|
76704
77170
|
// packages/workit-core/src/fanout-worktree.ts
|
|
76705
77171
|
import { randomBytes as randomBytes4 } from "node:crypto";
|
|
76706
77172
|
import fs24 from "node:fs";
|
|
76707
|
-
import
|
|
77173
|
+
import path46 from "node:path";
|
|
76708
77174
|
function rowSample(lines) {
|
|
76709
77175
|
const out = [];
|
|
76710
77176
|
let bytes = 0;
|
|
@@ -76764,7 +77230,7 @@ function adminDir(worktree) {
|
|
|
76764
77230
|
const file = dir.stdout.trim();
|
|
76765
77231
|
let token = null;
|
|
76766
77232
|
try {
|
|
76767
|
-
token = fs24.readFileSync(
|
|
77233
|
+
token = fs24.readFileSync(path46.join(file, TOKEN_FILE), "utf8").trim() || null;
|
|
76768
77234
|
} catch {
|
|
76769
77235
|
token = null;
|
|
76770
77236
|
}
|
|
@@ -76772,7 +77238,7 @@ function adminDir(worktree) {
|
|
|
76772
77238
|
const stat = worktreeDeps.stat(file);
|
|
76773
77239
|
return {
|
|
76774
77240
|
path: file,
|
|
76775
|
-
id:
|
|
77241
|
+
id: path46.basename(file),
|
|
76776
77242
|
ino: stat.ino.toString(),
|
|
76777
77243
|
token,
|
|
76778
77244
|
birthMs: stat.birthtimeNs > 0n ? Number(stat.birthtimeMs) : null,
|
|
@@ -76788,7 +77254,7 @@ function markAdminDir(worktree) {
|
|
|
76788
77254
|
return null;
|
|
76789
77255
|
const token = randomBytes4(16).toString("hex");
|
|
76790
77256
|
try {
|
|
76791
|
-
fs24.writeFileSync(
|
|
77257
|
+
fs24.writeFileSync(path46.join(admin.path, TOKEN_FILE), `${token}
|
|
76792
77258
|
`);
|
|
76793
77259
|
} catch {
|
|
76794
77260
|
return null;
|
|
@@ -76803,7 +77269,7 @@ function excludeScratch(cwd) {
|
|
|
76803
77269
|
const common = gitCommonDir(cwd);
|
|
76804
77270
|
if (!common)
|
|
76805
77271
|
return;
|
|
76806
|
-
const file =
|
|
77272
|
+
const file = path46.join(common, "info", "exclude");
|
|
76807
77273
|
let text = "";
|
|
76808
77274
|
try {
|
|
76809
77275
|
text = fs24.readFileSync(file, "utf8");
|
|
@@ -76812,7 +77278,7 @@ function excludeScratch(cwd) {
|
|
|
76812
77278
|
}
|
|
76813
77279
|
if (text.split(/\r?\n/u).includes(EXCLUDE_LINE))
|
|
76814
77280
|
return;
|
|
76815
|
-
fs24.mkdirSync(
|
|
77281
|
+
fs24.mkdirSync(path46.dirname(file), { recursive: true });
|
|
76816
77282
|
fs24.appendFileSync(file, `${text && !text.endsWith(`
|
|
76817
77283
|
`) ? `
|
|
76818
77284
|
` : ""}# workit fanout: per-worker scratch dirs
|
|
@@ -76820,7 +77286,7 @@ ${EXCLUDE_LINE}
|
|
|
76820
77286
|
`);
|
|
76821
77287
|
}
|
|
76822
77288
|
function ensureScratch(worktree) {
|
|
76823
|
-
const scratch =
|
|
77289
|
+
const scratch = path46.join(worktree, SCRATCH_DIR);
|
|
76824
77290
|
fs24.mkdirSync(scratch, { recursive: true });
|
|
76825
77291
|
return scratch;
|
|
76826
77292
|
}
|
|
@@ -77038,7 +77504,7 @@ function unpushedCount(cwd, branch) {
|
|
|
77038
77504
|
return count.ok ? Number(count.stdout.trim()) : null;
|
|
77039
77505
|
}
|
|
77040
77506
|
function removeScratch(worktree) {
|
|
77041
|
-
const scratch =
|
|
77507
|
+
const scratch = path46.join(worktree, SCRATCH_DIR);
|
|
77042
77508
|
let stat;
|
|
77043
77509
|
try {
|
|
77044
77510
|
stat = fs24.lstatSync(scratch);
|
|
@@ -77054,16 +77520,16 @@ var SLOW, SCRATCH_DIR = ".workit-scratch", EXCLUDE_LINE, DIRTY_ROW_BYTES = 2000,
|
|
|
77054
77520
|
try {
|
|
77055
77521
|
return fs24.realpathSync.native(target);
|
|
77056
77522
|
} catch {
|
|
77057
|
-
return
|
|
77523
|
+
return path46.resolve(target);
|
|
77058
77524
|
}
|
|
77059
77525
|
}, samePath = (a, b) => {
|
|
77060
77526
|
const left = canonical2(a);
|
|
77061
77527
|
const right = canonical2(b);
|
|
77062
77528
|
return process.platform === "win32" || process.platform === "darwin" ? left.toLowerCase() === right.toLowerCase() : left === right;
|
|
77063
77529
|
}, inside2 = (child, parent) => {
|
|
77064
|
-
const relative =
|
|
77065
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
77066
|
-
}, slicePath = (cwd, slice) =>
|
|
77530
|
+
const relative = path46.relative(canonical2(parent), canonical2(child));
|
|
77531
|
+
return relative === "" || !relative.startsWith("..") && !path46.isAbsolute(relative);
|
|
77532
|
+
}, slicePath = (cwd, slice) => path46.isAbsolute(slice.worktree) ? slice.worktree : path46.resolve(mainCheckout(cwd), slice.worktree), CLOCK_SLACK_MS = 2000, TOKEN_FILE = "workit-fanout-token", worktreeDeps, worktreeFailed = (stderr) => fanoutFail("failed", `git worktree add failed: ${stderr.trim().split(`
|
|
77067
77533
|
`)[0] || "unknown error"}`);
|
|
77068
77534
|
var init_fanout_worktree = __esm(() => {
|
|
77069
77535
|
init_ops();
|
|
@@ -77184,7 +77650,7 @@ var init_fanout_brief = __esm(() => {
|
|
|
77184
77650
|
});
|
|
77185
77651
|
|
|
77186
77652
|
// packages/workit-core/src/fanout-landed.ts
|
|
77187
|
-
import { spawnSync as
|
|
77653
|
+
import { spawnSync as spawnSync23 } from "node:child_process";
|
|
77188
77654
|
function forgeBreaker(resolved) {
|
|
77189
77655
|
if (!resolved)
|
|
77190
77656
|
return { forge: null, down: () => null };
|
|
@@ -77275,7 +77741,7 @@ function trunkPatchIds(ctx, fork) {
|
|
|
77275
77741
|
"--"
|
|
77276
77742
|
]);
|
|
77277
77743
|
if (log.ok && log.stdout.trim()) {
|
|
77278
|
-
const run =
|
|
77744
|
+
const run = spawnSync23("git", ["patch-id", "--stable"], {
|
|
77279
77745
|
cwd: ctx.cwd,
|
|
77280
77746
|
input: log.stdout,
|
|
77281
77747
|
encoding: "utf8",
|
|
@@ -77839,7 +78305,7 @@ __export(exports_fanout, {
|
|
|
77839
78305
|
run: () => run15
|
|
77840
78306
|
});
|
|
77841
78307
|
import fs25 from "node:fs";
|
|
77842
|
-
import
|
|
78308
|
+
import path47 from "node:path";
|
|
77843
78309
|
function forgeFor(io, offline) {
|
|
77844
78310
|
if (offline)
|
|
77845
78311
|
return { forge: null, error: "--offline" };
|
|
@@ -77900,7 +78366,7 @@ function plan2(argv, io) {
|
|
|
77900
78366
|
return usage3(io, "pass --trunk or --track, not both (--track picks the trunk)", PLAN_USAGE2);
|
|
77901
78367
|
if (flags.positionals.length !== 1)
|
|
77902
78368
|
return usage3(io, flags.positionals.length ? `unexpected argument ${flags.positionals[1]}` : "missing <plan.json>", PLAN_USAGE2);
|
|
77903
|
-
const file =
|
|
78369
|
+
const file = path47.resolve(io.cwd, flags.positionals[0]);
|
|
77904
78370
|
let raw;
|
|
77905
78371
|
try {
|
|
77906
78372
|
raw = JSON.parse(fs25.readFileSync(file, "utf8"));
|
|
@@ -93332,7 +93798,7 @@ var init_ast = __esm(() => {
|
|
|
93332
93798
|
|
|
93333
93799
|
// packages/workit-cli/src/test-audit/rules.ts
|
|
93334
93800
|
import { builtinModules } from "node:module";
|
|
93335
|
-
import
|
|
93801
|
+
import path48 from "node:path";
|
|
93336
93802
|
function testCallee(call) {
|
|
93337
93803
|
let callee = call.callee;
|
|
93338
93804
|
if (callee.type === "CallExpression")
|
|
@@ -93825,12 +94291,12 @@ function checkConstantsOnly(ctx, emit) {
|
|
|
93825
94291
|
}
|
|
93826
94292
|
function checkOverMocking(ctx, emit) {
|
|
93827
94293
|
const { program } = ctx.parsed;
|
|
93828
|
-
const unit =
|
|
94294
|
+
const unit = path48.basename(ctx.file).replace(/\.(?:[cm]?[jt]sx?)$/, "").replace(/\.(?:test|spec)$/, "");
|
|
93829
94295
|
const mocked = find2(program, (node) => node.type === "CallExpression" && MODULE_MOCK.test(calleeName(node.callee) ?? "") && node.arguments[0]?.type === "StringLiteral");
|
|
93830
94296
|
const internal = mocked.filter((call) => /^(?:\.|@\/|~\/)/.test(call.arguments[0].value));
|
|
93831
94297
|
for (const call of mocked) {
|
|
93832
94298
|
const spec = call.arguments[0].value;
|
|
93833
|
-
const base =
|
|
94299
|
+
const base = path48.basename(spec.replace(/\/index(?:\.[cm]?[jt]sx?)?$/, "")).replace(/\.(?:[cm]?[jt]sx?)$/, "");
|
|
93834
94300
|
if (base === unit)
|
|
93835
94301
|
emit({
|
|
93836
94302
|
rule: "over-mocking",
|
|
@@ -94218,11 +94684,11 @@ var init_rules = __esm(() => {
|
|
|
94218
94684
|
});
|
|
94219
94685
|
|
|
94220
94686
|
// packages/workit-cli/src/test-audit/audit.ts
|
|
94221
|
-
import { spawnSync as
|
|
94222
|
-
import { existsSync as
|
|
94223
|
-
import
|
|
94687
|
+
import { spawnSync as spawnSync24 } from "node:child_process";
|
|
94688
|
+
import { existsSync as existsSync19, readdirSync as readdirSync8, readFileSync as readFileSync21, statSync as statSync7 } from "node:fs";
|
|
94689
|
+
import path49 from "node:path";
|
|
94224
94690
|
function git6(cwd, args) {
|
|
94225
|
-
const run =
|
|
94691
|
+
const run = spawnSync24("git", ["-c", "core.quotepath=off", ...args], {
|
|
94226
94692
|
cwd,
|
|
94227
94693
|
encoding: "utf8",
|
|
94228
94694
|
maxBuffer: 64 * 1024 * 1024
|
|
@@ -94233,7 +94699,7 @@ function walkTests(dir, out) {
|
|
|
94233
94699
|
for (const entry of readdirSync8(dir, { withFileTypes: true })) {
|
|
94234
94700
|
if (entry.name.startsWith(".") && entry.name !== "." && entry.isDirectory())
|
|
94235
94701
|
continue;
|
|
94236
|
-
const full =
|
|
94702
|
+
const full = path49.join(dir, entry.name);
|
|
94237
94703
|
if (entry.isDirectory()) {
|
|
94238
94704
|
if (!SKIP_DIRS.has(entry.name))
|
|
94239
94705
|
walkTests(full, out);
|
|
@@ -94245,8 +94711,8 @@ function expandPaths(cwd, inputs) {
|
|
|
94245
94711
|
const files = [];
|
|
94246
94712
|
const missing = [];
|
|
94247
94713
|
for (const input of inputs.length > 0 ? inputs : ["."]) {
|
|
94248
|
-
const full =
|
|
94249
|
-
if (!
|
|
94714
|
+
const full = path49.resolve(cwd, input);
|
|
94715
|
+
if (!existsSync19(full))
|
|
94250
94716
|
missing.push(input);
|
|
94251
94717
|
else if (statSync7(full).isDirectory())
|
|
94252
94718
|
walkTests(full, files);
|
|
@@ -94282,11 +94748,11 @@ function changedLines(cwd, base) {
|
|
|
94282
94748
|
for (let offset = 0;offset < count; offset += 1)
|
|
94283
94749
|
lines.add(start + offset);
|
|
94284
94750
|
}
|
|
94285
|
-
changed.set(
|
|
94751
|
+
changed.set(path49.join(root, file), lines);
|
|
94286
94752
|
}
|
|
94287
94753
|
const untracked = git6(root, ["ls-files", "--others", "--exclude-standard", "-z"]);
|
|
94288
94754
|
for (const file of untracked.stdout.split("\x00").filter(Boolean))
|
|
94289
|
-
changed.set(
|
|
94755
|
+
changed.set(path49.join(root, file), "all");
|
|
94290
94756
|
return changed;
|
|
94291
94757
|
}
|
|
94292
94758
|
function auditFiles(files, display) {
|
|
@@ -94299,7 +94765,7 @@ function auditFiles(files, display) {
|
|
|
94299
94765
|
const shown = display(file);
|
|
94300
94766
|
let parsed;
|
|
94301
94767
|
try {
|
|
94302
|
-
parsed = parseSource(
|
|
94768
|
+
parsed = parseSource(readFileSync21(file, "utf8"), file);
|
|
94303
94769
|
} catch (error) {
|
|
94304
94770
|
parseErrors.push({
|
|
94305
94771
|
file: shown,
|
|
@@ -94348,7 +94814,7 @@ function auditFiles(files, display) {
|
|
|
94348
94814
|
durationMs: Math.round(performance.now() - started)
|
|
94349
94815
|
};
|
|
94350
94816
|
}
|
|
94351
|
-
var TEST_FILE, CODE_FILE, SKIP_DIRS, toPosix = (file) => file.split(
|
|
94817
|
+
var TEST_FILE, CODE_FILE, SKIP_DIRS, toPosix = (file) => file.split(path49.sep).join("/"), SEVERITY_ORDER, ruleIds;
|
|
94352
94818
|
var init_audit = __esm(() => {
|
|
94353
94819
|
init_ast();
|
|
94354
94820
|
init_rules();
|
|
@@ -94362,12 +94828,12 @@ var init_audit = __esm(() => {
|
|
|
94362
94828
|
// packages/workit-cli/src/test-audit/mutate.ts
|
|
94363
94829
|
import { spawn as spawn2 } from "node:child_process";
|
|
94364
94830
|
import {
|
|
94365
|
-
existsSync as
|
|
94831
|
+
existsSync as existsSync20,
|
|
94366
94832
|
lstatSync as lstatSync7,
|
|
94367
94833
|
mkdirSync as mkdirSync9,
|
|
94368
94834
|
mkdtempSync as mkdtempSync4,
|
|
94369
94835
|
readdirSync as readdirSync9,
|
|
94370
|
-
readFileSync as
|
|
94836
|
+
readFileSync as readFileSync22,
|
|
94371
94837
|
readlinkSync as readlinkSync3,
|
|
94372
94838
|
realpathSync as realpathSync9,
|
|
94373
94839
|
renameSync as renameSync5,
|
|
@@ -94378,7 +94844,7 @@ import {
|
|
|
94378
94844
|
copyFileSync as copyFileSync6
|
|
94379
94845
|
} from "node:fs";
|
|
94380
94846
|
import os14 from "node:os";
|
|
94381
|
-
import
|
|
94847
|
+
import path50 from "node:path";
|
|
94382
94848
|
function mutantsFor(source, file, lines) {
|
|
94383
94849
|
const parsed = parseSource(source, file);
|
|
94384
94850
|
const out = [];
|
|
@@ -94451,7 +94917,7 @@ function relatedTests(source, testFiles) {
|
|
|
94451
94917
|
for (const match of text.matchAll(SPECIFIER)) {
|
|
94452
94918
|
const spec = match[1];
|
|
94453
94919
|
if (spec.startsWith(".")) {
|
|
94454
|
-
if (stripExt(
|
|
94920
|
+
if (stripExt(path50.posix.join(path50.posix.dirname(file), spec)) === target)
|
|
94455
94921
|
return true;
|
|
94456
94922
|
} else if (spec.includes("/") && stripExt(spec).split("/").slice(-2).join("/") === tail)
|
|
94457
94923
|
return true;
|
|
@@ -94471,12 +94937,12 @@ function removeStaleCopies(tmp = os14.tmpdir()) {
|
|
|
94471
94937
|
for (const name of names) {
|
|
94472
94938
|
if (!name.startsWith(TEMP_PREFIX))
|
|
94473
94939
|
continue;
|
|
94474
|
-
const dir =
|
|
94940
|
+
const dir = path50.join(tmp, name);
|
|
94475
94941
|
try {
|
|
94476
94942
|
const stat = lstatSync7(dir);
|
|
94477
94943
|
if (!stat.isDirectory() || uid !== undefined && stat.uid !== uid)
|
|
94478
94944
|
continue;
|
|
94479
|
-
const pid = Number(
|
|
94945
|
+
const pid = Number(readFileSync22(path50.join(dir, PID_FILE), "utf8"));
|
|
94480
94946
|
if (Number.isInteger(pid) && pid > 0 && alive(pid))
|
|
94481
94947
|
continue;
|
|
94482
94948
|
} catch (error) {
|
|
@@ -94493,8 +94959,8 @@ function removeStaleCopies(tmp = os14.tmpdir()) {
|
|
|
94493
94959
|
function linkNodeModules(origin, target, repo, copy) {
|
|
94494
94960
|
mkdirSync9(target, { recursive: true });
|
|
94495
94961
|
for (const entry of readdirSync9(origin, { withFileTypes: true })) {
|
|
94496
|
-
const from =
|
|
94497
|
-
const to =
|
|
94962
|
+
const from = path50.join(origin, entry.name);
|
|
94963
|
+
const to = path50.join(target, entry.name);
|
|
94498
94964
|
if (entry.name.startsWith("@") && entry.isDirectory()) {
|
|
94499
94965
|
linkNodeModules(from, to, repo, copy);
|
|
94500
94966
|
continue;
|
|
@@ -94510,9 +94976,9 @@ function linkNodeModules(origin, target, repo, copy) {
|
|
|
94510
94976
|
} catch {
|
|
94511
94977
|
continue;
|
|
94512
94978
|
}
|
|
94513
|
-
const relative =
|
|
94514
|
-
if (!relative.startsWith("..") && !
|
|
94515
|
-
symlinkSync(
|
|
94979
|
+
const relative = path50.relative(repo, real);
|
|
94980
|
+
if (!relative.startsWith("..") && !path50.isAbsolute(relative) && !relative.split(path50.sep).includes("node_modules")) {
|
|
94981
|
+
symlinkSync(path50.join(copy, relative), to, "junction");
|
|
94516
94982
|
continue;
|
|
94517
94983
|
}
|
|
94518
94984
|
}
|
|
@@ -94528,7 +94994,7 @@ function copySymlink(repo, tree, from, to) {
|
|
|
94528
94994
|
}
|
|
94529
94995
|
const realRepo = realpathSync9(repo);
|
|
94530
94996
|
if (real && inside3(realRepo, real)) {
|
|
94531
|
-
symlinkSync(
|
|
94997
|
+
symlinkSync(path50.relative(path50.dirname(to), path50.join(tree, path50.relative(realRepo, real))), to);
|
|
94532
94998
|
return;
|
|
94533
94999
|
}
|
|
94534
95000
|
if (real && statSync8(real).isFile())
|
|
@@ -94537,10 +95003,10 @@ function copySymlink(repo, tree, from, to) {
|
|
|
94537
95003
|
symlinkSync(readlinkSync3(from), to);
|
|
94538
95004
|
}
|
|
94539
95005
|
function writeInside(copy, file, content) {
|
|
94540
|
-
const target =
|
|
95006
|
+
const target = path50.join(copy, file);
|
|
94541
95007
|
try {
|
|
94542
95008
|
const stat = lstatSync7(target);
|
|
94543
|
-
if (!stat.isFile() || !inside3(realpathSync9(copy), realpathSync9(
|
|
95009
|
+
if (!stat.isFile() || !inside3(realpathSync9(copy), realpathSync9(path50.dirname(target))))
|
|
94544
95010
|
return false;
|
|
94545
95011
|
} catch {
|
|
94546
95012
|
return false;
|
|
@@ -94551,9 +95017,9 @@ function writeInside(copy, file, content) {
|
|
|
94551
95017
|
return true;
|
|
94552
95018
|
}
|
|
94553
95019
|
function copyWorkingTree(repo) {
|
|
94554
|
-
const dir = mkdtempSync4(
|
|
94555
|
-
writeFileSync10(
|
|
94556
|
-
const tree =
|
|
95020
|
+
const dir = mkdtempSync4(path50.join(os14.tmpdir(), TEMP_PREFIX));
|
|
95021
|
+
writeFileSync10(path50.join(dir, PID_FILE), String(process.pid));
|
|
95022
|
+
const tree = path50.join(dir, "tree");
|
|
94557
95023
|
const head = git6(repo, ["rev-parse", "--verify", "--quiet", "HEAD"]).stdout.trim();
|
|
94558
95024
|
if (head && git6(dir, ["clone", "-q", "--shared", "--no-checkout", repo, tree]).ok) {
|
|
94559
95025
|
const branch = git6(repo, ["symbolic-ref", "--quiet", "--short", "HEAD"]).stdout.trim();
|
|
@@ -94570,29 +95036,29 @@ function copyWorkingTree(repo) {
|
|
|
94570
95036
|
for (const relative of listed) {
|
|
94571
95037
|
if (!relative)
|
|
94572
95038
|
continue;
|
|
94573
|
-
const from =
|
|
95039
|
+
const from = path50.join(repo, relative);
|
|
94574
95040
|
let stat;
|
|
94575
95041
|
try {
|
|
94576
95042
|
stat = lstatSync7(from);
|
|
94577
95043
|
} catch {
|
|
94578
95044
|
continue;
|
|
94579
95045
|
}
|
|
94580
|
-
const to =
|
|
94581
|
-
mkdirSync9(
|
|
95046
|
+
const to = path50.join(tree, relative);
|
|
95047
|
+
mkdirSync9(path50.dirname(to), { recursive: true });
|
|
94582
95048
|
if (stat.isSymbolicLink())
|
|
94583
95049
|
copySymlink(repo, tree, from, to);
|
|
94584
95050
|
else if (stat.isFile())
|
|
94585
95051
|
copyFileSync6(from, to);
|
|
94586
|
-
let parent =
|
|
95052
|
+
let parent = path50.dirname(relative);
|
|
94587
95053
|
while (parent !== "." && !dirs.has(parent)) {
|
|
94588
95054
|
dirs.add(parent);
|
|
94589
|
-
parent =
|
|
95055
|
+
parent = path50.dirname(parent);
|
|
94590
95056
|
}
|
|
94591
95057
|
}
|
|
94592
95058
|
for (const parent of dirs) {
|
|
94593
|
-
const modules =
|
|
94594
|
-
if (
|
|
94595
|
-
linkNodeModules(modules,
|
|
95059
|
+
const modules = path50.join(repo, parent, "node_modules");
|
|
95060
|
+
if (existsSync20(modules) && !existsSync20(path50.join(tree, parent, "node_modules")))
|
|
95061
|
+
linkNodeModules(modules, path50.join(tree, parent, "node_modules"), repo, tree);
|
|
94596
95062
|
}
|
|
94597
95063
|
return { dir, tree };
|
|
94598
95064
|
}
|
|
@@ -94631,20 +95097,20 @@ async function runMutation(options) {
|
|
|
94631
95097
|
const tracked = git6(repo, ["ls-files", "-co", "--exclude-standard", "-z"]).stdout.split("\x00").filter(Boolean);
|
|
94632
95098
|
const testFiles = tracked.filter((file) => TEST_FILE.test(file)).map((file) => {
|
|
94633
95099
|
try {
|
|
94634
|
-
return { file, text:
|
|
95100
|
+
return { file, text: readFileSync22(path50.join(repo, file), "utf8") };
|
|
94635
95101
|
} catch {
|
|
94636
95102
|
return { file, text: "" };
|
|
94637
95103
|
}
|
|
94638
95104
|
});
|
|
94639
95105
|
const candidates = [];
|
|
94640
95106
|
for (const [absolute, lines] of [...options.changed].toSorted(([a], [b]) => a.localeCompare(b))) {
|
|
94641
|
-
const relative = toPosix(
|
|
95107
|
+
const relative = toPosix(path50.relative(repo, absolute));
|
|
94642
95108
|
if (!CODE_FILE.test(relative) || TEST_FILE.test(relative) || relative.endsWith(".d.ts"))
|
|
94643
95109
|
continue;
|
|
94644
|
-
if (NOT_SOURCE.test(relative) || !
|
|
95110
|
+
if (NOT_SOURCE.test(relative) || !existsSync20(absolute))
|
|
94645
95111
|
continue;
|
|
94646
95112
|
try {
|
|
94647
|
-
candidates.push(...mutantsFor(
|
|
95113
|
+
candidates.push(...mutantsFor(readFileSync22(absolute, "utf8"), relative, lines));
|
|
94648
95114
|
} catch {}
|
|
94649
95115
|
}
|
|
94650
95116
|
const mutants = candidates.map((mutant, index) => ({ ...mutant, id: index + 1 }));
|
|
@@ -94652,9 +95118,9 @@ async function runMutation(options) {
|
|
|
94652
95118
|
for (const mutant of mutants)
|
|
94653
95119
|
if (!related.has(mutant.file))
|
|
94654
95120
|
related.set(mutant.file, relatedTests(mutant.file, testFiles));
|
|
94655
|
-
const cwdRelative =
|
|
95121
|
+
const cwdRelative = path50.relative(repo, options.cwd);
|
|
94656
95122
|
const asArg = (file) => {
|
|
94657
|
-
const relative = toPosix(
|
|
95123
|
+
const relative = toPosix(path50.relative(cwdRelative, file));
|
|
94658
95124
|
return shellQuote(relative.startsWith(".") ? relative : `./${relative}`);
|
|
94659
95125
|
};
|
|
94660
95126
|
const commandFor = (tests) => options.command.replaceAll("{files}", () => tests.map(asArg).join(" "));
|
|
@@ -94689,7 +95155,7 @@ async function runMutation(options) {
|
|
|
94689
95155
|
return finish2(options, 0, results, counts, started);
|
|
94690
95156
|
removeStaleCopies();
|
|
94691
95157
|
const { dir: copyDir, tree: copy } = copyWorkingTree(repo);
|
|
94692
|
-
const copyCwd =
|
|
95158
|
+
const copyCwd = path50.join(copy, cwdRelative);
|
|
94693
95159
|
let current = null;
|
|
94694
95160
|
const restore = () => {
|
|
94695
95161
|
if (current)
|
|
@@ -94724,7 +95190,7 @@ async function runMutation(options) {
|
|
|
94724
95190
|
record({ ...mutant, status: "skipped", durationMs: 0, tests, reason: "budget" });
|
|
94725
95191
|
continue;
|
|
94726
95192
|
}
|
|
94727
|
-
const original =
|
|
95193
|
+
const original = readFileSync22(path50.join(copy, mutant.file), "utf8");
|
|
94728
95194
|
const mutated = original.slice(0, mutant.start) + mutant.replacement + original.slice(mutant.end);
|
|
94729
95195
|
current = { file: mutant.file, original };
|
|
94730
95196
|
if (!writeInside(copy, mutant.file, mutated)) {
|
|
@@ -94781,8 +95247,8 @@ var NOT_SOURCE, FLIPS, isStringy = (node) => node.type === "StringLiteral" || no
|
|
|
94781
95247
|
return error.code === "EPERM";
|
|
94782
95248
|
}
|
|
94783
95249
|
}, inside3 = (root, file) => {
|
|
94784
|
-
const relative =
|
|
94785
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
95250
|
+
const relative = path50.relative(root, file);
|
|
95251
|
+
return relative === "" || !relative.startsWith("..") && !path50.isAbsolute(relative);
|
|
94786
95252
|
}, active, killTree = (child) => {
|
|
94787
95253
|
try {
|
|
94788
95254
|
if (child.pid && process.platform !== "win32")
|
|
@@ -94830,8 +95296,8 @@ var exports_test_audit = {};
|
|
|
94830
95296
|
__export(exports_test_audit, {
|
|
94831
95297
|
run: () => run17
|
|
94832
95298
|
});
|
|
94833
|
-
import { existsSync as
|
|
94834
|
-
import
|
|
95299
|
+
import { existsSync as existsSync21 } from "node:fs";
|
|
95300
|
+
import path51 from "node:path";
|
|
94835
95301
|
function parse7(argv) {
|
|
94836
95302
|
const options = {
|
|
94837
95303
|
paths: [],
|
|
@@ -94935,8 +95401,8 @@ async function run17(argv, io) {
|
|
|
94935
95401
|
return emit(io, fail("invalid_input", `${options}
|
|
94936
95402
|
usage: ${USAGE10}`));
|
|
94937
95403
|
const top = git6(io.cwd, ["rev-parse", "--show-toplevel"]);
|
|
94938
|
-
const repo = top.ok ?
|
|
94939
|
-
const display = (file) => toPosix(
|
|
95404
|
+
const repo = top.ok ? path51.resolve(top.stdout.trim()) : null;
|
|
95405
|
+
const display = (file) => toPosix(path51.relative(io.cwd, file) || file);
|
|
94940
95406
|
let base = null;
|
|
94941
95407
|
let changed = null;
|
|
94942
95408
|
if (options.diff || options.mutate) {
|
|
@@ -94953,7 +95419,7 @@ usage: ${USAGE10}`));
|
|
|
94953
95419
|
}
|
|
94954
95420
|
let files;
|
|
94955
95421
|
if (options.diff && changed)
|
|
94956
|
-
files = [...changed.keys()].filter((file) => TEST_FILE.test(toPosix(file)) &&
|
|
95422
|
+
files = [...changed.keys()].filter((file) => TEST_FILE.test(toPosix(file)) && existsSync21(file)).toSorted();
|
|
94957
95423
|
else {
|
|
94958
95424
|
const expanded = expandPaths(io.cwd, options.paths);
|
|
94959
95425
|
if (expanded.missing.length > 0)
|
|
@@ -95178,8 +95644,8 @@ var exports_youtrack = {};
|
|
|
95178
95644
|
__export(exports_youtrack, {
|
|
95179
95645
|
run: () => run19
|
|
95180
95646
|
});
|
|
95181
|
-
import { readFileSync as
|
|
95182
|
-
import
|
|
95647
|
+
import { readFileSync as readFileSync23 } from "node:fs";
|
|
95648
|
+
import path52 from "node:path";
|
|
95183
95649
|
async function run19(argv, io) {
|
|
95184
95650
|
const [sub, ...rest] = argv;
|
|
95185
95651
|
if (sub !== "note" && sub !== "time" && sub !== "meeting")
|
|
@@ -95225,7 +95691,7 @@ async function run19(argv, io) {
|
|
|
95225
95691
|
let markdown = flags.values.markdown ?? "";
|
|
95226
95692
|
if (flags.values.file)
|
|
95227
95693
|
try {
|
|
95228
|
-
markdown =
|
|
95694
|
+
markdown = readFileSync23(path52.resolve(io.cwd, flags.values.file), "utf8");
|
|
95229
95695
|
} catch (error) {
|
|
95230
95696
|
return usage3(io, `cannot read ${flags.values.file}: ${String(error)}`, USAGE12);
|
|
95231
95697
|
}
|
|
@@ -95293,8 +95759,8 @@ var exports_changelog = {};
|
|
|
95293
95759
|
__export(exports_changelog, {
|
|
95294
95760
|
run: () => run20
|
|
95295
95761
|
});
|
|
95296
|
-
import { readFileSync as
|
|
95297
|
-
import
|
|
95762
|
+
import { readFileSync as readFileSync24 } from "node:fs";
|
|
95763
|
+
import path53 from "node:path";
|
|
95298
95764
|
async function run20(argv, io) {
|
|
95299
95765
|
const [sub, ...rest] = argv;
|
|
95300
95766
|
if (sub !== "apply")
|
|
@@ -95340,7 +95806,7 @@ async function run20(argv, io) {
|
|
|
95340
95806
|
]);
|
|
95341
95807
|
}
|
|
95342
95808
|
var USAGE13 = "workit changelog apply (--entries <JSON|@file|-> | --normalize-only) [--path CHANGELOG.md] [--preview]", readEntries = (raw, cwd) => {
|
|
95343
|
-
const text = raw === "-" ?
|
|
95809
|
+
const text = raw === "-" ? readFileSync24(0, "utf8") : raw.startsWith("@") ? readFileSync24(path53.resolve(cwd, raw.slice(1)), "utf8") : raw;
|
|
95344
95810
|
return JSON.parse(text);
|
|
95345
95811
|
};
|
|
95346
95812
|
var init_changelog2 = __esm(() => {
|
|
@@ -95436,7 +95902,7 @@ var init_handoff = __esm(() => {
|
|
|
95436
95902
|
|
|
95437
95903
|
// packages/workit-cli/src/main.ts
|
|
95438
95904
|
init_package();
|
|
95439
|
-
import
|
|
95905
|
+
import path54 from "node:path";
|
|
95440
95906
|
|
|
95441
95907
|
// packages/workit-cli/src/help.ts
|
|
95442
95908
|
var HELP_FLAGS = new Set(["--help", "-h"]);
|
|
@@ -95764,8 +96230,8 @@ var VERBS = [
|
|
|
95764
96230
|
},
|
|
95765
96231
|
{
|
|
95766
96232
|
name: "commit",
|
|
95767
|
-
usage: "workit git commit -m|--message <msg>… [-a|--all | [--] <paths…>]",
|
|
95768
|
-
summary: "Commit with a linted Conventional Commit message and the session trailer"
|
|
96233
|
+
usage: "workit git commit -m|--message <msg>… | -F|--file <file|-> [--amend [--no-edit]] [--allow-empty] [-a|--all | [--] <paths…>]",
|
|
96234
|
+
summary: "Commit (or --amend) with a linted Conventional Commit message and the session trailer"
|
|
95769
96235
|
},
|
|
95770
96236
|
{
|
|
95771
96237
|
name: "push",
|
|
@@ -95776,6 +96242,7 @@ var VERBS = [
|
|
|
95776
96242
|
examples: [
|
|
95777
96243
|
"workit git branch --kind feature --slug help-everywhere",
|
|
95778
96244
|
'workit git commit -m "fix(cli): answer help flags first" --all',
|
|
96245
|
+
"workit git commit --amend --no-edit -- src/forgotten.ts",
|
|
95779
96246
|
"workit git push --set-upstream"
|
|
95780
96247
|
],
|
|
95781
96248
|
load: () => Promise.resolve().then(() => (init_git2(), exports_git))
|
|
@@ -96056,7 +96523,7 @@ var defaultIo = (json) => ({
|
|
|
96056
96523
|
});
|
|
96057
96524
|
async function main(argv, overrides = {}, options = {}) {
|
|
96058
96525
|
const parsed = parseGlobals(argv);
|
|
96059
|
-
const cwd =
|
|
96526
|
+
const cwd = path54.resolve(overrides.cwd ?? process.cwd(), parsed.cwd ?? ".");
|
|
96060
96527
|
const io = { ...defaultIo(parsed.json), ...overrides, json: parsed.json, cwd };
|
|
96061
96528
|
if (parsed.error)
|
|
96062
96529
|
return emit(io, fail("invalid_input", parsed.error));
|
|
@@ -96114,9 +96581,21 @@ async function main(argv, overrides = {}, options = {}) {
|
|
|
96114
96581
|
globalThis[Symbol.for("workit.migrationReporter")] = (report) => io.stderr(`workit: migrated ${report.tasks} task${report.tasks === 1 ? "" : "s"} from ${report.from} to ${report.to} (backup: ${report.backup})
|
|
96115
96582
|
`);
|
|
96116
96583
|
const verb = await entry.load();
|
|
96584
|
+
const runVerb = (target) => guarded(target, () => verb.run(ended, target));
|
|
96117
96585
|
if (!io.json)
|
|
96118
|
-
return
|
|
96119
|
-
return runJsonPure(io, command,
|
|
96586
|
+
return runVerb(io);
|
|
96587
|
+
return runJsonPure(io, command, runVerb);
|
|
96588
|
+
}
|
|
96589
|
+
async function guarded(io, run) {
|
|
96590
|
+
try {
|
|
96591
|
+
return await run();
|
|
96592
|
+
} catch (error) {
|
|
96593
|
+
if (!(error instanceof Error) || error.name !== "WorkspaceConfigError")
|
|
96594
|
+
throw error;
|
|
96595
|
+
return emit(io, fail("blocked", error.message, {
|
|
96596
|
+
unblock: "fix ~/.config/workit/workspaces.json (workit init → Workspaces)"
|
|
96597
|
+
}));
|
|
96598
|
+
}
|
|
96120
96599
|
}
|
|
96121
96600
|
var CODE_FOR_EXIT = {
|
|
96122
96601
|
1: "failed",
|