create-agentic-pdlc 3.0.0 → 3.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.
@@ -0,0 +1,336 @@
1
+ # PROJECT_ID as Actions Variable Implementation Plan
2
+
3
+ > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
4
+
5
+ **Goal:** Replace hardcoded `PROJECT_ID: "PVT_xxx"` in installed YAML files with `${{ vars.PROJECT_ID }}`, and have the installer set the value via the GitHub Actions Variables REST API.
6
+
7
+ **Architecture:** Three workflow templates swap their `PROJECT_ID` env value and guard condition. `bin/cli.js` gains a `setActionsVariable(repo, name, value, execFn)` helper (dependency-injected `execFn` for testability) called after board creation in both the `runFullSetup` and `runUpgradeToAgentic` flows.
8
+
9
+ **Tech Stack:** Node.js 22, `node:test` + `node:assert/strict`, `gh` CLI (`execFileSync`), GitHub Actions Variables REST API (`PATCH`/`POST /repos/{owner}/{repo}/actions/variables`).
10
+
11
+ ---
12
+
13
+ ## File Map
14
+
15
+ | Action | Path | Responsibility |
16
+ |---|---|---|
17
+ | Modify | `templates/.github/workflows/project-automation.yml` | Remove `{{PROJECT_ID}}` placeholder, source from `vars` |
18
+ | Modify | `templates/.github/workflows/add-to-board.yml` | Same |
19
+ | Modify | `templates/.github/workflows/agent-trigger.yml` | Same |
20
+ | Modify | `bin/cli.js` | Add helper, wire call sites, export helper |
21
+ | Modify | `tests/cli.test.js` | Tests for `setActionsVariable` logic |
22
+
23
+ ---
24
+
25
+ ### Task 1: Update workflow templates
26
+
27
+ **Files:**
28
+ - Modify: `templates/.github/workflows/project-automation.yml`
29
+ - Modify: `templates/.github/workflows/add-to-board.yml`
30
+ - Modify: `templates/.github/workflows/agent-trigger.yml`
31
+
32
+ No tests for template content — correctness is verified by the installer integration.
33
+
34
+ - [ ] **Step 1: Replace `PROJECT_ID` env value in all three templates**
35
+
36
+ In each of the three files, find every line matching:
37
+ ```yaml
38
+ PROJECT_ID: "{{PROJECT_ID}}"
39
+ ```
40
+ Replace with:
41
+ ```yaml
42
+ PROJECT_ID: ${{ vars.PROJECT_ID }}
43
+ ```
44
+
45
+ `project-automation.yml` line 12, `add-to-board.yml` line 8, `agent-trigger.yml` line 21.
46
+
47
+ - [ ] **Step 2: Replace guard conditions in all three templates**
48
+
49
+ In each of the three files, find every line matching:
50
+ ```yaml
51
+ env.PROJECT_ID != '{{PROJECT_ID}}'
52
+ ```
53
+ Replace with:
54
+ ```yaml
55
+ env.PROJECT_ID != ''
56
+ ```
57
+
58
+ Do a full-file search in each — there are multiple occurrences per file (every job that conditionally runs).
59
+
60
+ - [ ] **Step 3: Verify no `{{PROJECT_ID}}` remains in workflow files**
61
+
62
+ Run:
63
+ ```bash
64
+ grep -rn "{{PROJECT_ID}}" templates/.github/workflows/
65
+ ```
66
+
67
+ Expected: no output. If any lines appear, fix them before proceeding.
68
+
69
+ - [ ] **Step 4: Commit**
70
+
71
+ ```bash
72
+ git add templates/.github/workflows/project-automation.yml \
73
+ templates/.github/workflows/add-to-board.yml \
74
+ templates/.github/workflows/agent-trigger.yml
75
+ git commit -m "feat(templates): source PROJECT_ID from vars instead of hardcoded placeholder"
76
+ ```
77
+
78
+ ---
79
+
80
+ ### Task 2: Remove `{{PROJECT_ID}}` YAML substitution from `scaffoldFullTemplates`
81
+
82
+ **Files:**
83
+ - Modify: `bin/cli.js:345`
84
+
85
+ - [ ] **Step 1: Delete the single PROJECT_ID substitution line**
86
+
87
+ In `bin/cli.js`, find and remove exactly this line (currently line 345):
88
+ ```javascript
89
+ if (projectId) wfContent = wfContent.replace(/\{\{PROJECT_ID\}\}/g, () => projectId);
90
+ ```
91
+
92
+ The block around it (lines 342–356) substitutes `STATUS_FIELD_ID` and all `ID_*` column options into `project-automation.yml`. Keep all those lines. Only the `PROJECT_ID` line is removed.
93
+
94
+ - [ ] **Step 2: Verify other substitutions are intact**
95
+
96
+ Run:
97
+ ```bash
98
+ grep -n "wfContent.replace" bin/cli.js
99
+ ```
100
+
101
+ Expected output must include lines for `STATUS_FIELD_ID`, `ID_IDEA`, `ID_BRAINSTORMING`, `ID_DETAILING`, `ID_APPROVAL`, `ID_DEVELOPMENT`, `ID_TESTING`, `ID_CODE_REVIEW_PR`, `ID_PRODUCTION`. Must NOT include `PROJECT_ID`.
102
+
103
+ - [ ] **Step 3: Commit**
104
+
105
+ ```bash
106
+ git add bin/cli.js
107
+ git commit -m "fix(cli): remove PROJECT_ID hardcoding from scaffoldFullTemplates"
108
+ ```
109
+
110
+ ---
111
+
112
+ ### Task 3: Add `setActionsVariable` helper (TDD)
113
+
114
+ **Files:**
115
+ - Modify: `bin/cli.js` (add function before `scaffoldFullTemplates`, ~line 283)
116
+ - Modify: `tests/cli.test.js` (add describe block)
117
+ - Modify: `bin/cli.js:793` (add to `module.exports`)
118
+
119
+ - [ ] **Step 1: Write failing tests**
120
+
121
+ Add to `tests/cli.test.js`:
122
+
123
+ ```javascript
124
+ const { describe, it, mock } = require('node:test');
125
+
126
+ // ... existing imports and tests above ...
127
+
128
+ describe('setActionsVariable', () => {
129
+ it('calls PATCH first', () => {
130
+ const calls = [];
131
+ const execFn = (cmd, args) => { calls.push(args); };
132
+ const { setActionsVariable } = require('../bin/cli.js');
133
+ setActionsVariable('owner/repo', 'PROJECT_ID', 'PVT_abc', execFn);
134
+ assert.equal(calls.length, 1);
135
+ assert.ok(calls[0].includes('--method'));
136
+ assert.ok(calls[0].includes('PATCH'));
137
+ assert.ok(calls[0].some(a => a.includes('PROJECT_ID')));
138
+ });
139
+
140
+ it('falls back to POST on 404', () => {
141
+ const calls = [];
142
+ let callCount = 0;
143
+ const execFn = (cmd, args) => {
144
+ calls.push([...args]);
145
+ callCount++;
146
+ if (callCount === 1) {
147
+ const err = new Error('Not Found');
148
+ err.stderr = Buffer.from('Not Found');
149
+ throw err;
150
+ }
151
+ };
152
+ const { setActionsVariable } = require('../bin/cli.js');
153
+ setActionsVariable('owner/repo', 'PROJECT_ID', 'PVT_abc', execFn);
154
+ assert.equal(calls.length, 2);
155
+ assert.ok(calls[0].includes('PATCH'));
156
+ assert.ok(calls[1].includes('POST'));
157
+ });
158
+
159
+ it('throws on 403', () => {
160
+ const execFn = () => {
161
+ const err = new Error('Forbidden');
162
+ err.stderr = Buffer.from('Forbidden');
163
+ throw err;
164
+ };
165
+ const { setActionsVariable } = require('../bin/cli.js');
166
+ assert.throws(
167
+ () => setActionsVariable('owner/repo', 'PROJECT_ID', 'PVT_abc', execFn),
168
+ /Forbidden/
169
+ );
170
+ });
171
+ });
172
+ ```
173
+
174
+ - [ ] **Step 2: Run tests to verify they fail**
175
+
176
+ ```bash
177
+ npm test
178
+ ```
179
+
180
+ Expected: 3 new test failures — `setActionsVariable` is not exported yet.
181
+
182
+ - [ ] **Step 3: Add `setActionsVariable` to `bin/cli.js`**
183
+
184
+ Insert this function before `scaffoldFullTemplates` (~line 283):
185
+
186
+ ```javascript
187
+ function setActionsVariable(repo, name, value, execFn = execFileSync) {
188
+ try {
189
+ execFn('gh', [
190
+ 'api', `repos/${repo}/actions/variables/${name}`,
191
+ '--method', 'PATCH',
192
+ '-f', `name=${name}`,
193
+ '-f', `value=${value}`
194
+ ], { stdio: ['ignore', 'pipe', 'pipe'] });
195
+ } catch (err) {
196
+ const msg = (err.stderr?.toString() || '') + (err.message || '');
197
+ if (msg.includes('404') || msg.includes('Not Found')) {
198
+ execFn('gh', [
199
+ 'api', `repos/${repo}/actions/variables`,
200
+ '--method', 'POST',
201
+ '-f', `name=${name}`,
202
+ '-f', `value=${value}`
203
+ ], { stdio: ['ignore', 'pipe', 'pipe'] });
204
+ } else {
205
+ throw err;
206
+ }
207
+ }
208
+ }
209
+ ```
210
+
211
+ - [ ] **Step 4: Export the function**
212
+
213
+ In `bin/cli.js` at line 793, update:
214
+ ```javascript
215
+ if (typeof module !== 'undefined') module.exports = { resolveMode };
216
+ ```
217
+ to:
218
+ ```javascript
219
+ if (typeof module !== 'undefined') module.exports = { resolveMode, setActionsVariable };
220
+ ```
221
+
222
+ - [ ] **Step 5: Run tests to verify they pass**
223
+
224
+ ```bash
225
+ npm test
226
+ ```
227
+
228
+ Expected: all tests pass including the 3 new `setActionsVariable` tests.
229
+
230
+ - [ ] **Step 6: Commit**
231
+
232
+ ```bash
233
+ git add bin/cli.js tests/cli.test.js
234
+ git commit -m "feat(cli): add setActionsVariable helper with PATCH→POST fallback"
235
+ ```
236
+
237
+ ---
238
+
239
+ ### Task 4: Wire `setActionsVariable` into the create flow (`runFullSetup`)
240
+
241
+ **Files:**
242
+ - Modify: `bin/cli.js` (~line 554)
243
+
244
+ - [ ] **Step 1: Add the call site after the PROJECT_PAT block**
245
+
246
+ In `bin/cli.js`, locate the end of the `PROJECT_PAT` block in `runFullSetup` (the `} else if (projectId && isOrg)` line at ~554). Add after it:
247
+
248
+ ```javascript
249
+ // Set PROJECT_ID as GitHub Actions Variable
250
+ if (projectId) {
251
+ try {
252
+ setActionsVariable(repo, 'PROJECT_ID', projectId);
253
+ console.log(`${green}✅ vars.PROJECT_ID set as Actions Variable.${reset}`);
254
+ } catch (_) {
255
+ console.log(`${yellow}⚠️ Could not set vars.PROJECT_ID — token may lack variables:write scope.\n Set manually: repo Settings → Secrets and variables → Variables → PROJECT_ID = ${projectId}${reset}`);
256
+ }
257
+ }
258
+ ```
259
+
260
+ Insert this block between line 554 (`} else if (projectId && isOrg) { ... }`) and line 556 (`await setBranchProtection(...)`).
261
+
262
+ - [ ] **Step 2: Verify placement**
263
+
264
+ ```bash
265
+ grep -n "vars.PROJECT_ID\|setBranchProtection\|isOrg\)" bin/cli.js | head -20
266
+ ```
267
+
268
+ The `vars.PROJECT_ID` console log should appear between the `isOrg` block and `setBranchProtection`.
269
+
270
+ - [ ] **Step 3: Commit**
271
+
272
+ ```bash
273
+ git add bin/cli.js
274
+ git commit -m "feat(cli): set vars.PROJECT_ID as Actions Variable in create flow"
275
+ ```
276
+
277
+ ---
278
+
279
+ ### Task 5: Wire `setActionsVariable` into the upgrade flow (`runUpgradeToAgentic`)
280
+
281
+ **Files:**
282
+ - Modify: `bin/cli.js` (~line 1006)
283
+
284
+ - [ ] **Step 1: Add the call site after the PROJECT_PAT block in the upgrade flow**
285
+
286
+ In `bin/cli.js`, locate the end of the `PROJECT_PAT` block in `runUpgradeToAgentic` (the closing `}` of `if (projectId && !isOrg)` at ~line 1006). Add after it:
287
+
288
+ ```javascript
289
+ // Set PROJECT_ID as GitHub Actions Variable
290
+ if (projectId) {
291
+ try {
292
+ setActionsVariable(repo, 'PROJECT_ID', projectId);
293
+ console.log(`${green}✅ vars.PROJECT_ID set as Actions Variable.${reset}`);
294
+ } catch (_) {
295
+ console.log(`${yellow}⚠️ Could not set vars.PROJECT_ID — token may lack variables:write scope.\n Set manually: repo Settings → Secrets and variables → Variables → PROJECT_ID = ${projectId}${reset}`);
296
+ }
297
+ }
298
+ ```
299
+
300
+ Insert between line ~1006 (`}` closing the `PROJECT_PAT` block) and line ~1008 (`console.log scaffolding`).
301
+
302
+ - [ ] **Step 2: Verify no `{{PROJECT_ID}}` placeholder survives install**
303
+
304
+ ```bash
305
+ grep -rn "{{PROJECT_ID}}" templates/
306
+ ```
307
+
308
+ Expected: only `templates/full/docs/pdlc.md` (the documentation file). No workflow YAML files.
309
+
310
+ - [ ] **Step 3: Run full test suite**
311
+
312
+ ```bash
313
+ npm test
314
+ ```
315
+
316
+ Expected: all tests pass.
317
+
318
+ - [ ] **Step 4: Commit**
319
+
320
+ ```bash
321
+ git add bin/cli.js
322
+ git commit -m "feat(cli): set vars.PROJECT_ID as Actions Variable in upgrade flow"
323
+ ```
324
+
325
+ ---
326
+
327
+ ## Self-Review Checklist
328
+
329
+ - Acceptance Criteria 1 (new install sets `vars.PROJECT_ID`): Task 4 ✓
330
+ - Acceptance Criteria 2 (`resolve-ids` works without YAML modification): Task 1 ✓
331
+ - Acceptance Criteria 3 (`--update` sets variable): Task 5 ✓
332
+ - Acceptance Criteria 4 (403 warning, non-fatal): Tasks 3, 4, 5 ✓
333
+ - Edge case — PATCH first, POST on 404: Task 3 ✓
334
+ - Edge case — `vars.PROJECT_ID` unset resolves to `''`, guard works: Task 1 ✓
335
+ - No `{{PROJECT_ID}}` in any YAML after install: Tasks 1 + 2 ✓
336
+ - All other ID substitutions preserved: Task 2 Step 2 ✓
@@ -0,0 +1,114 @@
1
+ # Design: Set PROJECT_ID as Actions Variable During Install
2
+
3
+ **Issue:** #179
4
+ **Date:** 2026-06-05
5
+ **Status:** Approved
6
+
7
+ ## Problem
8
+
9
+ The `create-agentic-pdlc` installer embeds the GitHub Project ID (`PVT_xxx`) directly into workflow YAML files by replacing `{{PROJECT_ID}}` placeholders during `scaffoldFullTemplates`. This means new repo installs get workflows with a hardcoded value in source-controlled files — brittle, hard to rotate, and inconsistent with how GitHub recommends storing non-secret configuration.
10
+
11
+ The fix: set `vars.PROJECT_ID` as a GitHub Actions Variable via the REST API during install, and have workflow templates source it from `vars` at runtime.
12
+
13
+ ## Approach: env block sourced from `vars`
14
+
15
+ Keep the `PROJECT_ID:` entry in the workflow-level `env` block — just change its value from a hardcoded placeholder to `${{ vars.PROJECT_ID }}`. All `process.env.PROJECT_ID` references inside `actions/github-script` bodies remain unchanged. Minimal diff, maximum behavioral parity.
16
+
17
+ Rejected alternatives:
18
+ - **Inline `vars` everywhere**: larger diff, all script bodies change, no benefit.
19
+ - **Set vars + keep YAML hardcode**: adds complexity, defeats the goal.
20
+
21
+ ## Changes
22
+
23
+ ### 1. Templates — 3 workflow files
24
+
25
+ Files: `templates/.github/workflows/project-automation.yml`, `add-to-board.yml`, `agent-trigger.yml`
26
+
27
+ Every occurrence of:
28
+
29
+ | Before | After |
30
+ |---|---|
31
+ | `PROJECT_ID: "{{PROJECT_ID}}"` | `PROJECT_ID: ${{ vars.PROJECT_ID }}` |
32
+ | `env.PROJECT_ID != '{{PROJECT_ID}}'` | `env.PROJECT_ID != ''` |
33
+
34
+ Guard correctness: when `vars.PROJECT_ID` is unset, `${{ vars.PROJECT_ID }}` resolves to `''` at workflow startup → `env.PROJECT_ID != ''` suppresses execution cleanly. Verified: `vars.*` in workflow-level `env` block resolves before jobs run.
35
+
36
+ `pdlc.md` keeps `{{PROJECT_ID}}` substitution — it is a documentation file, not a YAML workflow.
37
+
38
+ ### 2. `bin/cli.js` — new helper `setActionsVariable`
39
+
40
+ ```javascript
41
+ function setActionsVariable(repo, name, value) {
42
+ try {
43
+ execFileSync('gh', ['api', `repos/${repo}/actions/variables/${name}`,
44
+ '--method', 'PATCH', '-f', `name=${name}`, '-f', `value=${value}`],
45
+ { stdio: ['ignore', 'pipe', 'pipe'] });
46
+ } catch (err) {
47
+ const msg = err.stderr?.toString() || '';
48
+ if (msg.includes('404') || msg.includes('Not Found')) {
49
+ execFileSync('gh', ['api', `repos/${repo}/actions/variables`,
50
+ '--method', 'POST', '-f', `name=${name}`, '-f', `value=${value}`],
51
+ { stdio: ['ignore', 'pipe', 'pipe'] });
52
+ } else {
53
+ throw err; // 403 bubbles up → caller emits user-visible warning
54
+ }
55
+ }
56
+ }
57
+ ```
58
+
59
+ Uses `gh api` via `execFileSync` — consistent with all other API calls in `cli.js`. PATCH on existing variable, POST on 404, throws on 403 so the caller can warn the user.
60
+
61
+ **Token scope requirement:** fine-grained PAT needs `variables:write`; classic PAT needs `repo` scope. `GITHUB_TOKEN` (workflow-issued) will return 403 — cannot set repo variables. The installer already calls `gh auth token` for `PROJECT_PAT`; the same authenticated session is used here.
62
+
63
+ ### 3. `bin/cli.js` — `scaffoldFullTemplates` (line 345)
64
+
65
+ Remove the single line that substitutes `{{PROJECT_ID}}` in `project-automation.yml`:
66
+
67
+ ```javascript
68
+ // REMOVE this line:
69
+ if (projectId) wfContent = wfContent.replace(/\{\{PROJECT_ID\}\}/g, () => projectId);
70
+ ```
71
+
72
+ All other substitutions on lines 346–355 (`STATUS_FIELD_ID`, `ID_BRAINSTORMING`, `ID_DETAILING`, etc.) are preserved unchanged.
73
+
74
+ ### 4. Create flow — call site
75
+
76
+ Inside the existing `if (projectId)` block (after `PROJECT_PAT` secret is set, ~line 541):
77
+
78
+ ```javascript
79
+ try {
80
+ setActionsVariable(repo, 'PROJECT_ID', projectId);
81
+ console.log(`${green}✅ vars.PROJECT_ID set as Actions Variable.${reset}`);
82
+ } catch (_) {
83
+ console.log(`${yellow}⚠️ Could not set vars.PROJECT_ID — token may lack variables:write scope.\n Set it manually: repo Settings → Secrets and variables → Variables → PROJECT_ID = ${projectId}${reset}`);
84
+ }
85
+ ```
86
+
87
+ Non-fatal. Install continues regardless.
88
+
89
+ ### 5. `--update` flow — same call site
90
+
91
+ The `--update` command is a **lite → full upgrade** — it exits early if the profile is already `full` (line 865). For lite → full, a new board is created via `createProjectV2` (line 920), producing a fresh `projectId`. Call `setActionsVariable` in the same `if (projectId)` block after board creation. Identical error handling to the create flow.
92
+
93
+ No "find existing project" query is needed. `projectId` is always available from the mutation result in both flows.
94
+
95
+ ## Acceptance Criteria
96
+
97
+ - `npx create-agentic-pdlc` → `vars.PROJECT_ID` set as GitHub Actions Variable on the target repo
98
+ - No `PVT_xxx` value appears in any installed YAML file
99
+ - `resolve-ids` job resolves column IDs without any YAML modification
100
+ - `--update` (lite → full) → `vars.PROJECT_ID` set with the newly created board ID
101
+ - If token lacks scope → installer prints actionable warning with manual steps; install does not abort
102
+
103
+ ## Out of Scope
104
+
105
+ - Migrating existing full installs (board already set up, YAML already hardcoded)
106
+ - Changing how `PROJECT_ID` is resolved during install (GraphQL flow unchanged)
107
+ - `--update` on an already-full install (exits early before any board logic runs)
108
+
109
+ ## Files to Modify
110
+
111
+ - `templates/.github/workflows/project-automation.yml`
112
+ - `templates/.github/workflows/add-to-board.yml`
113
+ - `templates/.github/workflows/agent-trigger.yml`
114
+ - `bin/cli.js` — `setActionsVariable` helper, `scaffoldFullTemplates` line 345, create flow call site, update flow call site
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-agentic-pdlc",
3
- "version": "3.0.0",
3
+ "version": "3.1.1",
4
4
  "description": "Agentic PDLC Framework - Conversational setup for your AI coding assistants",
