github-delivery-os 1.0.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.
- package/.github/ISSUE_TEMPLATE/bug_report.yml +84 -0
- package/.github/ISSUE_TEMPLATE/config.yml +2 -0
- package/.github/ISSUE_TEMPLATE/production_release_qa_signoff.yml +51 -0
- package/.github/ISSUE_TEMPLATE/qa_request.yml +55 -0
- package/.github/ISSUE_TEMPLATE/sprint_planning.yml +68 -0
- package/.github/ISSUE_TEMPLATE/task.yml +91 -0
- package/.github/workflows/authorize-deployment.yml +106 -0
- package/.github/workflows/auto-assign-qa.yml +25 -0
- package/.github/workflows/auto-close-sprint.yml +135 -0
- package/.github/workflows/notify-release-approver.yml +54 -0
- package/.github/workflows/setup-labels.yml +61 -0
- package/.github/workflows/sprint-child-creator.yml +44 -0
- package/.github/workflows/telegram-issues.yml +135 -0
- package/LICENSE +21 -0
- package/README.md +104 -0
- package/bin/delivery-os.js +3 -0
- package/package.json +44 -0
- package/src/cli.js +42 -0
- package/src/install.js +232 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Run once: Actions → Setup Labels → Run workflow
|
|
2
|
+
# Uses built-in GITHUB_TOKEN — no personal token or gh CLI needed.
|
|
3
|
+
# Labels must match scripts/install.sh --with-labels (same order and colors).
|
|
4
|
+
|
|
5
|
+
name: Setup Labels
|
|
6
|
+
|
|
7
|
+
on:
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
|
|
10
|
+
permissions:
|
|
11
|
+
contents: read
|
|
12
|
+
issues: write
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
create-labels:
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
permissions:
|
|
18
|
+
contents: read
|
|
19
|
+
issues: write
|
|
20
|
+
steps:
|
|
21
|
+
- name: Create labels
|
|
22
|
+
uses: actions/github-script@v7
|
|
23
|
+
with:
|
|
24
|
+
script: |
|
|
25
|
+
const labels = [
|
|
26
|
+
{ name: 'intake', color: '0E8A16' },
|
|
27
|
+
{ name: 'bug', color: 'D93F0B' },
|
|
28
|
+
{ name: 'sprint', color: '1D76DB' },
|
|
29
|
+
{ name: 'sprint-active', color: '1D76DB' },
|
|
30
|
+
{ name: 'planning', color: '5319E7' },
|
|
31
|
+
{ name: 'sprint-planning', color: '5319E7' },
|
|
32
|
+
{ name: 'task', color: '7057FF' },
|
|
33
|
+
{ name: 'qa', color: 'FBCA04' },
|
|
34
|
+
{ name: 'qa-request', color: 'FBCA04' },
|
|
35
|
+
{ name: 'production', color: 'D93F0B' },
|
|
36
|
+
{ name: 'release', color: 'B60205' },
|
|
37
|
+
{ name: 'approval', color: '0E8A16' },
|
|
38
|
+
{ name: 'ready-for-deploy', color: '0E8A16' },
|
|
39
|
+
{ name: 'declined', color: 'B60205' },
|
|
40
|
+
{ name: 'risk', color: 'B60205' },
|
|
41
|
+
];
|
|
42
|
+
let created = 0;
|
|
43
|
+
for (const { name, color } of labels) {
|
|
44
|
+
try {
|
|
45
|
+
await github.rest.issues.createLabel({
|
|
46
|
+
owner: context.repo.owner,
|
|
47
|
+
repo: context.repo.repo,
|
|
48
|
+
name,
|
|
49
|
+
color,
|
|
50
|
+
});
|
|
51
|
+
console.log(`Created: ${name}`);
|
|
52
|
+
created++;
|
|
53
|
+
} catch (e) {
|
|
54
|
+
if (e.message?.includes('already exists')) {
|
|
55
|
+
console.log(`Exists: ${name}`);
|
|
56
|
+
} else {
|
|
57
|
+
console.log(`Failed: ${name} - ${e.message}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
console.log(`\nCreated ${created} label(s).`);
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
name: Sprint Child Issue Creator
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
issues:
|
|
5
|
+
types: [opened]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
issues: write
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
create-child-issues:
|
|
12
|
+
if: contains(github.event.issue.title, 'SPRINT -')
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
permissions:
|
|
15
|
+
issues: write
|
|
16
|
+
|
|
17
|
+
steps:
|
|
18
|
+
- name: Create Child Issues
|
|
19
|
+
uses: actions/github-script@v7
|
|
20
|
+
with:
|
|
21
|
+
script: |
|
|
22
|
+
const issue = context.payload.issue;
|
|
23
|
+
const body = issue.body || "";
|
|
24
|
+
const parentNumber = issue.number;
|
|
25
|
+
|
|
26
|
+
const featuresMatch = body.match(/### Sprint Features[\s\S]*?(?=###|$)/);
|
|
27
|
+
if (!featuresMatch) return;
|
|
28
|
+
|
|
29
|
+
const features = featuresMatch[0]
|
|
30
|
+
.split('\n')
|
|
31
|
+
.map(line => line.trim())
|
|
32
|
+
.filter(line => line && !line.startsWith('###'));
|
|
33
|
+
|
|
34
|
+
const bodyContent = `Parent Sprint: #${parentNumber}\n\n---\n*Created by Delivery OS Sprint Child Creator*`;
|
|
35
|
+
|
|
36
|
+
for (const title of features) {
|
|
37
|
+
await github.rest.issues.create({
|
|
38
|
+
owner: context.repo.owner,
|
|
39
|
+
repo: context.repo.repo,
|
|
40
|
+
title,
|
|
41
|
+
body: bodyContent,
|
|
42
|
+
labels: ['sprint-active']
|
|
43
|
+
});
|
|
44
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
name: GitHub → Telegram Alerts
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
issues:
|
|
5
|
+
types: [opened, closed, reopened]
|
|
6
|
+
|
|
7
|
+
issue_comment:
|
|
8
|
+
types: [created]
|
|
9
|
+
|
|
10
|
+
pull_request:
|
|
11
|
+
types: [closed]
|
|
12
|
+
|
|
13
|
+
permissions:
|
|
14
|
+
contents: read
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
notify:
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
steps:
|
|
20
|
+
- name: Send Telegram Alert
|
|
21
|
+
shell: bash
|
|
22
|
+
env:
|
|
23
|
+
TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
|
|
24
|
+
TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }}
|
|
25
|
+
RELEASE_APPROVER: ${{ vars.RELEASE_APPROVER || 'release-approver' }}
|
|
26
|
+
EVENT_NAME: ${{ github.event_name }}
|
|
27
|
+
EVENT_ACTION: ${{ github.event.action }}
|
|
28
|
+
ACTOR: ${{ github.actor }}
|
|
29
|
+
ISSUE_TITLE: ${{ github.event.issue.title }}
|
|
30
|
+
ISSUE_URL: ${{ github.event.issue.html_url }}
|
|
31
|
+
COMMENT_BODY: ${{ github.event.comment.body }}
|
|
32
|
+
PR_MERGED: ${{ github.event.pull_request.merged }}
|
|
33
|
+
PR_BASE: ${{ github.event.pull_request.base.ref }}
|
|
34
|
+
PR_TITLE: ${{ github.event.pull_request.title }}
|
|
35
|
+
PR_URL: ${{ github.event.pull_request.html_url }}
|
|
36
|
+
run: |
|
|
37
|
+
EVENT="${EVENT_NAME}"
|
|
38
|
+
ACTION="${EVENT_ACTION}"
|
|
39
|
+
TIMESTAMP=$(TZ=Africa/Nairobi date +"%Y-%m-%d %-I:%M %p EAT")
|
|
40
|
+
MESSAGE=""
|
|
41
|
+
|
|
42
|
+
if [ -z "$TELEGRAM_BOT_TOKEN" ] || [ -z "$TELEGRAM_CHAT_ID" ]; then
|
|
43
|
+
echo "Telegram secrets not configured. Skipping."
|
|
44
|
+
exit 0
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
if [ "$EVENT" = "issues" ]; then
|
|
48
|
+
|
|
49
|
+
TITLE="${ISSUE_TITLE}"
|
|
50
|
+
URL="${ISSUE_URL}"
|
|
51
|
+
|
|
52
|
+
if [[ "${{ contains(github.event.issue.labels.*.name || fromJSON('[]'), 'bug') }}" == "true" ]]; then
|
|
53
|
+
if [ "$ACTION" = "opened" ]; then
|
|
54
|
+
MESSAGE="🟢🪲 BUG OPENED%0A$TITLE%0A$URL%0A---%0A👤 $ACTOR%0A🕒 $TIMESTAMP"
|
|
55
|
+
elif [ "$ACTION" = "reopened" ]; then
|
|
56
|
+
MESSAGE="🟡♻️ BUG REOPENED%0A$TITLE%0A$URL%0A---%0A👤 $ACTOR%0A🕒 $TIMESTAMP"
|
|
57
|
+
elif [ "$ACTION" = "closed" ]; then
|
|
58
|
+
MESSAGE="🟣✅ BUG CLOSED%0A$TITLE%0A$URL%0A---%0A👤 $ACTOR%0A🕒 $TIMESTAMP"
|
|
59
|
+
fi
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
if [[ "${{ contains(github.event.issue.labels.*.name || fromJSON('[]'), 'qa') }}" == "true" ]] || \
|
|
63
|
+
[[ "${{ contains(github.event.issue.labels.*.name || fromJSON('[]'), 'qa-request') }}" == "true" ]]; then
|
|
64
|
+
if [ "$ACTION" = "opened" ]; then
|
|
65
|
+
MESSAGE="🔵🧪 QA REQUEST OPENED%0A$TITLE%0A$URL%0A---%0A👤 $ACTOR%0A🕒 $TIMESTAMP"
|
|
66
|
+
elif [ "$ACTION" = "reopened" ]; then
|
|
67
|
+
MESSAGE="🟠🔁 QA REQUEST REOPENED%0A$TITLE%0A$URL%0A---%0A👤 $ACTOR%0A🕒 $TIMESTAMP"
|
|
68
|
+
elif [ "$ACTION" = "closed" ]; then
|
|
69
|
+
MESSAGE="🟢✅ QA REQUEST CLOSED%0A$TITLE%0A$URL%0A---%0A👤 $ACTOR%0A🕒 $TIMESTAMP"
|
|
70
|
+
fi
|
|
71
|
+
fi
|
|
72
|
+
|
|
73
|
+
if [[ "${{ contains(github.event.issue.labels.*.name || fromJSON('[]'), 'sprint') }}" == "true" ]] && \
|
|
74
|
+
[[ "${{ contains(github.event.issue.labels.*.name || fromJSON('[]'), 'planning') }}" == "true" ]]; then
|
|
75
|
+
if [ "$ACTION" = "opened" ]; then
|
|
76
|
+
MESSAGE="🟣📋 SPRINT CREATED%0A$TITLE%0A$URL%0A---%0A👤 $ACTOR%0A🕒 $TIMESTAMP"
|
|
77
|
+
fi
|
|
78
|
+
fi
|
|
79
|
+
|
|
80
|
+
if [[ "${{ contains(github.event.issue.labels.*.name || fromJSON('[]'), 'sprint-active') }}" == "true" ]]; then
|
|
81
|
+
if [ "$ACTION" = "opened" ]; then
|
|
82
|
+
MESSAGE="🟡🛠️ SPRINT TASK CREATED%0A$TITLE%0A$URL%0A---%0A👤 $ACTOR%0A🕒 $TIMESTAMP"
|
|
83
|
+
elif [ "$ACTION" = "closed" ]; then
|
|
84
|
+
MESSAGE="🟢✅ SPRINT TASK COMPLETED%0A$TITLE%0A$URL%0A---%0A👤 $ACTOR%0A🕒 $TIMESTAMP"
|
|
85
|
+
fi
|
|
86
|
+
fi
|
|
87
|
+
|
|
88
|
+
if [[ "${{ contains(github.event.issue.labels.*.name || fromJSON('[]'), 'production') }}" == "true" ]]; then
|
|
89
|
+
if [ "$ACTION" = "opened" ]; then
|
|
90
|
+
MESSAGE="🚀📦 PRODUCTION RELEASE CREATED%0A$TITLE%0A$URL%0A---%0A👤 $ACTOR%0A🕒 $TIMESTAMP"
|
|
91
|
+
elif [ "$ACTION" = "closed" ]; then
|
|
92
|
+
MESSAGE="🟣🚀 RELEASE CLOSED%0A$TITLE%0A$URL%0A---%0A👤 $ACTOR%0A🕒 $TIMESTAMP"
|
|
93
|
+
fi
|
|
94
|
+
fi
|
|
95
|
+
fi
|
|
96
|
+
|
|
97
|
+
if [ "$EVENT" = "issue_comment" ]; then
|
|
98
|
+
|
|
99
|
+
TITLE="${ISSUE_TITLE}"
|
|
100
|
+
URL="${ISSUE_URL}"
|
|
101
|
+
COMMENT=$(echo "$COMMENT_BODY" | head -c 300)
|
|
102
|
+
|
|
103
|
+
if [[ "$ACTOR" == "$RELEASE_APPROVER" ]] && echo "$COMMENT_BODY" | grep -iq "declined\|reject\|not approved"; then
|
|
104
|
+
MESSAGE="🔴🛑 RELEASE DECLINED%0A$TITLE%0A$URL%0A---%0A👤 $ACTOR%0A🕒 $TIMESTAMP"
|
|
105
|
+
|
|
106
|
+
elif [[ "$ACTOR" == "$RELEASE_APPROVER" ]] && echo "$COMMENT_BODY" | grep -iq "^approved"; then
|
|
107
|
+
MESSAGE="🟢🛡️ RELEASE APPROVED%0A$TITLE%0A$URL%0A---%0A👤 $ACTOR%0A🕒 $TIMESTAMP"
|
|
108
|
+
|
|
109
|
+
elif [[ "${{ contains(github.event.issue.labels.*.name || fromJSON('[]'), 'bug') }}" == "true" ]]; then
|
|
110
|
+
MESSAGE="💬🪲 BUG COMMENT%0A$TITLE%0A$URL%0A---%0A$COMMENT%0A---%0A👤 $ACTOR%0A🕒 $TIMESTAMP"
|
|
111
|
+
|
|
112
|
+
elif [[ "${{ contains(github.event.issue.labels.*.name || fromJSON('[]'), 'qa') }}" == "true" ]] || \
|
|
113
|
+
[[ "${{ contains(github.event.issue.labels.*.name || fromJSON('[]'), 'qa-request') }}" == "true" ]]; then
|
|
114
|
+
MESSAGE="💬🧪 QA COMMENT%0A$TITLE%0A$URL%0A---%0A$COMMENT%0A---%0A👤 $ACTOR%0A🕒 $TIMESTAMP"
|
|
115
|
+
|
|
116
|
+
elif [[ "${{ contains(github.event.issue.labels.*.name || fromJSON('[]'), 'production') }}" == "true" ]]; then
|
|
117
|
+
MESSAGE="💬🚀 RELEASE COMMENT%0A$TITLE%0A$URL%0A---%0A$COMMENT%0A---%0A👤 $ACTOR%0A🕒 $TIMESTAMP"
|
|
118
|
+
|
|
119
|
+
elif [[ "${{ contains(github.event.issue.labels.*.name || fromJSON('[]'), 'sprint-active') }}" == "true" ]]; then
|
|
120
|
+
MESSAGE="💬🛠️ SPRINT TASK COMMENT%0A$TITLE%0A$URL%0A---%0A$COMMENT%0A---%0A👤 $ACTOR%0A🕒 $TIMESTAMP"
|
|
121
|
+
fi
|
|
122
|
+
fi
|
|
123
|
+
|
|
124
|
+
if [ "$EVENT" = "pull_request" ]; then
|
|
125
|
+
|
|
126
|
+
if [ "$PR_MERGED" = "true" ] && [ "$PR_BASE" = "main" ]; then
|
|
127
|
+
MESSAGE="🟢🔀 PR MERGED TO MAIN%0A${PR_TITLE}%0A${PR_URL}%0A---%0A👤 $ACTOR%0A🕒 $TIMESTAMP"
|
|
128
|
+
fi
|
|
129
|
+
fi
|
|
130
|
+
|
|
131
|
+
if [ -n "$MESSAGE" ]; then
|
|
132
|
+
curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
|
|
133
|
+
-d chat_id="${TELEGRAM_CHAT_ID}" \
|
|
134
|
+
-d text="${MESSAGE}"
|
|
135
|
+
fi
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 GitHub Delivery Operating System Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# GitHub Delivery Operating System
|
|
2
|
+
|
|
3
|
+
> A GitHub-native Delivery Governance Framework for structured sprint execution, QA review, and collaborative production release control.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Why This Exists
|
|
8
|
+
|
|
9
|
+
Engineering teams often rely on informal coordination inside GitHub — manual approvals, inconsistent sprint tracking, reactive QA engagement, and socially enforced production releases.
|
|
10
|
+
|
|
11
|
+
As teams scale, this creates:
|
|
12
|
+
|
|
13
|
+
* Delivery ambiguity
|
|
14
|
+
* QA bottlenecks
|
|
15
|
+
* Unclear accountability
|
|
16
|
+
* Release risk
|
|
17
|
+
* Cross-team misalignment
|
|
18
|
+
|
|
19
|
+
The **GitHub Delivery Operating System (Delivery OS)** embeds structured intake, sprint orchestration, QA governance, and collaborative release gates directly into engineering repositories — without replacing CI/CD pipelines or disrupting developer workflows.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
**One command** (recommended):
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npx github-delivery-os install --with-templates .
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
From your repo root. Add `--with-labels` to create labels via `gh` CLI (requires `gh auth`). Use `--dry-run` to preview first.
|
|
32
|
+
|
|
33
|
+
**Alternative — clone and run script:**
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
git clone https://github.com/jkaweesi22/github-delivery-operating-system
|
|
37
|
+
cd github-delivery-operating-system
|
|
38
|
+
|
|
39
|
+
# New install or repo with existing workflows — adds only missing files (safe)
|
|
40
|
+
./scripts/install.sh --with-templates /path/to/your-repo
|
|
41
|
+
|
|
42
|
+
# Also create labels via gh CLI (requires gh auth)
|
|
43
|
+
./scripts/install.sh --with-templates --with-labels /path/to/your-repo
|
|
44
|
+
|
|
45
|
+
# Update Delivery OS (replace existing) — use --dry-run first to preview
|
|
46
|
+
./scripts/install.sh --with-templates --overwrite /path/to/your-repo
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**Note:** By default, existing files are **never overwritten**. Use `--overwrite` only when updating Delivery OS. See [Consumer Setup](docs/consumer-setup.md) for the full command guide.
|
|
50
|
+
|
|
51
|
+
**What gets installed:**
|
|
52
|
+
|
|
53
|
+
| Workflow | Purpose |
|
|
54
|
+
|----------|---------|
|
|
55
|
+
| `sprint-child-creator` | Creates child issues when a sprint (title `SPRINT -`) is opened |
|
|
56
|
+
| `auto-close-sprint` | Burn-down, sprint health, auto-close at 100% |
|
|
57
|
+
| `notify-release-approver` | Pings approver when production release issue opens |
|
|
58
|
+
| `authorize-deployment` | Dual approval (release approver + QA) |
|
|
59
|
+
| `auto-assign-qa` | Assigns QA team to `qa` / `qa-request` issues |
|
|
60
|
+
| `telegram-issues` | Telegram alerts for bugs, QA, sprints, releases |
|
|
61
|
+
| `setup-labels` | One-time workflow to create required labels |
|
|
62
|
+
|
|
63
|
+
Workflows and templates are **copied directly** into your repo. No `workflow_call` or external references.
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Quick Start (After Install)
|
|
68
|
+
|
|
69
|
+
1. **Create labels:** Actions → Setup Labels → Run workflow
|
|
70
|
+
2. **Configure variables:** Settings → Secrets and variables → Actions → Variables
|
|
71
|
+
- `RELEASE_APPROVER` — GitHub username
|
|
72
|
+
- `QA_APPROVER` — GitHub username
|
|
73
|
+
- `QA_ASSIGNEES` — Comma-separated usernames (e.g. `user1,user2`)
|
|
74
|
+
3. **Optional:** Add `TELEGRAM_BOT_TOKEN` and `TELEGRAM_CHAT_ID` for alerts
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## Sprint Child Creation
|
|
79
|
+
|
|
80
|
+
When you open an issue using the **Sprint Planning** template with a title like `SPRINT - Sprint 12`:
|
|
81
|
+
|
|
82
|
+
1. Each line under "Sprint Features (One Per Line)" becomes a child issue
|
|
83
|
+
2. Child issues link back with `Parent Sprint: #N`
|
|
84
|
+
3. Closing child issues updates burn-down; sprint auto-closes at 100%
|
|
85
|
+
|
|
86
|
+
**Required:** Install with `--with-templates` so the sprint form is available.
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## Documentation
|
|
91
|
+
|
|
92
|
+
| Document | Description |
|
|
93
|
+
|----------|-------------|
|
|
94
|
+
| **[Landing page & quick start](https://jkaweesi22.github.io/github-delivery-operating-system/)** | Overview, one-command install, features |
|
|
95
|
+
| [Consumer Setup](docs/consumer-setup.md) | Installation, configuration, variables, labels, Telegram, uninstall |
|
|
96
|
+
| [How To](docs/how-to.md) | Create sprints, request releases, approve, report bugs, QA requests |
|
|
97
|
+
| [Architecture](docs/architecture.md) | Workflows, templates, data flow |
|
|
98
|
+
| [Governance](docs/governance.md) | Lifecycle, approval gates, automation rules |
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## License
|
|
103
|
+
|
|
104
|
+
MIT License. See [LICENSE](LICENSE) for details.
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "github-delivery-os",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A GitHub-native Delivery Governance Framework for structured sprint execution, QA review, and collaborative production release control.",
|
|
5
|
+
"main": "src/install.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"delivery-os": "./bin/delivery-os.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"install:local": "node bin/delivery-os.js install --with-templates ."
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"github",
|
|
14
|
+
"delivery",
|
|
15
|
+
"sprint",
|
|
16
|
+
"qa",
|
|
17
|
+
"release",
|
|
18
|
+
"governance",
|
|
19
|
+
"workflows",
|
|
20
|
+
"automation"
|
|
21
|
+
],
|
|
22
|
+
"author": "",
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"repository": {
|
|
25
|
+
"type": "git",
|
|
26
|
+
"url": "https://github.com/jkaweesi22/github-delivery-operating-system"
|
|
27
|
+
},
|
|
28
|
+
"homepage": "https://jkaweesi22.github.io/github-delivery-operating-system/",
|
|
29
|
+
"bugs": {
|
|
30
|
+
"url": "https://github.com/jkaweesi22/github-delivery-operating-system/issues"
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"bin",
|
|
34
|
+
"src",
|
|
35
|
+
".github/workflows",
|
|
36
|
+
".github/ISSUE_TEMPLATE"
|
|
37
|
+
],
|
|
38
|
+
"engines": {
|
|
39
|
+
"node": ">=14.0.0"
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"commander": "^11.1.0"
|
|
43
|
+
}
|
|
44
|
+
}
|
package/src/cli.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { program } = require('commander');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const { runInstall } = require('./install');
|
|
7
|
+
|
|
8
|
+
const pkgPath = path.join(__dirname, '..', 'package.json');
|
|
9
|
+
const version = fs.existsSync(pkgPath)
|
|
10
|
+
? require(pkgPath).version
|
|
11
|
+
: '1.0.0';
|
|
12
|
+
|
|
13
|
+
program
|
|
14
|
+
.name('delivery-os')
|
|
15
|
+
.description('GitHub Delivery Operating System — structured sprint execution, QA review, and production release control')
|
|
16
|
+
.version(version);
|
|
17
|
+
|
|
18
|
+
program
|
|
19
|
+
.command('install [target]')
|
|
20
|
+
.description('Install workflows and templates into a repository')
|
|
21
|
+
.option('-t, --with-templates', 'Copy issue templates (sprint, task, bug, QA, production release)')
|
|
22
|
+
.option('-l, --with-labels', 'Create labels via gh CLI (requires gh auth)')
|
|
23
|
+
.option('-o, --overwrite', 'Replace existing workflow/template files')
|
|
24
|
+
.option('--no-overwrite', 'Skip existing files (default)')
|
|
25
|
+
.option('-d, --dry-run', 'Show what would happen without changing files')
|
|
26
|
+
.action((target, options) => {
|
|
27
|
+
const targetDir = target || '.';
|
|
28
|
+
runInstall({
|
|
29
|
+
targetDir,
|
|
30
|
+
withTemplates: options.withTemplates ?? false,
|
|
31
|
+
withLabels: options.withLabels ?? false,
|
|
32
|
+
overwrite: options.overwrite ?? false,
|
|
33
|
+
dryRun: options.dryRun ?? false,
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
program.parse();
|
|
38
|
+
|
|
39
|
+
// Show help if no command
|
|
40
|
+
if (!process.argv.slice(2).length) {
|
|
41
|
+
program.outputHelp();
|
|
42
|
+
}
|