github-delivery-os 1.4.1 → 1.5.0
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.
|
@@ -21,7 +21,7 @@ Creating issues and comments in a repo is a visible, outward action — other co
|
|
|
21
21
|
Before creating anything that depends on configuration, check the target repo actually has Delivery OS installed and configured — a silent no-op (nothing happens because a variable is unset) is more confusing than an upfront "this won't do much yet":
|
|
22
22
|
|
|
23
23
|
- **Installed?** `gh api repos/<owner>/<repo>/contents/.github/workflows/authorize-deployment.yml --silent` (404 = not installed — suggest `npx github-delivery-os status .` or `install --with-templates` in that repo).
|
|
24
|
-
- **Labels set up?** `gh label list --repo <owner>/<repo>` — look for `production`, `qa`, `qa-request`, `sprint`, `sprint-
|
|
24
|
+
- **Labels set up?** `gh label list --repo <owner>/<repo>` — look for `production`, `qa`, `qa-request`, `sprint`, `sprint-child`, `planning`, `declined`, `ready-for-deploy`. Missing labels mean `Setup Labels` hasn't been run there yet — offer to fix it directly rather than just reporting the gap: `gh workflow run setup-labels.yml --repo <owner>/<repo>` (it's a `workflow_dispatch` trigger, so this actually creates them on the spot). Confirm with the user first since it's a real change to their repo. (`sprint-active` instead of `sprint-child` means the repo is on a pre-1.5.0 install — still valid, just the older label name.)
|
|
25
25
|
- **Repo variables set?** `gh variable list --repo <owner>/<repo>` — look for `RELEASE_APPROVER`, `QA_APPROVER`, `QA_ASSIGNEES`. If unset, say so plainly: the issue will still get created, but `notify-release-approver` will ping the literal placeholder `release-approver`/`qa-approver`, not a real person. Setting these requires repo admin access (`gh variable set NAME --repo <owner>/<repo> --body <value>`) — don't set them without being asked to, since they name a real person as approver.
|
|
26
26
|
|
|
27
27
|
## Creating issues
|
|
@@ -32,7 +32,7 @@ Before creating anything that depends on configuration, check the target repo ac
|
|
|
32
32
|
|
|
33
33
|
This confirm-first default is for issues created **on explicit request** ("file a bug for this", "create a sprint"). The "Autonomous tracking" section below describes a *different* mode — noticing and filing work on its own during a session — and overrides this default there: act first, confirm after, per its own "Confirm only when unsure" rule. Don't apply both rules to the same action.
|
|
34
34
|
|
|
35
|
-
**Sprint Planning** — triggers `sprint-child-creator` (one child issue per feature line, each labeled `sprint-active
|
|
35
|
+
**Sprint Planning** — triggers `sprint-child-creator` (one child issue per feature line, each labeled `sprint-child` — `sprint-active` on repos installed before 1.5.0 — on open):
|
|
36
36
|
- Title **must contain** the literal string `SPRINT -`, e.g. `SPRINT - Sprint 14`
|
|
37
37
|
- Labels: `sprint`, `planning`
|
|
38
38
|
- Body:
|
|
@@ -163,7 +163,7 @@ Closing a sprint task (child) issue is what actually moves the burn-down — cre
|
|
|
163
163
|
gh issue close <number> --repo <owner>/<repo>
|
|
164
164
|
```
|
|
165
165
|
|
|
166
|
-
`auto-close-sprint` fires on close, re-reads every
|
|
166
|
+
`auto-close-sprint` fires on close, re-reads every issue whose body contains `Parent Sprint: #<N>` (label-independent — see below), recomputes progress, and rewrites the sprint issue's `## 🚦 Sprint Status` section. At 100% it also closes the sprint issue itself and posts a completion comment. Re-check the sprint issue's body afterward to see the update — it happens as a side effect of closing the child, not as a response visible on the child issue itself.
|
|
167
167
|
|
|
168
168
|
## Autonomous tracking (identify → file → update → close)
|
|
169
169
|
|
|
@@ -201,7 +201,7 @@ Don't rely on recalling an issue number from earlier in the conversation, and ne
|
|
|
201
201
|
gh issue list --repo <owner>/<repo> --state open --search "<keywords from the work>"
|
|
202
202
|
```
|
|
203
203
|
|
|
204
|
-
Narrow with `--label task` (or `bug`, `sprint-
|
|
204
|
+
Narrow with `--label task` (or `bug`, `sprint-child`, `qa-request`) only once the category is known and the target repo actually has that label — don't assume `--label task` alone finds everything relevant. (Older repos may still use `sprint-active` instead of `sprint-child`; check `gh label list` first.)
|
|
205
205
|
|
|
206
206
|
This is also what makes picking work back up in a *new* session possible without any local memory — the issue list itself is the state.
|
|
207
207
|
|
|
@@ -232,7 +232,7 @@ Given an SRS/PRD, or just a plain-language feature description, break it into a
|
|
|
232
232
|
|
|
233
233
|
3. **Per phase, once confirmed:**
|
|
234
234
|
- Create the Sprint Planning issue with the usual recipe (`SPRINT - <phase name>`, dates, goal). "Sprint Features (One Per Line)" is template-required, so it can't be left empty — put one line noting the real breakdown is in linked Task issues (e.g. `See linked Task issues for this phase's breakdown`). This means exactly one bare placeholder child gets auto-created.
|
|
235
|
-
- File one full Task issue per requirement (the usual Task recipe — Owner, Priority, Acceptance Criteria drawn from the spec text; ask only when the spec genuinely doesn't specify something, like priority). Include `Parent Sprint: #<sprint-number>` in the body (e.g. under Artifacts / Links) — that exact phrase is what `auto-close-sprint` actually scans for (`.github/workflows/auto-close-sprint.yml` filters on body content only, **not** the `sprint-
|
|
235
|
+
- File one full Task issue per requirement (the usual Task recipe — Owner, Priority, Acceptance Criteria drawn from the spec text; ask only when the spec genuinely doesn't specify something, like priority). Include `Parent Sprint: #<sprint-number>` in the body (e.g. under Artifacts / Links) — that exact phrase is what `auto-close-sprint` actually scans for (`.github/workflows/auto-close-sprint.yml` filters on body content only, **not** the `sprint-child` label, deliberately — see its own comment on why label-scoping was tried and reverted). Add the `sprint-child` label anyway, for consistency with how the sprint's own children are found (see "Finding things" below), but know it plays no role in the burn-down count.
|
|
236
236
|
- Now that the real Task issues exist, close the one placeholder child: `gh issue close <N> --repo <owner>/<repo> --comment "Superseded by full Task issues for this phase — see #.., #.., #.."`, filling in the actual issue numbers just created. Otherwise the placeholder sits in the sprint's burn-down denominator as an item that can never represent real completed work, and the sprint can never legitimately reach 100%.
|
|
237
237
|
|
|
238
238
|
4. **Report back everything created**, grouped by phase — sprint issue number, task issue numbers, and the placeholder-close.
|
|
@@ -245,4 +245,4 @@ When there's no issue number in hand yet:
|
|
|
245
245
|
- **Production releases awaiting a decision:** `gh issue list --repo <owner>/<repo> --label production --state open`
|
|
246
246
|
- **Active sprints:** `gh issue list --repo <owner>/<repo> --label sprint --state open` (title contains `SPRINT -`)
|
|
247
247
|
- **Open QA requests:** `gh issue list --repo <owner>/<repo> --label qa-request --state open`
|
|
248
|
-
- **A sprint's own children:** `gh issue list --repo <owner>/<repo> --label sprint-
|
|
248
|
+
- **A sprint's own children:** `gh issue list --repo <owner>/<repo> --label sprint-child --search "\"Parent Sprint: #<N>\" in:body"` — the exact-phrase quotes matter, otherwise the search matches "Parent", "Sprint", and the number as separate free-text terms instead of the literal phrase. On a repo installed before 1.5.0 (or one that hasn't run the `gh label edit` migration — see `docs/consumer-setup.md`), use `--label sprint-active` instead, or drop `--label` entirely and rely on the body search alone if you're not sure which name applies.
|
|
@@ -26,7 +26,7 @@ jobs:
|
|
|
26
26
|
{ name: 'intake', color: '0E8A16' },
|
|
27
27
|
{ name: 'bug', color: 'D93F0B' },
|
|
28
28
|
{ name: 'sprint', color: '1D76DB' },
|
|
29
|
-
{ name: 'sprint-
|
|
29
|
+
{ name: 'sprint-child', color: '1D76DB', description: "Applied to a sprint's task-breakdown children on open; doesn't change when the sprint closes" },
|
|
30
30
|
{ name: 'planning', color: '5319E7' },
|
|
31
31
|
{ name: 'sprint-planning', color: '5319E7' },
|
|
32
32
|
{ name: 'task', color: '7057FF' },
|
|
@@ -40,13 +40,14 @@ jobs:
|
|
|
40
40
|
{ name: 'risk', color: 'B60205' },
|
|
41
41
|
];
|
|
42
42
|
let created = 0;
|
|
43
|
-
for (const { name, color } of labels) {
|
|
43
|
+
for (const { name, color, description } of labels) {
|
|
44
44
|
try {
|
|
45
45
|
await github.rest.issues.createLabel({
|
|
46
46
|
owner: context.repo.owner,
|
|
47
47
|
repo: context.repo.repo,
|
|
48
48
|
name,
|
|
49
49
|
color,
|
|
50
|
+
...(description ? { description } : {}),
|
|
50
51
|
});
|
|
51
52
|
console.log(`Created: ${name}`);
|
|
52
53
|
created++;
|
|
@@ -77,7 +77,10 @@ jobs:
|
|
|
77
77
|
fi
|
|
78
78
|
fi
|
|
79
79
|
|
|
80
|
-
|
|
80
|
+
# sprint-active is the pre-1.5.0 label name; still checked here so
|
|
81
|
+
# issues created before a repo upgrades don't go silent on close.
|
|
82
|
+
if [[ "${{ contains(github.event.issue.labels.*.name || fromJSON('[]'), 'sprint-child') }}" == "true" ]] || \
|
|
83
|
+
[[ "${{ contains(github.event.issue.labels.*.name || fromJSON('[]'), 'sprint-active') }}" == "true" ]]; then
|
|
81
84
|
if [ "$ACTION" = "opened" ]; then
|
|
82
85
|
MESSAGE="🟡🛠️ SPRINT TASK CREATED%0A$TITLE%0A$URL%0A---%0A👤 $ACTOR%0A🕒 $TIMESTAMP"
|
|
83
86
|
elif [ "$ACTION" = "closed" ]; then
|
|
@@ -129,7 +132,8 @@ jobs:
|
|
|
129
132
|
elif [[ "${{ contains(github.event.issue.labels.*.name || fromJSON('[]'), 'production') }}" == "true" ]]; then
|
|
130
133
|
MESSAGE="💬🚀 RELEASE COMMENT%0A$TITLE%0A$URL%0A---%0A$COMMENT%0A---%0A👤 $ACTOR%0A🕒 $TIMESTAMP"
|
|
131
134
|
|
|
132
|
-
elif [[ "${{ contains(github.event.issue.labels.*.name || fromJSON('[]'), 'sprint-
|
|
135
|
+
elif [[ "${{ contains(github.event.issue.labels.*.name || fromJSON('[]'), 'sprint-child') }}" == "true" ]] || \
|
|
136
|
+
[[ "${{ contains(github.event.issue.labels.*.name || fromJSON('[]'), 'sprint-active') }}" == "true" ]]; then
|
|
133
137
|
MESSAGE="💬🛠️ SPRINT TASK COMMENT%0A$TITLE%0A$URL%0A---%0A$COMMENT%0A---%0A👤 $ACTOR%0A🕒 $TIMESTAMP"
|
|
134
138
|
fi
|
|
135
139
|
fi
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "github-delivery-os",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "A GitHub-native Delivery Governance Framework for structured sprint execution, QA review, and collaborative production release control.",
|
|
5
5
|
"main": "src/install.js",
|
|
6
6
|
"bin": {
|
package/src/cli.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
const { program } = require('commander');
|
|
3
|
+
const { program, Option } = require('commander');
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const fs = require('fs');
|
|
6
6
|
const { runInstall, runStatus, runUninstall } = require('./install');
|
|
@@ -21,8 +21,12 @@ program
|
|
|
21
21
|
.option('-t, --with-templates', 'Copy issue templates (sprint, task, bug, QA, production release)')
|
|
22
22
|
.option('-l, --with-labels', 'Create labels via gh CLI (requires gh auth)')
|
|
23
23
|
.option('-s, --with-skill', 'Add the delivery-ops Claude Code skill (.claude/skills/delivery-ops/SKILL.md)')
|
|
24
|
-
.option('-
|
|
25
|
-
.option('--no-
|
|
24
|
+
.option('-u, --update', 'Replace existing workflow/template files with the latest version')
|
|
25
|
+
.option('--no-update', 'Skip existing files (default)')
|
|
26
|
+
// Pre-1.5.0 names, kept working silently so existing scripts/CI calling
|
|
27
|
+
// `install --overwrite` don't break — --update is the documented name now.
|
|
28
|
+
.addOption(new Option('-o, --overwrite').hideHelp())
|
|
29
|
+
.addOption(new Option('--no-overwrite').hideHelp())
|
|
26
30
|
.option('-d, --dry-run', 'Show what would happen without changing files')
|
|
27
31
|
.action((target, options) => {
|
|
28
32
|
const targetDir = target || '.';
|
|
@@ -31,7 +35,7 @@ program
|
|
|
31
35
|
withTemplates: options.withTemplates ?? false,
|
|
32
36
|
withLabels: options.withLabels ?? false,
|
|
33
37
|
withSkill: options.withSkill ?? false,
|
|
34
|
-
overwrite: options.overwrite ?? false,
|
|
38
|
+
overwrite: options.update ?? options.overwrite ?? false,
|
|
35
39
|
dryRun: options.dryRun ?? false,
|
|
36
40
|
});
|
|
37
41
|
});
|
package/src/install.js
CHANGED
|
@@ -51,7 +51,7 @@ const LABELS = [
|
|
|
51
51
|
['intake', '0E8A16'],
|
|
52
52
|
['bug', 'D93F0B'],
|
|
53
53
|
['sprint', '1D76DB'],
|
|
54
|
-
['sprint-
|
|
54
|
+
['sprint-child', '1D76DB', "Applied to a sprint's task-breakdown children on open; doesn't change when the sprint closes"],
|
|
55
55
|
['planning', '5319E7'],
|
|
56
56
|
['sprint-planning', '5319E7'],
|
|
57
57
|
['task', '7057FF'],
|
|
@@ -351,9 +351,11 @@ function runInstall(options) {
|
|
|
351
351
|
}
|
|
352
352
|
|
|
353
353
|
if (!labelsSkipReason) {
|
|
354
|
-
for (const [name, color] of LABELS) {
|
|
354
|
+
for (const [name, color, description] of LABELS) {
|
|
355
355
|
try {
|
|
356
|
-
|
|
356
|
+
const args = ['label', 'create', name, '--color', color];
|
|
357
|
+
if (description) args.push('--description', description);
|
|
358
|
+
execFileSync('gh', args, {
|
|
357
359
|
cwd: targetAbs,
|
|
358
360
|
stdio: 'pipe',
|
|
359
361
|
});
|
|
@@ -406,11 +408,11 @@ function runInstall(options) {
|
|
|
406
408
|
if (templatesPresentButNotTouched || skillPresentButNotTouched) {
|
|
407
409
|
console.log(' Note: previously-installed templates and/or the Claude Code skill exist');
|
|
408
410
|
console.log(' on disk but were not requested this run, so the recorded Delivery OS');
|
|
409
|
-
console.log(' version was not updated. Re-run with --
|
|
411
|
+
console.log(' version was not updated. Re-run with --update plus --with-templates');
|
|
410
412
|
console.log(' and/or --with-skill to bring everything (and the recorded version) in sync.');
|
|
411
413
|
} else {
|
|
412
414
|
console.log(' Note: some files already existed and were skipped, so the recorded');
|
|
413
|
-
console.log(' Delivery OS version was not updated. Re-run with --
|
|
415
|
+
console.log(' Delivery OS version was not updated. Re-run with --update to sync');
|
|
414
416
|
console.log(' all files (and the recorded version) to the latest release.');
|
|
415
417
|
}
|
|
416
418
|
console.log('');
|
|
@@ -453,7 +455,7 @@ function runInstall(options) {
|
|
|
453
455
|
console.log('Dry run complete. No files were changed.');
|
|
454
456
|
} else {
|
|
455
457
|
console.log('No new files created (existing files were skipped).');
|
|
456
|
-
console.log('To update: use --
|
|
458
|
+
console.log('To update: use --update (run with --dry-run first to preview).');
|
|
457
459
|
}
|
|
458
460
|
}
|
|
459
461
|
console.log('');
|
|
@@ -464,11 +466,11 @@ function runInstall(options) {
|
|
|
464
466
|
// installed actually gets touched this run (see the cleanInstall check
|
|
465
467
|
// there) — so a repo with templates and/or the skill already on disk needs
|
|
466
468
|
// --with-templates/--with-skill passed again on an update, not just
|
|
467
|
-
// --
|
|
469
|
+
// --update, or the recorded version never advances and `status` keeps
|
|
468
470
|
// suggesting the same command forever. Build the hint from what's actually
|
|
469
471
|
// on disk so it's never wrong.
|
|
470
|
-
function
|
|
471
|
-
const flags = [hasTemplates ? '--with-templates' : null, hasSkill ? '--with-skill' : null, '--
|
|
472
|
+
function buildUpdateCommand({ hasTemplates, hasSkill }) {
|
|
473
|
+
const flags = [hasTemplates ? '--with-templates' : null, hasSkill ? '--with-skill' : null, '--update']
|
|
472
474
|
.filter(Boolean)
|
|
473
475
|
.join(' ');
|
|
474
476
|
return `npx github-delivery-os@latest install ${flags} .`;
|
|
@@ -533,7 +535,7 @@ async function runStatus(options) {
|
|
|
533
535
|
} else {
|
|
534
536
|
console.log('Installed version: unknown (installed before version tracking was added)');
|
|
535
537
|
console.log(
|
|
536
|
-
` Run: ${
|
|
538
|
+
` Run: ${buildUpdateCommand({ hasTemplates: installedTemplates.length > 0, hasSkill: skillInstalled })}`
|
|
537
539
|
);
|
|
538
540
|
}
|
|
539
541
|
|
|
@@ -546,7 +548,7 @@ async function runStatus(options) {
|
|
|
546
548
|
} else if (manifest && manifest.version) {
|
|
547
549
|
console.log(`⬆️ Update available: ${manifest.version} → ${latest}`);
|
|
548
550
|
console.log(
|
|
549
|
-
` Run: ${
|
|
551
|
+
` Run: ${buildUpdateCommand({ hasTemplates: installedTemplates.length > 0, hasSkill: skillInstalled })}`
|
|
550
552
|
);
|
|
551
553
|
} else {
|
|
552
554
|
console.log(`Latest published version: ${latest}`);
|
|
@@ -568,7 +570,7 @@ async function runStatus(options) {
|
|
|
568
570
|
});
|
|
569
571
|
console.log(' That workflow will fail with MODULE_NOT_FOUND the next time it runs.');
|
|
570
572
|
console.log(
|
|
571
|
-
` Fix: ${
|
|
573
|
+
` Fix: ${buildUpdateCommand({ hasTemplates: installedTemplates.length > 0, hasSkill: skillInstalled })}`
|
|
572
574
|
);
|
|
573
575
|
console.log('');
|
|
574
576
|
}
|
|
@@ -579,7 +581,7 @@ async function runStatus(options) {
|
|
|
579
581
|
console.log(' require()s a script under .github/scripts will fail with "module is not');
|
|
580
582
|
console.log(' defined in ES module scope" the next time it runs.');
|
|
581
583
|
console.log(
|
|
582
|
-
` Fix: ${
|
|
584
|
+
` Fix: ${buildUpdateCommand({ hasTemplates: installedTemplates.length > 0, hasSkill: skillInstalled })}`
|
|
583
585
|
);
|
|
584
586
|
console.log('');
|
|
585
587
|
}
|
|
@@ -768,7 +770,7 @@ module.exports = {
|
|
|
768
770
|
readManifest,
|
|
769
771
|
writeManifest,
|
|
770
772
|
fetchLatestVersion,
|
|
771
|
-
|
|
773
|
+
buildUpdateCommand,
|
|
772
774
|
skillPath,
|
|
773
775
|
SKILL_REL_PATH,
|
|
774
776
|
WORKFLOWS,
|
|
@@ -776,5 +778,6 @@ module.exports = {
|
|
|
776
778
|
SCRIPTS,
|
|
777
779
|
SCRIPTS_PACKAGE_JSON,
|
|
778
780
|
REQUIRED_SCRIPT_BY_WORKFLOW,
|
|
781
|
+
LABELS,
|
|
779
782
|
},
|
|
780
783
|
};
|