5
5
  "type": "commonjs",
6
6
  "bin": {
@@ -0,0 +1,20 @@
1
+ // Single source of truth for label → board column classification.
2
+ // Used by the event dispatcher (project-automation.yml) and the reconciliation cron (board-reconciliation.yml).
3
+ // pr:* beats stage:* — a live PR is higher-confidence signal than a stage label that may not have been cleaned up.
4
+ const LABEL_PRIORITY = [
5
+ { label: 'pr:in-review', column: 'code_review_pr' },
6
+ { label: 'pr:approved', column: 'code_review_pr' },
7
+ { label: 'stage:development', column: 'development' },
8
+ { label: 'stage:approval', column: 'approval' },
9
+ { label: 'stage:detailing', column: 'detailing' },
10
+ { label: 'stage:brainstorming', column: 'brainstorming' },
11
+ ];
12
+
13
+ function classifyItem(labelNames) {
14
+ for (const { label, column } of LABEL_PRIORITY) {
15
+ if (labelNames.includes(label)) return column;
16
+ }
17
+ return null;
18
+ }
19
+
20
+ module.exports = { classifyItem, LABEL_PRIORITY };
@@ -5,7 +5,7 @@ on:
5
5
  types: [opened]
6
6
 
7
7
  env:
8
- PROJECT_ID: "{{PROJECT_ID}}"
8
+ PROJECT_ID: ${{ vars.PROJECT_ID }}
9
9
  STATUS_FIELD_ID: "{{STATUS_FIELD_ID}}"
10
10
  STATUS_IDEA: "{{ID_IDEA}}"
11
11
 
@@ -17,7 +17,7 @@ jobs:
17
17
  PROJECT_TOKEN: ${{ secrets.PROJECT_TOKEN }}
18
18
  steps:
19
19
  - name: Add issue to project board
20
- if: ${{ env.PROJECT_TOKEN != '' && env.PROJECT_ID != '{{PROJECT_ID}}' }}
20
+ if: ${{ env.PROJECT_TOKEN != '' && env.PROJECT_ID != '' }}
21
21
  uses: actions/github-script@v8
22
22
  with:
23
23
  github-token: ${{ env.PROJECT_TOKEN }}
@@ -18,7 +18,7 @@ jobs:
18
18
  contents: read
19
19
  env:
20
20
  PROJECT_TOKEN: ${{ secrets.PROJECT_TOKEN }}
21
- PROJECT_ID: "{{PROJECT_ID}}"
21
+ PROJECT_ID: ${{ vars.PROJECT_ID }}
22
22
  STATUS_FIELD_ID: "{{STATUS_FIELD_ID}}"
23
23
  STATUS_DEVELOPMENT: "{{ID_DEVELOPMENT}}"
24
24
  steps:
@@ -62,7 +62,7 @@ jobs:
62
62
  });
