FlywheelIC 0.4.5__tar.gz
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.
- flywheelic-0.4.5/LICENSE +21 -0
- flywheelic-0.4.5/PKG-INFO +485 -0
- flywheelic-0.4.5/README.md +461 -0
- flywheelic-0.4.5/pyproject.toml +58 -0
- flywheelic-0.4.5/setup.cfg +4 -0
- flywheelic-0.4.5/src/FlywheelIC.egg-info/PKG-INFO +485 -0
- flywheelic-0.4.5/src/FlywheelIC.egg-info/SOURCES.txt +37 -0
- flywheelic-0.4.5/src/FlywheelIC.egg-info/dependency_links.txt +1 -0
- flywheelic-0.4.5/src/FlywheelIC.egg-info/requires.txt +14 -0
- flywheelic-0.4.5/src/FlywheelIC.egg-info/top_level.txt +1 -0
- flywheelic-0.4.5/src/methodology_framework/__init__.py +23 -0
- flywheelic-0.4.5/src/methodology_framework/__main__.py +28 -0
- flywheelic-0.4.5/src/methodology_framework/bootstrap_jira.py +702 -0
- flywheelic-0.4.5/src/methodology_framework/build_playbook.py +193 -0
- flywheelic-0.4.5/src/methodology_framework/jira_discovery.py +762 -0
- flywheelic-0.4.5/src/methodology_framework/jira_shapes/__init__.py +0 -0
- flywheelic-0.4.5/src/methodology_framework/jira_shapes/automation_rules.yaml +85 -0
- flywheelic-0.4.5/src/methodology_framework/jira_shapes/custom_fields.yaml +45 -0
- flywheelic-0.4.5/src/methodology_framework/jira_shapes/workflow.yaml +143 -0
- flywheelic-0.4.5/src/methodology_framework/playbooks/bindings/greet.yaml +13 -0
- flywheelic-0.4.5/src/methodology_framework/playbooks/bindings/meth.yaml +11 -0
- flywheelic-0.4.5/src/methodology_framework/playbooks/bindings/scrum.yaml +12 -0
- flywheelic-0.4.5/src/methodology_framework/playbooks/router-base.body.md +236 -0
- flywheelic-0.4.5/src/methodology_framework/populate_acus.py +358 -0
- flywheelic-0.4.5/src/methodology_framework/register_playbook_with_devin.py +341 -0
- flywheelic-0.4.5/src/methodology_framework/specs/devin-story-format.md +536 -0
- flywheelic-0.4.5/src/methodology_framework/sync_stories_to_jira.py +1352 -0
- flywheelic-0.4.5/src/methodology_framework/templates/github_workflows/populate-story-acus-caller.yml +29 -0
- flywheelic-0.4.5/src/methodology_framework/templates/story.md +152 -0
- flywheelic-0.4.5/tests/test_bootstrap_jira.py +719 -0
- flywheelic-0.4.5/tests/test_build_playbook.py +119 -0
- flywheelic-0.4.5/tests/test_jira_discovery.py +477 -0
- flywheelic-0.4.5/tests/test_jira_discovery_custom_fields_fallback.py +218 -0
- flywheelic-0.4.5/tests/test_jira_discovery_transitions.py +67 -0
- flywheelic-0.4.5/tests/test_populate_acus.py +498 -0
- flywheelic-0.4.5/tests/test_populate_acus_integration.py +97 -0
- flywheelic-0.4.5/tests/test_register_playbook_with_devin.py +439 -0
- flywheelic-0.4.5/tests/test_sync_stories_to_jira.py +1707 -0
- flywheelic-0.4.5/tests/test_workflows.py +191 -0
flywheelic-0.4.5/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 whiteout59
|
|
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.
|
|
@@ -0,0 +1,485 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: FlywheelIC
|
|
3
|
+
Version: 0.4.5
|
|
4
|
+
Summary: Portable process tooling for agent-driven delivery — scripts, playbooks, templates, and specs.
|
|
5
|
+
Author: whiteout59
|
|
6
|
+
License-Expression: Apache-2.0
|
|
7
|
+
Requires-Python: >=3.12
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Requires-Dist: requests<3,>=2.31
|
|
11
|
+
Requires-Dist: python-frontmatter<2,>=1.1
|
|
12
|
+
Requires-Dist: pyyaml<7,>=6.0
|
|
13
|
+
Requires-Dist: colorama<1,>=0.4
|
|
14
|
+
Provides-Extra: dev
|
|
15
|
+
Requires-Dist: pytest<9,>=8.0; extra == "dev"
|
|
16
|
+
Requires-Dist: requests-mock<2,>=1.12; extra == "dev"
|
|
17
|
+
Requires-Dist: ruff==0.15.14; extra == "dev"
|
|
18
|
+
Requires-Dist: mypy<2,>=1.10; extra == "dev"
|
|
19
|
+
Requires-Dist: types-requests<3,>=2.31; extra == "dev"
|
|
20
|
+
Requires-Dist: types-PyYAML<7,>=6.0; extra == "dev"
|
|
21
|
+
Requires-Dist: types-colorama<1,>=0.4; extra == "dev"
|
|
22
|
+
Requires-Dist: vcrpy<9,>=7.0; extra == "dev"
|
|
23
|
+
Dynamic: license-file
|
|
24
|
+
|
|
25
|
+
[](https://pypi.org/project/FlywheelIC/)
|
|
26
|
+
# methodology-framework
|
|
27
|
+
|
|
28
|
+
Portable process tooling for agent-driven delivery -- scripts, playbooks, templates, and specs.
|
|
29
|
+
|
|
30
|
+
## Installation
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pip install FlywheelIC
|
|
34
|
+
# or, from a source checkout:
|
|
35
|
+
pip install -e .
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Package contents
|
|
39
|
+
|
|
40
|
+
- `methodology_framework.sync_stories_to_jira` -- one-way repo-to-Jira story sync
|
|
41
|
+
- `methodology_framework.build_playbook` -- build a concrete playbook from a parameterized body + bindings
|
|
42
|
+
- `methodology_framework.register_playbook_with_devin` -- register a rendered playbook with Devin's API
|
|
43
|
+
- `methodology_framework/playbooks/` -- parameterized playbook bodies (package data)
|
|
44
|
+
- `methodology_framework/templates/` -- story and process templates (package data)
|
|
45
|
+
- `methodology_framework/specs/` -- format specs (package data)
|
|
46
|
+
- `methodology_framework.bootstrap_jira` -- verify a Jira project matches the canonical shape (read-only spec-compliance checker)
|
|
47
|
+
- `methodology_framework/jira_shapes/` -- canonical Jira shape definitions (YAML, package data)
|
|
48
|
+
|
|
49
|
+
## Jira Bootstrap (Verify Mode)
|
|
50
|
+
|
|
51
|
+
The CLI checks whether a Jira project matches the methodology-framework's
|
|
52
|
+
canonical shape and reports gaps. It does **not** provision — operators apply
|
|
53
|
+
gaps via the Jira UI. This read-only design lets the tool run in CI as a gate.
|
|
54
|
+
|
|
55
|
+
> **v0.2.0 breaking change:** `--apply` and `--dry-run` were removed.
|
|
56
|
+
> The CLI is now a spec-compliance verifier. See `operator-verify-runbook.md`
|
|
57
|
+
> for the operator workflow.
|
|
58
|
+
|
|
59
|
+
### Prerequisites
|
|
60
|
+
|
|
61
|
+
- Python 3.12+
|
|
62
|
+
- `pip install FlywheelIC` (or `pip install -e .` from source)
|
|
63
|
+
- For `--verify` mode: `JIRA_ADMIN_TOKEN` environment variable set to a Jira
|
|
64
|
+
API token (admin scope is **not** required; a regular user token with read
|
|
65
|
+
access is sufficient), and `JIRA_USER_EMAIL` set to the email of the
|
|
66
|
+
Atlassian account that owns the token
|
|
67
|
+
|
|
68
|
+
### Usage
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
# Verify — query Jira and report spec-compliance gaps (read-only)
|
|
72
|
+
export JIRA_ADMIN_TOKEN="<your-token>"
|
|
73
|
+
export JIRA_USER_EMAIL="<your-email>"
|
|
74
|
+
python -m methodology_framework bootstrap-jira \
|
|
75
|
+
--project-key=MYPROJ \
|
|
76
|
+
--jira-host=myorg.atlassian.net \
|
|
77
|
+
--verify
|
|
78
|
+
|
|
79
|
+
# JSON output (stable schema — safe for CI parsing)
|
|
80
|
+
python -m methodology_framework bootstrap-jira \
|
|
81
|
+
--project-key=MYPROJ \
|
|
82
|
+
--jira-host=myorg.atlassian.net \
|
|
83
|
+
--verify --output=json
|
|
84
|
+
|
|
85
|
+
# Markdown output
|
|
86
|
+
python -m methodology_framework bootstrap-jira \
|
|
87
|
+
--project-key=MYPROJ \
|
|
88
|
+
--jira-host=myorg.atlassian.net \
|
|
89
|
+
--verify --output=markdown
|
|
90
|
+
|
|
91
|
+
# Export — emit an importable config bundle (YAML); no API calls
|
|
92
|
+
python -m methodology_framework bootstrap-jira \
|
|
93
|
+
--project-key=MYPROJ \
|
|
94
|
+
--jira-host=myorg.atlassian.net \
|
|
95
|
+
--export /tmp/jira-bundle.yaml
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Exit codes
|
|
99
|
+
|
|
100
|
+
| Code | Meaning |
|
|
101
|
+
|------|---------|
|
|
102
|
+
| `0` | All spec items verified OK |
|
|
103
|
+
| `1` | One or more items missing (gaps found) |
|
|
104
|
+
| `2` | One or more items unverifiable (API limitation; operator must inspect manually) |
|
|
105
|
+
| `3` | Error (connectivity, auth, or runtime failure) |
|
|
106
|
+
|
|
107
|
+
### Flags
|
|
108
|
+
|
|
109
|
+
| Flag | Required | Description |
|
|
110
|
+
|------|----------|-------------|
|
|
111
|
+
| `--project-key` | Yes | Jira project key (e.g. `SCRUM`). Substituted for `{{PROJECT_KEY}}` in shape defs. |
|
|
112
|
+
| `--jira-host` | Yes | Jira Cloud host (e.g. `myorg.atlassian.net`). No `https://` prefix. |
|
|
113
|
+
| `--verify` | No | Query Jira and report spec-compliance gaps (read-only). |
|
|
114
|
+
| `--export <path>` | No | Emit importable config bundle at `<path>`. No API calls. |
|
|
115
|
+
| `--output` | No | Output format for `--verify`: `text` (default), `json`, `markdown`. |
|
|
116
|
+
|
|
117
|
+
### Environment variables
|
|
118
|
+
|
|
119
|
+
| Variable | When needed | Description |
|
|
120
|
+
|----------|-------------|-------------|
|
|
121
|
+
| `JIRA_ADMIN_TOKEN` | `--verify` mode | Jira API token. Admin scope is **not** required; a regular user token with read access is sufficient. **Never** accepted as a CLI flag. |
|
|
122
|
+
| `JIRA_USER_EMAIL` | `--verify` mode | Email of the Atlassian account that owns the API token. Used with the token for HTTP Basic auth. **Never** accepted as a CLI flag. |
|
|
123
|
+
|
|
124
|
+
### JSON output schema (v0.2.0)
|
|
125
|
+
|
|
126
|
+
```json
|
|
127
|
+
{
|
|
128
|
+
"spec_version": "0.2.0",
|
|
129
|
+
"project_key": "METH",
|
|
130
|
+
"verified_at": "2025-05-31T00:00:00Z",
|
|
131
|
+
"items": [
|
|
132
|
+
{"resource_type": "status", "name": "To Do", "result": "ok", "details": null},
|
|
133
|
+
{"resource_type": "custom_field", "name": "Story File", "result": "missing", "details": "Operator must create via Settings → Custom fields → Create"}
|
|
134
|
+
],
|
|
135
|
+
"summary": {"total": 14, "ok": 12, "missing": 1, "unverifiable": 1},
|
|
136
|
+
"exit_code": 1
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Any breaking change to this schema requires a MAJOR version bump.
|
|
141
|
+
|
|
142
|
+
### Shape definitions
|
|
143
|
+
|
|
144
|
+
The three canonical shape files shipped as package data:
|
|
145
|
+
|
|
146
|
+
- `jira_shapes/workflow.yaml` — workflow statuses (To Do, Ready for AI agent,
|
|
147
|
+
In Progress, In Review, Waiting, Blocked, Done, Won't do) and transitions
|
|
148
|
+
with actor permissions
|
|
149
|
+
- `jira_shapes/custom_fields.yaml` — Story File (URL), Requirement IDs
|
|
150
|
+
(labels), Agent Estimate (number)
|
|
151
|
+
- `jira_shapes/automation_rules.yaml` — PR-title-key auto-transition,
|
|
152
|
+
dependency resolution, BLOCKED protection
|
|
153
|
+
|
|
154
|
+
For full methodology context see the methodology requirements doc § 4.13.3
|
|
155
|
+
("Jira Bootstrap CLI").
|
|
156
|
+
|
|
157
|
+
## Adopter Integration
|
|
158
|
+
|
|
159
|
+
Adopting projects consume the methodology framework's CI pipelines via
|
|
160
|
+
**reusable GitHub Actions workflows**. Instead of copying workflow YAML
|
|
161
|
+
into each repo, adopters write a thin caller that references the
|
|
162
|
+
framework's workflows by SemVer tag.
|
|
163
|
+
|
|
164
|
+
> **Version pinning requirement:** adopters MUST pin to a specific tag
|
|
165
|
+
> (`@v0.X.Y`), never `@main`. The framework version must be explicit
|
|
166
|
+
> in the caller so updates are deliberate, not silent. This matches the
|
|
167
|
+
> version-pinning model per methodology requirements § 4.13.
|
|
168
|
+
|
|
169
|
+
### Required inputs
|
|
170
|
+
|
|
171
|
+
| Input | Required | Default | Description |
|
|
172
|
+
|-------|----------|---------|-------------|
|
|
173
|
+
| `project-key` | **Yes** | — | Jira project key (e.g. `METH`, `SCRUM`). No default — sync errors if absent. |
|
|
174
|
+
| `jira-base-url` | **Yes** | — | Jira Cloud base URL (e.g. `https://icpipeline.atlassian.net`). |
|
|
175
|
+
| `jira-user-email` | **Yes** | — | Jira service account email for HTTP Basic auth. |
|
|
176
|
+
| `stories-dir` | No | `docs/stories` | Relative path to the stories directory. |
|
|
177
|
+
| `phase-regex` | No | `^phase\d+$` | Regex for phase directory matching. Empty string (`""`) disables phase labeling entirely. |
|
|
178
|
+
| `repo-url` | No | *(auto)* | Override repo URL. Normally derived from `GITHUB_SERVER_URL` + `GITHUB_REPOSITORY` (GHA-native). |
|
|
179
|
+
|
|
180
|
+
### Per-tenant discovery
|
|
181
|
+
|
|
182
|
+
Sync discovers all tenant-specific Jira IDs at runtime via the REST API,
|
|
183
|
+
using canonical **names** as lookup keys. The names come from the framework's
|
|
184
|
+
shape definitions (`jira_shapes/*.yaml`) — the same spec the verifier
|
|
185
|
+
(`bootstrap-jira --verify`) enforces.
|
|
186
|
+
|
|
187
|
+
**Custom fields** (discovered via `GET /rest/api/3/field`):
|
|
188
|
+
|
|
189
|
+
| Name | Purpose |
|
|
190
|
+
|------|---------|
|
|
191
|
+
| `Requirement IDs` | Multi-value field for REQ-id traceability |
|
|
192
|
+
| `Story File` | URL to the canonical story `.md` file in the repo |
|
|
193
|
+
| `Agent Estimate` | Numeric estimate in agent-hours |
|
|
194
|
+
|
|
195
|
+
**Statuses** (discovered via `GET /rest/api/3/project/{key}/statuses`):
|
|
196
|
+
|
|
197
|
+
| Name | Category |
|
|
198
|
+
|------|----------|
|
|
199
|
+
| `To Do` | TODO |
|
|
200
|
+
| `Ready for AI agent` | TODO |
|
|
201
|
+
| `In Progress` | IN_PROGRESS |
|
|
202
|
+
| `In Review` | IN_PROGRESS |
|
|
203
|
+
| `Waiting` | IN_PROGRESS |
|
|
204
|
+
| `Blocked` | IN_PROGRESS |
|
|
205
|
+
| `Done` | DONE |
|
|
206
|
+
| `Won't do` | DONE |
|
|
207
|
+
|
|
208
|
+
**Transitions**: Sourced from the workflow spec (`jira_shapes/workflow.yaml`).
|
|
209
|
+
|
|
210
|
+
**Link types** (discovered via `GET /rest/api/3/issueLinkType`):
|
|
211
|
+
`Blocks` (inward: "is blocked by").
|
|
212
|
+
|
|
213
|
+
**Issue types** (discovered via `GET /rest/api/3/issuetype`):
|
|
214
|
+
`Story`, `Sub-task`.
|
|
215
|
+
|
|
216
|
+
If any required name is missing from the target project, sync fails fast:
|
|
217
|
+
```
|
|
218
|
+
ERROR: Required custom field 'Story File' not found in Jira project METH;
|
|
219
|
+
run 'methodology-framework bootstrap-jira --verify --project-key=METH' to identify the gap.
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### Story sync workflow
|
|
223
|
+
|
|
224
|
+
Syncs story `.md` files from the adopter's repo to Jira. Create
|
|
225
|
+
`.github/workflows/sync-stories.yml` in the adopter repo:
|
|
226
|
+
|
|
227
|
+
```yaml
|
|
228
|
+
name: Sync stories to Jira
|
|
229
|
+
|
|
230
|
+
on:
|
|
231
|
+
push:
|
|
232
|
+
branches: [main]
|
|
233
|
+
paths:
|
|
234
|
+
- "docs/stories/**/*.md"
|
|
235
|
+
workflow_dispatch:
|
|
236
|
+
inputs:
|
|
237
|
+
mode:
|
|
238
|
+
description: "Sync mode"
|
|
239
|
+
required: true
|
|
240
|
+
default: "since-ref"
|
|
241
|
+
type: choice
|
|
242
|
+
options:
|
|
243
|
+
- since-ref
|
|
244
|
+
- all
|
|
245
|
+
|
|
246
|
+
jobs:
|
|
247
|
+
sync:
|
|
248
|
+
permissions:
|
|
249
|
+
contents: write
|
|
250
|
+
pull-requests: read
|
|
251
|
+
uses: whiteout59/methodology-framework/.github/workflows/sync.yml@v0.3.0
|
|
252
|
+
with:
|
|
253
|
+
project_key: MYPROJ
|
|
254
|
+
mode: ${{ github.event.inputs.mode || 'since-ref' }}
|
|
255
|
+
jira_base_url: "https://myorg.atlassian.net"
|
|
256
|
+
jira_user_email: "devin-sync@myorg.atlassian.net"
|
|
257
|
+
# stories_dir: "docs/stories" # default
|
|
258
|
+
# phase_regex: "^phase\\d+$" # default; set "" to disable
|
|
259
|
+
secrets:
|
|
260
|
+
JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
The `mode` input defaults to `since-ref` for routine push-triggered
|
|
264
|
+
runs (preserves the 250-story scale guard). Pass `mode: all` explicitly
|
|
265
|
+
for nightly or `workflow_dispatch` full-corpus syncs.
|
|
266
|
+
|
|
267
|
+
> **Secrets forwarding:** the caller's `secrets:` block must explicitly
|
|
268
|
+
> map each secret the reusable workflow declares. Secrets are NOT
|
|
269
|
+
> inherited by default in reusable workflows. Using `secrets: inherit`
|
|
270
|
+
> works but is brittle — prefer explicit mapping.
|
|
271
|
+
|
|
272
|
+
#### Required caller permissions
|
|
273
|
+
|
|
274
|
+
The reusable sync workflow declares `permissions: contents: write`
|
|
275
|
+
internally (for `jira_key` + `agent_acus` writeback commits). GitHub
|
|
276
|
+
Actions requires the **caller** to grant at least the same permissions
|
|
277
|
+
at the job level — otherwise the workflow fails at the `uses:`
|
|
278
|
+
resolution step with:
|
|
279
|
+
|
|
280
|
+
```
|
|
281
|
+
The workflow is requesting 'contents: write', but is only allowed 'contents: read'.
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
Add a job-level `permissions:` block to your caller:
|
|
285
|
+
|
|
286
|
+
```yaml
|
|
287
|
+
jobs:
|
|
288
|
+
sync:
|
|
289
|
+
permissions:
|
|
290
|
+
contents: write
|
|
291
|
+
pull-requests: read
|
|
292
|
+
uses: whiteout59/methodology-framework/.github/workflows/sync.yml@v0.2.0
|
|
293
|
+
# ... with: / secrets: as above
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
> **Job-scoped, not workflow-scoped.** Keep `permissions:` on the job,
|
|
297
|
+
> not the top-level `on:` — if future jobs are added to the same file,
|
|
298
|
+
> they should not inherit write access they don't need.
|
|
299
|
+
|
|
300
|
+
### Playbook build + register workflow
|
|
301
|
+
|
|
302
|
+
Builds a concrete playbook from a parameterized body + bindings file,
|
|
303
|
+
then registers it with Devin's API. Create
|
|
304
|
+
`.github/workflows/build-and-register.yml` in the adopter repo:
|
|
305
|
+
|
|
306
|
+
```yaml
|
|
307
|
+
name: Build and register playbook
|
|
308
|
+
|
|
309
|
+
on:
|
|
310
|
+
push:
|
|
311
|
+
branches: [main]
|
|
312
|
+
paths:
|
|
313
|
+
- "methodology/playbooks/*.body.md"
|
|
314
|
+
- "docs/jira-pickup-config.md"
|
|
315
|
+
workflow_dispatch:
|
|
316
|
+
|
|
317
|
+
jobs:
|
|
318
|
+
build-and-register:
|
|
319
|
+
uses: whiteout59/methodology-framework/.github/workflows/build-and-register.yml@v0.1.0
|
|
320
|
+
with:
|
|
321
|
+
playbook_body_path: "methodology/playbooks/router-base.body.md"
|
|
322
|
+
bindings_path: "docs/jira-pickup-config.md"
|
|
323
|
+
secrets:
|
|
324
|
+
DEVIN_API_TOKEN: ${{ secrets.DEVIN_API_TOKEN }}
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
> **Nesting limit:** GitHub allows reusable workflow nesting up to
|
|
328
|
+
> 4 levels deep. If your repo wraps these workflows in another
|
|
329
|
+
> caller layer, verify the total nesting depth stays within limits.
|
|
330
|
+
|
|
331
|
+
## How this repo evolves
|
|
332
|
+
|
|
333
|
+
The methodology-framework repo itself uses the same sync workflow it
|
|
334
|
+
ships to adopters. Framework-side evolution stories (new specs, CLI
|
|
335
|
+
features, workflow improvements) are drafted as `.md` files under
|
|
336
|
+
`docs/stories/` and synced to the **METH** Jira project via
|
|
337
|
+
[`.github/workflows/sync-stories.yml`](.github/workflows/sync-stories.yml).
|
|
338
|
+
Adopter-side stories (product-specific work) file in each adopter's own
|
|
339
|
+
Jira project (e.g. SCRUM for centralized-pipeline-ui).
|
|
340
|
+
|
|
341
|
+
## PyPI Publishing
|
|
342
|
+
|
|
343
|
+
The package is published to PyPI automatically via OIDC Trusted Publishing when
|
|
344
|
+
`pyproject.toml` changes on `main`.
|
|
345
|
+
|
|
346
|
+
The `release.yml` workflow builds and publishes using
|
|
347
|
+
`pypa/gh-action-pypi-publish` in OIDC mode -- no `PYPI_API_TOKEN` secret
|
|
348
|
+
is needed. The job uses the `pypi` GitHub environment for protection rules.
|
|
349
|
+
|
|
350
|
+
**Operator setup (one-time):** bind the PyPI project `FlywheelIC`
|
|
351
|
+
to the GitHub repo `invoice-cloud/Flywheel`, workflow `release.yml`,
|
|
352
|
+
environment `pypi` at
|
|
353
|
+
[PyPI Trusted Publishers](https://pypi.org/manage/account/publishing/).
|
|
354
|
+
|
|
355
|
+
## Release lifecycle
|
|
356
|
+
|
|
357
|
+
Version bumps in `pyproject.toml` drive the entire release cycle
|
|
358
|
+
automatically. The operator's only decision surface is PR review.
|
|
359
|
+
|
|
360
|
+
### Primary flow
|
|
361
|
+
|
|
362
|
+
1. A PR bumps the `version` field in `pyproject.toml` (e.g. `"0.2.0"` → `"0.2.1"`).
|
|
363
|
+
2. Reviewer approves and merges the PR to `main`.
|
|
364
|
+
3. `release.yml` fires on the `pyproject.toml` change, extracts the version,
|
|
365
|
+
validates it as stable SemVer (`^[0-9]+\.[0-9]+\.[0-9]+$`), builds the
|
|
366
|
+
package, creates an annotated tag such as `v0.2.1`, pushes the tag, and
|
|
367
|
+
publishes to PyPI via OIDC Trusted Publishing.
|
|
368
|
+
|
|
369
|
+
**Operator surface = PR review only.** No manual `git tag` step, no manual
|
|
370
|
+
publish trigger for the normal path.
|
|
371
|
+
|
|
372
|
+
### Fallback (operator-pushed tag)
|
|
373
|
+
|
|
374
|
+
If the `pyproject.toml` push trigger does not fire, an operator can run
|
|
375
|
+
`release.yml` manually with the stable SemVer version input.
|
|
376
|
+
|
|
377
|
+
### Pre-release versions
|
|
378
|
+
|
|
379
|
+
The `release.yml` workflow only tags **stable** SemVer versions. Versions
|
|
380
|
+
containing `-rc`, `-beta`, `-alpha`, `.dev`, or `.pre` suffixes (or any
|
|
381
|
+
string not matching `^[0-9]+\.[0-9]+\.[0-9]+$`) are logged and skipped.
|
|
382
|
+
Pre-release publishing is out of scope; if needed, a separate workflow
|
|
383
|
+
would handle pre-release tag patterns.
|
|
384
|
+
|
|
385
|
+
## Cost tracking via `agent_acus`
|
|
386
|
+
|
|
387
|
+
The methodology's post-execution notes include an `agent_acus` field that
|
|
388
|
+
records per-story compute cost. Since agents cannot self-report ACU
|
|
389
|
+
consumption mid-session, a post-merge populator workflow backfills the
|
|
390
|
+
value from the Devin API after the session completes.
|
|
391
|
+
|
|
392
|
+
**Adopter wiring (3 steps):**
|
|
393
|
+
|
|
394
|
+
1. Copy the template caller into your repo:
|
|
395
|
+
```bash
|
|
396
|
+
cp "$(python -c "import methodology_framework; import pathlib; \
|
|
397
|
+
print(pathlib.Path(methodology_framework.__file__).parent / \
|
|
398
|
+
'templates/github_workflows/populate-story-acus-caller.yml')")" \
|
|
399
|
+
.github/workflows/populate-story-acus.yml
|
|
400
|
+
```
|
|
401
|
+
2. Set the `DEVIN_API_TOKEN` repo secret (Settings > Secrets and
|
|
402
|
+
variables > Actions > New repository secret).
|
|
403
|
+
3. Pin the framework version in the caller's `uses:` line.
|
|
404
|
+
|
|
405
|
+
The caller fires on merged PRs that touch story files, invokes the
|
|
406
|
+
reusable `populate-story-acus.yml` workflow, and opens a follow-on PR
|
|
407
|
+
with the populated ACU values.
|
|
408
|
+
|
|
409
|
+
## Contributing
|
|
410
|
+
|
|
411
|
+
Framework evolution stories — new specs, CLI features, workflow improvements —
|
|
412
|
+
are filed as Markdown story files under [`docs/stories/`](docs/stories/).
|
|
413
|
+
See the [directory README](docs/stories/README.md) for the filing convention
|
|
414
|
+
(slug-named directories, frontmatter shape, `depends_on` hygiene). Stories
|
|
415
|
+
sync to the **METH** Jira project via the same sync workflow the framework
|
|
416
|
+
ships to adopters.
|
|
417
|
+
|
|
418
|
+
For the canonical story template and format spec, see
|
|
419
|
+
[`whiteout59/centralized-pipeline-ui/methodology/templates/story.md`](https://github.com/whiteout59/centralized-pipeline-ui/blob/main/methodology/templates/story.md)
|
|
420
|
+
and
|
|
421
|
+
[`whiteout59/centralized-pipeline-ui/methodology/devin-story-format.md`](https://github.com/whiteout59/centralized-pipeline-ui/blob/main/methodology/devin-story-format.md).
|
|
422
|
+
|
|
423
|
+
### Jira Story Sync Auth
|
|
424
|
+
|
|
425
|
+
Story sync supports `JIRA_AUTH_MODE=basic` and `JIRA_AUTH_MODE=oauth`.
|
|
426
|
+
|
|
427
|
+
Basic mode is the default and requires `JIRA_BASE_URL`, `JIRA_USER_EMAIL`,
|
|
428
|
+
and `JIRA_API_TOKEN`.
|
|
429
|
+
|
|
430
|
+
OAuth mode requires `JIRA_CLOUD_ID` and either `JIRA_OAUTH_ACCESS_TOKEN` for
|
|
431
|
+
manual debugging or both `JIRA_OAUTH_CLIENT_ID` and `JIRA_OAUTH_CLIENT_SECRET`
|
|
432
|
+
for client-credentials token exchange via
|
|
433
|
+
`https://api.atlassian.com/oauth/token`. OAuth Jira REST calls use the
|
|
434
|
+
Atlassian API gateway base URL
|
|
435
|
+
`https://api.atlassian.com/ex/jira/{JIRA_CLOUD_ID}`.
|
|
436
|
+
|
|
437
|
+
## Development
|
|
438
|
+
|
|
439
|
+
```bash
|
|
440
|
+
pip install -e ".[dev]"
|
|
441
|
+
pytest tests/ -v
|
|
442
|
+
ruff check src/methodology_framework
|
|
443
|
+
ruff format --check src/methodology_framework
|
|
444
|
+
mypy --strict src/methodology_framework
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
### Shape-def expansion (v0.1.2+)
|
|
448
|
+
|
|
449
|
+
`workflow.yaml` now includes a `statusCategory` field on each status
|
|
450
|
+
(`TODO`, `IN_PROGRESS`, or `DONE`). The `--apply` handler validates
|
|
451
|
+
this field at load time and exits with a clear error if any status is
|
|
452
|
+
missing it.
|
|
453
|
+
|
|
454
|
+
`custom_fields.yaml` uses canonical shorthand types (`text`, `string`,
|
|
455
|
+
`multi-value`, `numeric`) that are mapped to fully-qualified Jira
|
|
456
|
+
identifiers at apply time. Fields may also specify a fully-qualified
|
|
457
|
+
type directly (any value containing `:` is passed through unchanged).
|
|
458
|
+
|
|
459
|
+
### Recording new VCR cassettes
|
|
460
|
+
|
|
461
|
+
Integration tests under `tests/integration/` replay pre-recorded VCR
|
|
462
|
+
cassettes in CI (no network access required). To record fresh cassettes
|
|
463
|
+
against a real Jira instance:
|
|
464
|
+
|
|
465
|
+
```bash
|
|
466
|
+
export JIRA_USER_EMAIL="<your-email>"
|
|
467
|
+
export JIRA_ADMIN_TOKEN="<your-api-token>"
|
|
468
|
+
PYTEST_VCR_MODE=record pytest tests/integration/ -v
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
Cassettes are stored at `tests/fixtures/vcr/*.yaml`. The VCR config in
|
|
472
|
+
`tests/integration/conftest.py` automatically strips `Authorization`
|
|
473
|
+
headers from recorded interactions. **Never commit a cassette with a
|
|
474
|
+
real token in any header.**
|
|
475
|
+
|
|
476
|
+
After recording, verify no credentials leaked:
|
|
477
|
+
|
|
478
|
+
```bash
|
|
479
|
+
grep -i 'authorization' tests/fixtures/vcr/*.yaml
|
|
480
|
+
# Expected: no output
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
## License
|
|
484
|
+
|
|
485
|
+
Apache-2.0
|