tines 0.0.136 → 0.0.137
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/dist/index.js +133 -11
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4465,7 +4465,9 @@ async function resolveProject(api, ref) {
|
|
|
4465
4465
|
die(`no project named "${ref}" (have: ${have || "none"})`);
|
|
4466
4466
|
}
|
|
4467
4467
|
async function resolveWorkflow(api, ref) {
|
|
4468
|
-
|
|
4468
|
+
return pickWorkflow((await api.listWorkflows()).items, ref);
|
|
4469
|
+
}
|
|
4470
|
+
function pickWorkflow(items, ref) {
|
|
4469
4471
|
const found = items.find((w) => w.id === ref) ?? (items.filter((w) => w.name === ref).length === 1 ? items.find((w) => w.name === ref) : void 0);
|
|
4470
4472
|
if (found) return found;
|
|
4471
4473
|
if (items.filter((w) => w.name === ref).length > 1) {
|
|
@@ -7970,6 +7972,20 @@ existing sets wholesale when present.
|
|
|
7970
7972
|
Each NEW state should carry a "prompt" \u2014 its initial stage instructions,
|
|
7971
7973
|
created as a state-scoped context item \u2014 or pass --no-prompts to skip.
|
|
7972
7974
|
|
|
7975
|
+
A state may inherit context from another state \u2014 its base \u2014 with
|
|
7976
|
+
"inherits_from". Give the base as "<workflow>/<state>" (the qualified form
|
|
7977
|
+
\`tines workflows show\` prints, so it can be pasted straight back in), as a
|
|
7978
|
+
bare state name to mean one of this same request's states, or as a state id:
|
|
7979
|
+
|
|
7980
|
+
{ "id": "wfs_abc", "name": "Merging", "category": "active",
|
|
7981
|
+
"inherits_from": "Engineering/Review" }
|
|
7982
|
+
|
|
7983
|
+
Items scoped to the base are part of an issue's context in the child state,
|
|
7984
|
+
stitched before the child's own layer. Chains are at most 3 states long and
|
|
7985
|
+
may not cycle. On an EXISTING state (one with an "id") the field is
|
|
7986
|
+
merge-patch: leaving it out keeps the current base, and "inherits_from": null
|
|
7987
|
+
clears it. See \`tines workflows bases\` for the pointers already in place.
|
|
7988
|
+
|
|
7973
7989
|
A transition may declare artifact requirements ("requires"): it can then only
|
|
7974
7990
|
be taken once a FRESH artifact with that name \u2014 attached (or reaffirmed)
|
|
7975
7991
|
since the issue entered its current state \u2014 exists on the issue:
|
|
@@ -7983,18 +7999,92 @@ since the issue entered its current state \u2014 exists on the issue:
|
|
|
7983
7999
|
"image/"; file/text only) are optional narrowing; "artifact" is the slot
|
|
7984
8000
|
name issues must carry (see: tines issues artifacts --help).
|
|
7985
8001
|
`;
|
|
7986
|
-
function
|
|
8002
|
+
async function loadLibrary(api) {
|
|
8003
|
+
const workflows = await listAll((page) => api.listWorkflows(page));
|
|
8004
|
+
const states = /* @__PURE__ */ new Map();
|
|
8005
|
+
for (const workflow of workflows) {
|
|
8006
|
+
for (const state of workflow.states) states.set(state.id, { workflow, state });
|
|
8007
|
+
}
|
|
8008
|
+
const children = /* @__PURE__ */ new Map();
|
|
8009
|
+
for (const entry of states.values()) {
|
|
8010
|
+
const base = entry.state.inherits_from;
|
|
8011
|
+
if (base === null) continue;
|
|
8012
|
+
const siblings = children.get(base);
|
|
8013
|
+
if (siblings) siblings.push(entry);
|
|
8014
|
+
else children.set(base, [entry]);
|
|
8015
|
+
}
|
|
8016
|
+
return { workflows, states, children };
|
|
8017
|
+
}
|
|
8018
|
+
var qualify = (entry) => `${entry.workflow.name} / ${entry.state.name}`;
|
|
8019
|
+
function stateLabel(lib, id) {
|
|
8020
|
+
const entry = lib.states.get(id);
|
|
8021
|
+
return entry ? qualify(entry) : id;
|
|
8022
|
+
}
|
|
8023
|
+
function inheritanceLines(lib, state) {
|
|
8024
|
+
const lines = [];
|
|
8025
|
+
if (state.inherits_from !== null) {
|
|
8026
|
+
lines.push(` inherits from: ${stateLabel(lib, state.inherits_from)}`);
|
|
8027
|
+
}
|
|
8028
|
+
const kids = lib.children.get(state.id) ?? [];
|
|
8029
|
+
if (kids.length > 0) lines.push(` inherited by: ${kids.map(qualify).join(", ")}`);
|
|
8030
|
+
return lines;
|
|
8031
|
+
}
|
|
8032
|
+
async function resolveStateBases(states, library) {
|
|
8033
|
+
if (!Array.isArray(states)) return;
|
|
8034
|
+
let lib;
|
|
8035
|
+
for (const entry of states) {
|
|
8036
|
+
if (typeof entry !== "object" || entry === null) continue;
|
|
8037
|
+
const state = entry;
|
|
8038
|
+
if (typeof state.inherits_from !== "string" || !state.inherits_from.includes("/")) continue;
|
|
8039
|
+
lib ??= await library();
|
|
8040
|
+
state.inherits_from = resolveBasePair(
|
|
8041
|
+
lib,
|
|
8042
|
+
state.inherits_from,
|
|
8043
|
+
typeof state.name === "string" ? state.name : "(unnamed)"
|
|
8044
|
+
);
|
|
8045
|
+
}
|
|
8046
|
+
}
|
|
8047
|
+
function resolveBasePair(lib, pair, stateName) {
|
|
8048
|
+
const sep2 = pair.indexOf("/");
|
|
8049
|
+
const workflowRef = pair.slice(0, sep2).trim();
|
|
8050
|
+
const stateRef = pair.slice(sep2 + 1).trim();
|
|
8051
|
+
const where = `state "${stateName}" inherits from "${pair}"`;
|
|
8052
|
+
if (!workflowRef || !stateRef) {
|
|
8053
|
+
die(`${where}, which is not a <workflow>/<state> pair`);
|
|
8054
|
+
}
|
|
8055
|
+
const byName = lib.workflows.filter((w) => w.name === workflowRef);
|
|
8056
|
+
if (byName.length > 1)
|
|
8057
|
+
die(`${where}, but workflow name "${workflowRef}" is ambiguous; use an id`);
|
|
8058
|
+
const workflow = lib.workflows.find((w) => w.id === workflowRef) ?? byName[0];
|
|
8059
|
+
if (!workflow) {
|
|
8060
|
+
die(
|
|
8061
|
+
`${where}, but there is no workflow "${workflowRef}" (have: ${lib.workflows.map((w) => w.name).join(", ")})`
|
|
8062
|
+
);
|
|
8063
|
+
}
|
|
8064
|
+
const state = workflow.states.find((s) => s.name === stateRef) ?? workflow.states.find((s) => s.id === stateRef);
|
|
8065
|
+
if (!state) {
|
|
8066
|
+
die(
|
|
8067
|
+
`${where}, but workflow "${workflow.name}" has no state "${stateRef}" (have: ${workflow.states.map((s) => s.name).join(", ")})`
|
|
8068
|
+
);
|
|
8069
|
+
}
|
|
8070
|
+
return state.id;
|
|
8071
|
+
}
|
|
8072
|
+
function printWorkflowDetail(wf, lib) {
|
|
7987
8073
|
console.log(`${wf.name}${wf.is_system ? " (standard, read-only)" : ""} [${wf.id}]`);
|
|
7988
8074
|
if (wf.description) console.log(wf.description);
|
|
7989
8075
|
console.log("\nstates:");
|
|
7990
8076
|
const byId = new Map(wf.states.map((s) => [s.id, s]));
|
|
7991
|
-
|
|
8077
|
+
const rows = wf.states.length === 0 ? [] : formatTable(
|
|
7992
8078
|
wf.states.map((s) => [
|
|
7993
8079
|
` ${s.name}`,
|
|
7994
8080
|
s.category,
|
|
7995
8081
|
s.id === wf.initial_state_id ? "(initial)" : ""
|
|
7996
8082
|
])
|
|
7997
|
-
);
|
|
8083
|
+
).split("\n");
|
|
8084
|
+
for (const [i, row] of rows.entries()) {
|
|
8085
|
+
console.log(row);
|
|
8086
|
+
for (const line of inheritanceLines(lib, wf.states[i])) console.log(line);
|
|
8087
|
+
}
|
|
7998
8088
|
console.log("\ntransitions:");
|
|
7999
8089
|
for (const t of wf.transitions) {
|
|
8000
8090
|
console.log(
|
|
@@ -8034,10 +8124,38 @@ function register11(program3) {
|
|
|
8034
8124
|
withCommon(
|
|
8035
8125
|
workflows.command("show <id-or-name>").description("Show a workflow with states and transitions")
|
|
8036
8126
|
).action(async (ref, opts) => {
|
|
8037
|
-
const
|
|
8038
|
-
const wf =
|
|
8127
|
+
const lib = await loadLibrary(client(opts));
|
|
8128
|
+
const wf = pickWorkflow(lib.workflows, ref);
|
|
8039
8129
|
if (opts.json) return printJson(wf);
|
|
8040
|
-
printWorkflowDetail(wf);
|
|
8130
|
+
printWorkflowDetail(wf, lib);
|
|
8131
|
+
});
|
|
8132
|
+
withCommon(
|
|
8133
|
+
workflows.command("bases").description("List the states other states inherit context from, with their children")
|
|
8134
|
+
).action(async (opts) => {
|
|
8135
|
+
const lib = await loadLibrary(client(opts));
|
|
8136
|
+
const bases = [...lib.states.values()].filter((e) => lib.children.has(e.state.id));
|
|
8137
|
+
if (opts.json) {
|
|
8138
|
+
const ref = (e) => ({
|
|
8139
|
+
workflow: { id: e.workflow.id, name: e.workflow.name },
|
|
8140
|
+
state: { id: e.state.id, name: e.state.name }
|
|
8141
|
+
});
|
|
8142
|
+
return printJson(
|
|
8143
|
+
bases.map((b) => ({
|
|
8144
|
+
...ref(b),
|
|
8145
|
+
inherited_by: lib.children.get(b.state.id).map(ref)
|
|
8146
|
+
}))
|
|
8147
|
+
);
|
|
8148
|
+
}
|
|
8149
|
+
if (bases.length === 0) return console.log("no base states");
|
|
8150
|
+
let group;
|
|
8151
|
+
for (const base of bases) {
|
|
8152
|
+
if (base.workflow.id !== group) {
|
|
8153
|
+
console.log(`${group === void 0 ? "" : "\n"}${base.workflow.name}`);
|
|
8154
|
+
group = base.workflow.id;
|
|
8155
|
+
}
|
|
8156
|
+
console.log(` ${base.state.name}`);
|
|
8157
|
+
console.log(` inherited by: ${lib.children.get(base.state.id).map(qualify).join(", ")}`);
|
|
8158
|
+
}
|
|
8041
8159
|
});
|
|
8042
8160
|
withCommon(
|
|
8043
8161
|
workflows.command("create [json]").description(
|
|
@@ -8053,11 +8171,13 @@ see \`tines workflows create --help\` for the expected shape`
|
|
|
8053
8171
|
);
|
|
8054
8172
|
}
|
|
8055
8173
|
assertNewStatesHavePrompts(body.states, opts.prompts);
|
|
8056
|
-
const
|
|
8174
|
+
const api = client(opts);
|
|
8175
|
+
await resolveStateBases(body.states, () => loadLibrary(api));
|
|
8176
|
+
const wf = await api.createWorkflow(body);
|
|
8057
8177
|
if (opts.json) return printJson(wf);
|
|
8058
8178
|
console.log(`created workflow "${wf.name}" (${wf.id})
|
|
8059
8179
|
`);
|
|
8060
|
-
printWorkflowDetail(wf);
|
|
8180
|
+
printWorkflowDetail(wf, await loadLibrary(api));
|
|
8061
8181
|
}
|
|
8062
8182
|
);
|
|
8063
8183
|
withCommon(
|
|
@@ -8065,9 +8185,11 @@ see \`tines workflows create --help\` for the expected shape`
|
|
|
8065
8185
|
).action(
|
|
8066
8186
|
async (ref, inline, opts) => {
|
|
8067
8187
|
const api = client(opts);
|
|
8068
|
-
const
|
|
8188
|
+
const lib = await loadLibrary(api);
|
|
8189
|
+
const wf = pickWorkflow(lib.workflows, ref);
|
|
8069
8190
|
const body = readJsonBody(inline, opts.file) ?? {};
|
|
8070
8191
|
assertNewStatesHavePrompts(body.states, opts.prompts);
|
|
8192
|
+
await resolveStateBases(body.states, async () => lib);
|
|
8071
8193
|
if (opts.name !== void 0) body.name = opts.name;
|
|
8072
8194
|
if (opts.description !== void 0) body.description = opts.description;
|
|
8073
8195
|
if (opts.initialState !== void 0) body.initial_state = opts.initialState;
|
|
@@ -8078,7 +8200,7 @@ see \`tines workflows create --help\` for the expected shape`
|
|
|
8078
8200
|
if (opts.json) return printJson(updated);
|
|
8079
8201
|
console.log(`updated workflow "${updated.name}" (${updated.id})
|
|
8080
8202
|
`);
|
|
8081
|
-
printWorkflowDetail(updated);
|
|
8203
|
+
printWorkflowDetail(updated, await loadLibrary(api));
|
|
8082
8204
|
}
|
|
8083
8205
|
);
|
|
8084
8206
|
withCommon(
|