63
63
 
64
64
  - name: Move board card to Development
65
- if: ${{ env.PROJECT_TOKEN != '' && env.PROJECT_ID != '{{PROJECT_ID}}' }}
65
+ if: ${{ env.PROJECT_TOKEN != '' && env.PROJECT_ID != '' }}
66
66
  continue-on-error: true
67
67
  uses: actions/github-script@v8
68
68
  with:
@@ -6,7 +6,7 @@ on:
6
6
  - cron: '0 8 * * 1' # Every Monday at 8am
7
7
 
8
8
  env:
9
- PROJECT_ID: "{{PROJECT_ID}}"
9
+ PROJECT_ID: ${{ vars.PROJECT_ID }}
10
10
  STATUS_FIELD_ID: "{{STATUS_FIELD_ID}}"
11
11
  STATUS_BRAINSTORMING: "{{ID_BRAINSTORMING}}"
12
12
  STATUS_DETAILING: "{{ID_DETAILING}}"
@@ -26,7 +26,7 @@ jobs:
26
26
  issues: write
27
27
  steps:
28
28
  - name: Validate Board Configuration
29
- if: ${{ env.PROJECT_TOKEN != '' && env.PROJECT_ID != '{{PROJECT_ID}}' }}
29
+ if: ${{ env.PROJECT_TOKEN != '' && env.PROJECT_ID != '' }}
30
30
  uses: actions/github-script@v8
