@gh-symphony/cli 0.1.4 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +87 -0
- package/dist/{chunk-HT3FAJAO.js → chunk-27UZ6KX2.js} +273 -10
- package/dist/{chunk-EWTMSDCE.js → chunk-3SKN5L3I.js} +260 -18
- package/dist/{chunk-WOVNN5NW.js → chunk-4ICDSQCJ.js} +1 -0
- package/dist/{chunk-DW63WPRE.js → chunk-6PFFGP7S.js} +18 -3
- package/dist/{chunk-Z3NZOPLZ.js → chunk-BOM2BYZQ.js} +43 -0
- package/dist/{chunk-E7OCBNB2.js → chunk-FAU72YC2.js} +1 -1
- package/dist/{chunk-RHLUIMBN.js → chunk-PLBG7TZA.js} +306 -30
- package/dist/{chunk-6I753NYO.js → chunk-RZ3WO7OV.js} +1 -1
- package/dist/{repo-IH6UWE4H.js → chunk-X4QSP3AX.js} +5443 -6207
- package/dist/{config-cmd-2ADPUYWA.js → config-cmd-AOZVS6GU.js} +1 -1
- package/dist/{doctor-I32MANQ4.js → doctor-GDZSGJIT.js} +602 -26
- package/dist/index.js +12 -8
- package/dist/repo-SWEUWY4H.js +2693 -0
- package/dist/{setup-UJC2WYHQ.js → setup-XNOSJ3RX.js} +35 -32
- package/dist/{upgrade-XYHCUGHT.js → upgrade-ZWUAJLHK.js} +2 -2
- package/dist/{version-B2AYYGLM.js → version-PLQK6X2P.js} +1 -1
- package/dist/worker-entry.js +77 -9
- package/dist/{workflow-WSXHMO5B.js → workflow-2ERPNGRB.js} +7 -6
- package/package.json +3 -3
- package/dist/chunk-YIARPBOR.js +0 -1648
|
@@ -53,6 +53,7 @@ var DEFAULT_WORKFLOW_TRACKER = {
|
|
|
53
53
|
terminalStates: DEFAULT_WORKFLOW_LIFECYCLE.terminalStates,
|
|
54
54
|
projectId: null,
|
|
55
55
|
stateFieldName: DEFAULT_WORKFLOW_LIFECYCLE.stateFieldName,
|
|
56
|
+
priority: null,
|
|
56
57
|
priorityFieldName: null,
|
|
57
58
|
blockerCheckStates: DEFAULT_WORKFLOW_LIFECYCLE.blockerCheckStates
|
|
58
59
|
};
|
|
@@ -169,6 +170,7 @@ function parseWorkflowMarkdown(markdown, env = process.env, options = {}) {
|
|
|
169
170
|
terminalStates,
|
|
170
171
|
projectId: readOptionalString(tracker, "project_id", env),
|
|
171
172
|
stateFieldName: readOptionalString(tracker, "state_field", env) ?? DEFAULT_WORKFLOW_TRACKER.stateFieldName,
|
|
173
|
+
priority: readPriorityConfig(tracker, env),
|
|
172
174
|
priorityFieldName: readOptionalString(tracker, "priority_field", env),
|
|
173
175
|
blockerCheckStates
|
|
174
176
|
},
|
|
@@ -235,6 +237,52 @@ function validateTrackerConfig(tracker, trackerKind, env) {
|
|
|
235
237
|
}
|
|
236
238
|
}
|
|
237
239
|
}
|
|
240
|
+
function readPriorityConfig(tracker, env) {
|
|
241
|
+
if (tracker.priority === void 0 || tracker.priority === null) {
|
|
242
|
+
return null;
|
|
243
|
+
}
|
|
244
|
+
const priority = readObject(tracker, "priority", "tracker.priority");
|
|
245
|
+
const source = readRequiredString(priority, "source", env);
|
|
246
|
+
const keys = new Set(Object.keys(priority));
|
|
247
|
+
if (source === "project-field") {
|
|
248
|
+
rejectPriorityKeys(keys, ["source", "field", "values"], source);
|
|
249
|
+
const field = readRequiredString(priority, "field", env);
|
|
250
|
+
const values = readNumberMap(priority, "values", "tracker.priority.values");
|
|
251
|
+
if (Object.keys(values).length === 0) {
|
|
252
|
+
throw new Error(
|
|
253
|
+
'Workflow front matter field "tracker.priority.values" must be a non-empty object for tracker.priority.source "project-field".'
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
return { source, field, values };
|
|
257
|
+
}
|
|
258
|
+
if (source === "labels") {
|
|
259
|
+
rejectPriorityKeys(keys, ["source", "labels"], source);
|
|
260
|
+
const labels = readNumberMap(priority, "labels", "tracker.priority.labels");
|
|
261
|
+
if (Object.keys(labels).length === 0) {
|
|
262
|
+
throw new Error(
|
|
263
|
+
'Workflow front matter field "tracker.priority.labels" must be a non-empty object for tracker.priority.source "labels".'
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
return { source, labels };
|
|
267
|
+
}
|
|
268
|
+
if (source === "disabled") {
|
|
269
|
+
rejectPriorityKeys(keys, ["source"], source);
|
|
270
|
+
return { source };
|
|
271
|
+
}
|
|
272
|
+
throw new Error(
|
|
273
|
+
`Unsupported workflow tracker.priority.source "${source}". Supported values: project-field, labels, disabled.`
|
|
274
|
+
);
|
|
275
|
+
}
|
|
276
|
+
function rejectPriorityKeys(keys, allowedKeys, source) {
|
|
277
|
+
const allowed = new Set(allowedKeys);
|
|
278
|
+
for (const key of keys) {
|
|
279
|
+
if (!allowed.has(key)) {
|
|
280
|
+
throw new Error(
|
|
281
|
+
`Workflow front matter field "tracker.priority.${key}" is not supported for tracker.priority.source "${source}".`
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
238
286
|
function parseLegacyWorkflowMarkdown(markdown) {
|
|
239
287
|
const promptGuidelines = matchOptionalSection(markdown, "Prompt Guidelines") ?? "";
|
|
240
288
|
return {
|
|
@@ -262,6 +310,10 @@ function parseBlock(lines, startIndex, indent) {
|
|
|
262
310
|
index += 1;
|
|
263
311
|
continue;
|
|
264
312
|
}
|
|
313
|
+
if (line.trim().startsWith("#")) {
|
|
314
|
+
index += 1;
|
|
315
|
+
continue;
|
|
316
|
+
}
|
|
265
317
|
const lineIndent = countIndent(line);
|
|
266
318
|
if (lineIndent < indent) {
|
|
267
319
|
break;
|
|
@@ -306,11 +358,16 @@ function parseBlock(lines, startIndex, indent) {
|
|
|
306
358
|
);
|
|
307
359
|
}
|
|
308
360
|
collectionType = "object";
|
|
309
|
-
const separatorIndex = trimmed
|
|
361
|
+
const separatorIndex = findMappingSeparator(trimmed);
|
|
310
362
|
if (separatorIndex < 0) {
|
|
311
363
|
throw new Error(`Invalid workflow front matter line "${trimmed}".`);
|
|
312
364
|
}
|
|
313
|
-
const
|
|
365
|
+
const rawKey = trimmed.slice(0, separatorIndex).trim();
|
|
366
|
+
const parsedKey = parseScalar(rawKey);
|
|
367
|
+
if (typeof parsedKey !== "string") {
|
|
368
|
+
throw new Error(`Invalid workflow front matter key "${rawKey}".`);
|
|
369
|
+
}
|
|
370
|
+
const key = parsedKey;
|
|
314
371
|
const remainder = trimmed.slice(separatorIndex + 1).trim();
|
|
315
372
|
if (remainder === "|" || remainder === "|-") {
|
|
316
373
|
const [multiline, nextIndex2] = parseMultilineScalar(
|
|
@@ -355,6 +412,30 @@ function parseMultilineScalar(lines, startIndex, indent) {
|
|
|
355
412
|
function countIndent(line) {
|
|
356
413
|
return line.match(/^ */)?.[0].length ?? 0;
|
|
357
414
|
}
|
|
415
|
+
function findMappingSeparator(value) {
|
|
416
|
+
let quote = null;
|
|
417
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
418
|
+
const char = value[index];
|
|
419
|
+
if (quote) {
|
|
420
|
+
if (char === "\\") {
|
|
421
|
+
index += 1;
|
|
422
|
+
continue;
|
|
423
|
+
}
|
|
424
|
+
if (char === quote) {
|
|
425
|
+
quote = null;
|
|
426
|
+
}
|
|
427
|
+
continue;
|
|
428
|
+
}
|
|
429
|
+
if (char === '"' || char === "'") {
|
|
430
|
+
quote = char;
|
|
431
|
+
continue;
|
|
432
|
+
}
|
|
433
|
+
if (char === ":") {
|
|
434
|
+
return index;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
return -1;
|
|
438
|
+
}
|
|
358
439
|
function parseScalar(value) {
|
|
359
440
|
if (value === "null") return null;
|
|
360
441
|
if (value === "true") return true;
|
|
@@ -363,8 +444,18 @@ function parseScalar(value) {
|
|
|
363
444
|
return parseInlineArray(value);
|
|
364
445
|
}
|
|
365
446
|
if (/^-?\d+$/.test(value)) return Number.parseInt(value, 10);
|
|
366
|
-
if (value.startsWith('"') && value.endsWith('"')
|
|
367
|
-
|
|
447
|
+
if (value.startsWith('"') && value.endsWith('"')) {
|
|
448
|
+
try {
|
|
449
|
+
const parsed = JSON.parse(value);
|
|
450
|
+
if (typeof parsed === "string") {
|
|
451
|
+
return parsed;
|
|
452
|
+
}
|
|
453
|
+
} catch {
|
|
454
|
+
throw new Error(`Invalid quoted workflow front matter scalar "${value}".`);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
if (value.startsWith("'") && value.endsWith("'")) {
|
|
458
|
+
return value.slice(1, -1).replace(/''/g, "'");
|
|
368
459
|
}
|
|
369
460
|
return value;
|
|
370
461
|
}
|
|
@@ -559,13 +650,13 @@ function readOptionalIntegerLike(input, key) {
|
|
|
559
650
|
}
|
|
560
651
|
throw new Error(`Workflow front matter field "${key}" must be an integer.`);
|
|
561
652
|
}
|
|
562
|
-
function readNumberMap(input, key) {
|
|
653
|
+
function readNumberMap(input, key, path = key) {
|
|
563
654
|
const value = input[key];
|
|
564
655
|
if (value === void 0 || value === null) {
|
|
565
656
|
return {};
|
|
566
657
|
}
|
|
567
658
|
if (typeof value !== "object" || Array.isArray(value)) {
|
|
568
|
-
throw new Error(`Workflow front matter field "${
|
|
659
|
+
throw new Error(`Workflow front matter field "${path}" must be an object.`);
|
|
569
660
|
}
|
|
570
661
|
const result = {};
|
|
571
662
|
for (const [entryKey, entryValue] of Object.entries(value)) {
|
|
@@ -573,12 +664,12 @@ function readNumberMap(input, key) {
|
|
|
573
664
|
result[entryKey] = entryValue;
|
|
574
665
|
continue;
|
|
575
666
|
}
|
|
576
|
-
if (typeof entryValue === "string" &&
|
|
667
|
+
if (typeof entryValue === "string" && /^-?\d+$/.test(entryValue)) {
|
|
577
668
|
result[entryKey] = Number.parseInt(entryValue, 10);
|
|
578
669
|
continue;
|
|
579
670
|
}
|
|
580
671
|
throw new Error(
|
|
581
|
-
`Workflow front matter field "${
|
|
672
|
+
`Workflow front matter field "${path}.${entryKey}" must be an integer.`
|
|
582
673
|
);
|
|
583
674
|
}
|
|
584
675
|
return result;
|
|
@@ -1464,6 +1555,10 @@ function isFileMissing(error) {
|
|
|
1464
1555
|
// ../core/src/observability/event-formatter.ts
|
|
1465
1556
|
function formatEventMessage(event) {
|
|
1466
1557
|
switch (event.event) {
|
|
1558
|
+
case "tracker.list":
|
|
1559
|
+
return `Tracker list saw ${event.issue.identifier}`;
|
|
1560
|
+
case "tracker.fetchByIds":
|
|
1561
|
+
return `Tracker fetch refreshed ${event.issue.identifier}`;
|
|
1467
1562
|
case "run-dispatched":
|
|
1468
1563
|
return event.issueState ? `Dispatched from ${event.issueState}` : "Dispatched";
|
|
1469
1564
|
case "run-recovered":
|
|
@@ -1538,31 +1633,176 @@ var SENSITIVE_KEY_SUBSTRINGS = [
|
|
|
1538
1633
|
"api_key"
|
|
1539
1634
|
];
|
|
1540
1635
|
function redactObservabilitySecrets(value) {
|
|
1541
|
-
return
|
|
1636
|
+
return redactObservabilitySecretsWithStats(value).value;
|
|
1637
|
+
}
|
|
1638
|
+
function redactObservabilitySecretsWithStats(value) {
|
|
1639
|
+
const counts = createRedactionCounts();
|
|
1640
|
+
const redacted = redactValue(value, counts, {
|
|
1641
|
+
redactStringValues: false
|
|
1642
|
+
});
|
|
1643
|
+
return { value: redacted, redactions: summarizeRedactionCounts(counts) };
|
|
1644
|
+
}
|
|
1645
|
+
function redactObservabilityDiagnosticsWithStats(value) {
|
|
1646
|
+
const counts = createRedactionCounts();
|
|
1647
|
+
const redacted = redactValue(value, counts, {
|
|
1648
|
+
redactStringValues: true
|
|
1649
|
+
});
|
|
1650
|
+
return { value: redacted, redactions: summarizeRedactionCounts(counts) };
|
|
1542
1651
|
}
|
|
1543
|
-
function
|
|
1652
|
+
function redactObservabilityTextWithStats(text) {
|
|
1653
|
+
const counts = createRedactionCounts();
|
|
1654
|
+
return {
|
|
1655
|
+
value: redactTextValue(text, counts),
|
|
1656
|
+
redactions: summarizeRedactionCounts(counts)
|
|
1657
|
+
};
|
|
1658
|
+
}
|
|
1659
|
+
function redactValue(value, counts, options) {
|
|
1544
1660
|
if (Array.isArray(value)) {
|
|
1545
|
-
return value.map((item) => redactValue(item));
|
|
1661
|
+
return value.map((item) => redactValue(item, counts, options));
|
|
1662
|
+
}
|
|
1663
|
+
if (typeof value === "string" && options.redactStringValues) {
|
|
1664
|
+
return redactTextValue(value, counts);
|
|
1546
1665
|
}
|
|
1547
1666
|
if (!isRecord3(value)) {
|
|
1548
1667
|
return value;
|
|
1549
1668
|
}
|
|
1550
1669
|
return Object.fromEntries(
|
|
1551
|
-
Object.entries(value).map(([key, nested]) =>
|
|
1552
|
-
key
|
|
1553
|
-
|
|
1554
|
-
|
|
1670
|
+
Object.entries(value).map(([key, nested]) => {
|
|
1671
|
+
const redactionClass = redactionClassForKey(key);
|
|
1672
|
+
if (redactionClass) {
|
|
1673
|
+
incrementRedaction(counts, redactionClass);
|
|
1674
|
+
return [key, REDACTED];
|
|
1675
|
+
}
|
|
1676
|
+
return [key, redactValue(nested, counts, options)];
|
|
1677
|
+
})
|
|
1555
1678
|
);
|
|
1556
1679
|
}
|
|
1557
|
-
function
|
|
1680
|
+
function redactionClassForKey(key) {
|
|
1558
1681
|
const normalizedKey = key.toLowerCase();
|
|
1559
|
-
|
|
1682
|
+
if (normalizedKey.includes("authorization")) {
|
|
1683
|
+
return "authorization_header";
|
|
1684
|
+
}
|
|
1685
|
+
if (normalizedKey.includes("apikey") || normalizedKey.includes("api-key") || normalizedKey.includes("api_key")) {
|
|
1686
|
+
return "api_key";
|
|
1687
|
+
}
|
|
1688
|
+
if (normalizedKey.includes("secret")) {
|
|
1689
|
+
return "secret_key";
|
|
1690
|
+
}
|
|
1691
|
+
if (normalizedKey === "token" || normalizedKey.endsWith("token")) {
|
|
1692
|
+
return "env_token";
|
|
1693
|
+
}
|
|
1694
|
+
if (SENSITIVE_KEY_SUBSTRINGS.some(
|
|
1560
1695
|
(pattern) => normalizedKey.includes(pattern.toLowerCase())
|
|
1561
|
-
)
|
|
1696
|
+
)) {
|
|
1697
|
+
return "secret_key";
|
|
1698
|
+
}
|
|
1699
|
+
return null;
|
|
1562
1700
|
}
|
|
1563
1701
|
function isRecord3(value) {
|
|
1564
1702
|
return value != null && typeof value === "object";
|
|
1565
1703
|
}
|
|
1704
|
+
function redactTextValue(text, counts) {
|
|
1705
|
+
let redacted = replaceAndCount(
|
|
1706
|
+
text,
|
|
1707
|
+
/\b(Authorization\s*:\s*Bearer\s+)([^\s]+)/gi,
|
|
1708
|
+
"authorization_header",
|
|
1709
|
+
counts,
|
|
1710
|
+
"$1[REDACTED]"
|
|
1711
|
+
);
|
|
1712
|
+
redacted = replaceAndCount(
|
|
1713
|
+
redacted,
|
|
1714
|
+
/\b(X-API-Key\s*:\s*)([^\s]+)/gi,
|
|
1715
|
+
"api_key",
|
|
1716
|
+
counts,
|
|
1717
|
+
"$1[REDACTED]"
|
|
1718
|
+
);
|
|
1719
|
+
redacted = replaceAndCount(
|
|
1720
|
+
redacted,
|
|
1721
|
+
/^([A-Z0-9_]*(?:TOKEN)\w*\s*=\s*)([^\s]+)/gim,
|
|
1722
|
+
"env_token",
|
|
1723
|
+
counts,
|
|
1724
|
+
"$1[REDACTED]"
|
|
1725
|
+
);
|
|
1726
|
+
redacted = replaceAndCount(
|
|
1727
|
+
redacted,
|
|
1728
|
+
/^([A-Z0-9_]*(?:API_KEY)\w*\s*=\s*)([^\s]+)/gim,
|
|
1729
|
+
"api_key",
|
|
1730
|
+
counts,
|
|
1731
|
+
"$1[REDACTED]"
|
|
1732
|
+
);
|
|
1733
|
+
redacted = replaceAndCount(
|
|
1734
|
+
redacted,
|
|
1735
|
+
/^([A-Z0-9_]*(?:SECRET)\w*\s*=\s*)([^\s]+)/gim,
|
|
1736
|
+
"secret_key",
|
|
1737
|
+
counts,
|
|
1738
|
+
"$1[REDACTED]"
|
|
1739
|
+
);
|
|
1740
|
+
redacted = replaceAndCount(
|
|
1741
|
+
redacted,
|
|
1742
|
+
/((?:"token"|'token'|token)\s*:\s*)(?:"([^"]*)"|'([^']*)'|([^\s,}\]]+))/gi,
|
|
1743
|
+
"env_token",
|
|
1744
|
+
counts,
|
|
1745
|
+
'$1"[REDACTED]"'
|
|
1746
|
+
);
|
|
1747
|
+
redacted = replaceAndCount(
|
|
1748
|
+
redacted,
|
|
1749
|
+
/((?:"secret"|'secret'|secret)\s*:\s*)(?:"([^"]*)"|'([^']*)'|([^\s,}\]]+))/gi,
|
|
1750
|
+
"secret_key",
|
|
1751
|
+
counts,
|
|
1752
|
+
'$1"[REDACTED]"'
|
|
1753
|
+
);
|
|
1754
|
+
redacted = replaceAndCount(
|
|
1755
|
+
redacted,
|
|
1756
|
+
/((?:"apiKey"|'apiKey'|apiKey)\s*:\s*)(?:"([^"]*)"|'([^']*)'|([^\s,}\]]+))/g,
|
|
1757
|
+
"api_key",
|
|
1758
|
+
counts,
|
|
1759
|
+
'$1"[REDACTED]"'
|
|
1760
|
+
);
|
|
1761
|
+
redacted = replaceAndCount(
|
|
1762
|
+
redacted,
|
|
1763
|
+
/\bghp_[A-Za-z0-9_]+/g,
|
|
1764
|
+
"env_token",
|
|
1765
|
+
counts,
|
|
1766
|
+
"[REDACTED]"
|
|
1767
|
+
);
|
|
1768
|
+
redacted = replaceAndCount(
|
|
1769
|
+
redacted,
|
|
1770
|
+
/\blin_[A-Za-z0-9_]+/g,
|
|
1771
|
+
"api_key",
|
|
1772
|
+
counts,
|
|
1773
|
+
"[REDACTED]"
|
|
1774
|
+
);
|
|
1775
|
+
redacted = replaceAndCount(
|
|
1776
|
+
redacted,
|
|
1777
|
+
/\bsk-[A-Za-z0-9_-]+/g,
|
|
1778
|
+
"api_key",
|
|
1779
|
+
counts,
|
|
1780
|
+
"[REDACTED]"
|
|
1781
|
+
);
|
|
1782
|
+
return redacted;
|
|
1783
|
+
}
|
|
1784
|
+
function replaceAndCount(text, pattern, redactionClass, counts, replacement) {
|
|
1785
|
+
return text.replace(pattern, (...args) => {
|
|
1786
|
+
const matched = typeof args[0] === "string" ? args[0] : "";
|
|
1787
|
+
if (matched.includes(REDACTED)) {
|
|
1788
|
+
return matched;
|
|
1789
|
+
}
|
|
1790
|
+
incrementRedaction(counts, redactionClass);
|
|
1791
|
+
return replacement.replace(/\$(\d+)/g, (_placeholder, index) => {
|
|
1792
|
+
const group = args[Number.parseInt(index, 10)];
|
|
1793
|
+
return typeof group === "string" ? group : "";
|
|
1794
|
+
});
|
|
1795
|
+
});
|
|
1796
|
+
}
|
|
1797
|
+
function createRedactionCounts() {
|
|
1798
|
+
return /* @__PURE__ */ new Map();
|
|
1799
|
+
}
|
|
1800
|
+
function incrementRedaction(counts, redactionClass) {
|
|
1801
|
+
counts.set(redactionClass, (counts.get(redactionClass) ?? 0) + 1);
|
|
1802
|
+
}
|
|
1803
|
+
function summarizeRedactionCounts(counts) {
|
|
1804
|
+
return Array.from(counts.entries()).filter(([, count]) => count > 0).map(([redactionClass, count]) => ({ class: redactionClass, count })).sort((left, right) => left.class.localeCompare(right.class));
|
|
1805
|
+
}
|
|
1566
1806
|
|
|
1567
1807
|
// ../core/src/observability/status-assembler.ts
|
|
1568
1808
|
function isMatchingIssueRun(run, issueId, issueIdentifier) {
|
|
@@ -6304,6 +6544,8 @@ export {
|
|
|
6304
6544
|
isFileMissing,
|
|
6305
6545
|
parseRecentEvents,
|
|
6306
6546
|
redactObservabilitySecrets,
|
|
6547
|
+
redactObservabilityDiagnosticsWithStats,
|
|
6548
|
+
redactObservabilityTextWithStats,
|
|
6307
6549
|
isMatchingIssueRun,
|
|
6308
6550
|
mapIssueOrchestrationStateToStatus,
|
|
6309
6551
|
resolveGitHubGraphQLToken,
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
resolveRepoRuntimeRoot
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-RZ3WO7OV.js";
|
|
5
5
|
import {
|
|
6
6
|
parseWorkflowMarkdown
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-3SKN5L3I.js";
|
|
8
8
|
import {
|
|
9
9
|
saveGlobalConfig,
|
|
10
10
|
saveProjectConfig
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-4ICDSQCJ.js";
|
|
12
12
|
|
|
13
13
|
// src/repo-runtime.ts
|
|
14
14
|
import { execFileSync } from "child_process";
|
|
@@ -58,6 +58,7 @@ async function initRepoRuntime(flags) {
|
|
|
58
58
|
await migrateLegacyRuntime(runtimeRoot);
|
|
59
59
|
const workflowPath = resolve(repoDir, flags.workflowFile ?? "WORKFLOW.md");
|
|
60
60
|
const workflow = parseWorkflowMarkdown(await readFile(workflowPath, "utf8"));
|
|
61
|
+
validateRepoInitWorkflow(workflow);
|
|
61
62
|
const repository = resolveRepository(repoDir);
|
|
62
63
|
const trackerAdapter = workflow.tracker.kind ?? "github-project";
|
|
63
64
|
const trackerBindingId = workflow.tracker.projectId ?? workflow.tracker.projectSlug ?? "";
|
|
@@ -70,6 +71,9 @@ async function initRepoRuntime(flags) {
|
|
|
70
71
|
if (flags.assignedOnly) {
|
|
71
72
|
trackerSettings.assignedOnly = true;
|
|
72
73
|
}
|
|
74
|
+
if (workflow.tracker.priorityFieldName) {
|
|
75
|
+
trackerSettings.priorityFieldName = workflow.tracker.priorityFieldName;
|
|
76
|
+
}
|
|
73
77
|
if (trackerAdapter === "file") {
|
|
74
78
|
if (!process.env.GH_SYMPHONY_FILE_TRACKER_ISSUES_PATH) {
|
|
75
79
|
throw new Error(
|
|
@@ -88,6 +92,7 @@ async function initRepoRuntime(flags) {
|
|
|
88
92
|
adapter: trackerAdapter,
|
|
89
93
|
bindingId: trackerBindingId,
|
|
90
94
|
...workflow.tracker.endpoint ? { apiUrl: workflow.tracker.endpoint } : {},
|
|
95
|
+
priority: workflow.tracker.priority,
|
|
91
96
|
settings: trackerSettings
|
|
92
97
|
}
|
|
93
98
|
};
|
|
@@ -112,6 +117,16 @@ async function initRepoRuntime(flags) {
|
|
|
112
117
|
repository
|
|
113
118
|
};
|
|
114
119
|
}
|
|
120
|
+
function validateRepoInitWorkflow(workflow) {
|
|
121
|
+
if (workflow.tracker.kind !== "linear") {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
if (!workflow.tracker.apiKey?.trim()) {
|
|
125
|
+
throw new Error(
|
|
126
|
+
'Linear tracker repo init requires WORKFLOW.md field "tracker.api_key" to reference a resolvable environment variable such as "$LINEAR_API_KEY".'
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
115
130
|
async function migrateLegacyRuntime(runtimeRoot) {
|
|
116
131
|
const projectsDir = join(runtimeRoot, "projects");
|
|
117
132
|
const projectIds = await readDirectoryNames(projectsDir);
|
|
@@ -32,6 +32,48 @@ function createClient(token, options) {
|
|
|
32
32
|
fetchImpl: options?.fetchImpl ?? fetch
|
|
33
33
|
};
|
|
34
34
|
}
|
|
35
|
+
async function listRepositoryLabels(client, owner, name) {
|
|
36
|
+
const restUrl = client.apiUrl.replace("/graphql", "");
|
|
37
|
+
const baseUrl = restUrl === client.apiUrl ? REST_API_URL : restUrl;
|
|
38
|
+
const labels = [];
|
|
39
|
+
let page = 1;
|
|
40
|
+
while (true) {
|
|
41
|
+
const response = await client.fetchImpl(
|
|
42
|
+
`${baseUrl}/repos/${encodeURIComponent(owner)}/${encodeURIComponent(name)}/labels?per_page=100&page=${page}`,
|
|
43
|
+
{
|
|
44
|
+
headers: {
|
|
45
|
+
authorization: `Bearer ${client.token}`,
|
|
46
|
+
accept: "application/vnd.github+json"
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
);
|
|
50
|
+
if (!response.ok) {
|
|
51
|
+
const payload = await response.json().catch(() => null);
|
|
52
|
+
const message = payload?.message?.trim() || response.statusText;
|
|
53
|
+
throw new GitHubApiError(
|
|
54
|
+
`GitHub label lookup failed for ${owner}/${name}: ${response.status} ${message}`.trim(),
|
|
55
|
+
response.status
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
const pageLabels = await response.json();
|
|
59
|
+
labels.push(
|
|
60
|
+
...pageLabels.flatMap(
|
|
61
|
+
(label) => typeof label.name === "string" ? [
|
|
62
|
+
{
|
|
63
|
+
name: label.name,
|
|
64
|
+
color: label.color ?? null,
|
|
65
|
+
description: label.description ?? null
|
|
66
|
+
}
|
|
67
|
+
] : []
|
|
68
|
+
)
|
|
69
|
+
);
|
|
70
|
+
if (pageLabels.length < 100) {
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
page += 1;
|
|
74
|
+
}
|
|
75
|
+
return labels;
|
|
76
|
+
}
|
|
35
77
|
async function validateToken(client) {
|
|
36
78
|
const restUrl = client.apiUrl.replace("/graphql", "");
|
|
37
79
|
const baseUrl = restUrl === client.apiUrl ? REST_API_URL : restUrl;
|
|
@@ -739,6 +781,7 @@ export {
|
|
|
739
781
|
GitHubApiError,
|
|
740
782
|
GitHubScopeError,
|
|
741
783
|
createClient,
|
|
784
|
+
listRepositoryLabels,
|
|
742
785
|
validateToken,
|
|
743
786
|
checkRequiredScopes,
|
|
744
787
|
discoverUserProjects,
|