31
31
  with:
32
32
  github-token: ${{ env.PROJECT_TOKEN }}
@@ -9,7 +9,7 @@ on:
9
9
  types: [labeled, edited, closed]
10
10
 
11
11
  env:
12
- PROJECT_ID: "{{PROJECT_ID}}"
12
+ PROJECT_ID: ${{ vars.PROJECT_ID }}
13
13
  STATUS_FIELD_ID: "{{STATUS_FIELD_ID}}"
14
14
  STATUS_IDEA: "{{ID_IDEA}}"
15
15
  STATUS_BRAINSTORMING: "{{ID_BRAINSTORMING}}"
@@ -30,7 +30,7 @@ jobs:
30
30
  PROJECT_TOKEN: ${{ secrets.PROJECT_TOKEN }}
31
31
  steps:
32
32
  - name: Detect Label and Move Issue
33
- if: ${{ env.PROJECT_TOKEN != '' && env.PROJECT_ID != '{{PROJECT_ID}}' }}
33
+ if: ${{ env.PROJECT_TOKEN != '' && env.PROJECT_ID != '' }}
34
34
  uses: actions/github-script@v8
35
35
  with:
36
36
  github-token: ${{ env.PROJECT_TOKEN }}
@@ -91,7 +91,7 @@ jobs:
91
91
  PROJECT_TOKEN: ${{ secrets.PROJECT_TOKEN }}
92
92
  steps:
93
93
  - name: Check spec markers and swap labels
94
- if: ${{ env.PROJECT_TOKEN != '' && env.PROJECT_ID != '{{PROJECT_ID}}' }}
94
+ if: ${{ env.PROJECT_TOKEN != '' && env.PROJECT_ID != '' }}
95
95
  uses: actions/github-script@v8
96
96
  with:
97
97
  github-token: ${{ env.PROJECT_TOKEN }}
@@ -149,7 +149,7 @@ jobs:
149
149
  # runs-on: ubuntu-latest
150
150
  # steps:
151
151
  # - name: Move issue to Idea
152
- # if: ${{ env.PROJECT_TOKEN != '' && env.PROJECT_ID != '{{PROJECT_ID}}' }}
152
+ # if: ${{ env.PROJECT_TOKEN != '' && env.PROJECT_ID != '' }}
153
153
  # uses: actions/github-script@v8
154
154
  # with:
155
155
  # github-token: ${{ env.PROJECT_TOKEN }}
@@ -179,7 +179,7 @@ jobs:
179
179
  PROJECT_TOKEN: ${{ secrets.PROJECT_TOKEN }}
180
180
  steps:
181
181
  - name: Move linked issue to Testing
182
- if: ${{ env.PROJECT_TOKEN != '' && env.PROJECT_ID != '{{PROJECT_ID}}' }}
182
+ if: ${{ env.PROJECT_TOKEN != '' && env.PROJECT_ID != '' }}
183
183
  uses: actions/github-script@v8
184
184
  with:
185
185
  github-token: ${{ env.PROJECT_TOKEN }}
@@ -233,7 +233,7 @@ jobs:
233
233
  PROJECT_TOKEN: ${{ secrets.PROJECT_TOKEN }}
234
234
  steps:
235
235
  - name: Move linked issue to Code Review / PR
236
- if: ${{ env.PROJECT_TOKEN != '' && env.PROJECT_ID != '{{PROJECT_ID}}' }}
236
+ if: ${{ env.PROJECT_TOKEN != '' && env.PROJECT_ID != '' }}
237
237
  uses: actions/github-script@v8
238
238
  with:
239
239
  github-token: ${{ env.PROJECT_TOKEN }}
@@ -281,7 +281,7 @@ jobs:
281
281
  PROJECT_TOKEN: ${{ secrets.PROJECT_TOKEN }}
282
282
  steps:
283
283
  - name: Swap PR labels
284
- if: ${{ env.PROJECT_TOKEN != '' && env.PROJECT_ID != '{{PROJECT_ID}}' }}
284
+ if: ${{ env.PROJECT_TOKEN != '' && env.PROJECT_ID != '' }}
285
285
  uses: actions/github-script@v8
286
286
  with:
287
287
  github-token: ${{ env.PROJECT_TOKEN }}
@@ -300,7 +300,7 @@ jobs:
300
300
  PROJECT_TOKEN: ${{ secrets.PROJECT_TOKEN }}
301
301
  steps:
302
302
  - name: Move issue to Production
303
- if: ${{ env.PROJECT_TOKEN != '' && env.PROJECT_ID != '{{PROJECT_ID}}' }}
303
+ if: ${{ env.PROJECT_TOKEN != '' && env.PROJECT_ID != '' }}
304
304
  uses: actions/github-script@v8
305
305
  with:
306
306
  github-token: ${{ env.PROJECT_TOKEN }}
@@ -359,3 +359,42 @@ jobs:
359
359
  await github.rest.issues.removeLabel({ owner, repo, issue_number, name: label }).catch(() => {});
360
360
  }
361
361
  console.log(`Issue #${issue_number} labels cleaned up`);
362
+
363
+ move-card-on-issue-close:
364
+ name: Closed issue → Archive from board
365
+ if: github.event_name == 'issues' && github.event.action == 'closed'
366
+ runs-on: ubuntu-latest
367
+ env:
368
+ PROJECT_TOKEN: ${{ secrets.PROJECT_TOKEN }}
369
+ steps:
370
+ - name: Archive board card
371
+ if: ${{ env.PROJECT_TOKEN != '' && env.PROJECT_ID != '' }}
372
+ uses: actions/github-script@v8
373
+ with:
374
+ github-token: ${{ env.PROJECT_TOKEN }}
375
+ script: |
376
+ const nodeId = context.payload.issue.node_id;
377
+ let itemId;
378
+ try {
379
+ const added = await github.graphql(`
380
+ mutation($p: ID!, $c: ID!) {
381
+ addProjectV2ItemById(input: {projectId: $p, contentId: $c}) { item { id } }
382
+ }`, { p: process.env.PROJECT_ID, c: nodeId });
383
+ itemId = added.addProjectV2ItemById.item.id;
384
+ if (!itemId) {
385
+ console.log(`Could not extract itemId from add response`);
386
+ return;
387
+ }
388
+ } catch (e) {
389
+ console.log(`Could not add issue to project: ${e.message}`);
390
+ return;
391
+ }
392
+ try {
393
+ await github.graphql(`
394
+ mutation($p: ID!, $i: ID!) {
395
+ archiveProjectV2Item(input: {projectId: $p, itemId: $i}) { item { id } }
396
+ }`, { p: process.env.PROJECT_ID, i: itemId });
397
+ console.log(`Issue #${context.payload.issue.number} archived from board`);
398
+ } catch (e) {
399
+ console.log(`Could not archive item: ${e.message}`);
400
+ }