Add project plan, milestones and SQA framework #25
@@ -0,0 +1,7 @@
|
||||
# Copy to .env and fill in real values. .env is gitignored - never commit it.
|
||||
|
||||
# Gitea personal access token (Settings -> Applications -> Generate New Token),
|
||||
# scoped to issues only if your Gitea version supports scoped tokens.
|
||||
# Used by framework/scripts/sync-project.sh --apply against a Gitea remote.
|
||||
# NOT a deploy key - deploy keys authenticate git-over-SSH only, not the API.
|
||||
GITEA_TOKEN=
|
||||
@@ -103,7 +103,8 @@ jobs:
|
||||
headers=gitea_headers,
|
||||
)
|
||||
if not isinstance(mirrors, list):
|
||||
raise RuntimeError("Gitea returned an invalid push mirror list.")
|
||||
print("Gitea returned an invalid push mirror list.")
|
||||
exit 0
|
||||
|
||||
github_targets = []
|
||||
for mirror in mirrors:
|
||||
|
||||
@@ -1,161 +0,0 @@
|
||||
name: Sync GitHub mirror metadata
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: "17 3 * * *"
|
||||
|
||||
jobs:
|
||||
sync-metadata:
|
||||
permissions:
|
||||
contents: read
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Sync description and topics
|
||||
env:
|
||||
GITEA_API_URL: ${{ gitea.api_url }}
|
||||
GITEA_CREDENTIALS: ${{ secrets.TOKEN_FOR_GITEA }}
|
||||
SOURCE_REPOSITORY: ${{ gitea.repository }}
|
||||
GITHUB_CREDENTIALS: ${{ secrets.CREDENTIALS_FOR_GITHUB }}
|
||||
run: |
|
||||
python3 - <<'PY'
|
||||
import json
|
||||
import os
|
||||
import urllib.error
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
|
||||
def request_json(url, method="GET", headers=None, body=None):
|
||||
request = urllib.request.Request(
|
||||
url,
|
||||
data=json.dumps(body).encode("utf-8") if body is not None else None,
|
||||
headers=headers or {},
|
||||
method=method,
|
||||
)
|
||||
try:
|
||||
with urllib.request.urlopen(request, timeout=30) as response:
|
||||
content = response.read()
|
||||
return json.loads(content) if content else None
|
||||
except urllib.error.HTTPError as error:
|
||||
raise RuntimeError(
|
||||
f"API request failed with HTTP {error.code} ({error.reason})"
|
||||
) from None
|
||||
|
||||
raw_credentials = os.environ.get("GITHUB_CREDENTIALS", "").strip()
|
||||
if not raw_credentials:
|
||||
raise RuntimeError("CREDENTIALS_FOR_GITHUB is missing or empty.")
|
||||
|
||||
try:
|
||||
credentials = json.loads(raw_credentials)
|
||||
except json.JSONDecodeError:
|
||||
raise RuntimeError(
|
||||
"CREDENTIALS_FOR_GITHUB must contain valid JSON."
|
||||
) from None
|
||||
|
||||
if not isinstance(credentials, dict):
|
||||
raise RuntimeError(
|
||||
"CREDENTIALS_FOR_GITHUB must be a JSON object."
|
||||
)
|
||||
|
||||
github_token = credentials.get("GITHUB_PAT")
|
||||
if not isinstance(github_token, str) or not github_token.strip():
|
||||
raise RuntimeError(
|
||||
"CREDENTIALS_FOR_GITHUB must contain a non-empty GITHUB_PAT."
|
||||
)
|
||||
|
||||
raw_gitea_credentials = os.environ.get("GITEA_CREDENTIALS", "").strip()
|
||||
if not raw_gitea_credentials:
|
||||
raise RuntimeError("TOKEN_FOR_GITEA is missing or empty.")
|
||||
|
||||
try:
|
||||
gitea_credentials = json.loads(raw_gitea_credentials)
|
||||
except json.JSONDecodeError:
|
||||
gitea_token = raw_gitea_credentials
|
||||
else:
|
||||
if isinstance(gitea_credentials, dict):
|
||||
gitea_token = gitea_credentials.get("GITEA_TOKEN")
|
||||
elif isinstance(gitea_credentials, str):
|
||||
gitea_token = gitea_credentials
|
||||
else:
|
||||
gitea_token = None
|
||||
|
||||
if not isinstance(gitea_token, str) or not gitea_token.strip():
|
||||
raise RuntimeError(
|
||||
"TOKEN_FOR_GITEA must contain a non-empty GITEA_TOKEN."
|
||||
)
|
||||
source_owner, separator, source_repo = os.environ[
|
||||
"SOURCE_REPOSITORY"
|
||||
].partition("/")
|
||||
if not separator or not source_owner or not source_repo:
|
||||
raise RuntimeError("Could not determine the Gitea source repository.")
|
||||
|
||||
gitea_api_url = os.environ["GITEA_API_URL"].rstrip("/")
|
||||
source_url = f"{gitea_api_url}/repos/{source_owner}/{source_repo}"
|
||||
gitea_headers = {
|
||||
"Authorization": f"token {gitea_token.strip()}",
|
||||
"Accept": "application/json",
|
||||
}
|
||||
source = request_json(source_url, headers=gitea_headers)
|
||||
mirrors = request_json(
|
||||
f"{source_url}/push_mirrors",
|
||||
headers=gitea_headers,
|
||||
)
|
||||
if not isinstance(mirrors, list):
|
||||
raise RuntimeError("Gitea returned an invalid push mirror list.")
|
||||
|
||||
github_targets = []
|
||||
for mirror in mirrors:
|
||||
remote_address = mirror.get("remote_address", "")
|
||||
if remote_address.startswith("git@github.com:"):
|
||||
mirror_path = remote_address.split(":", 1)[1]
|
||||
else:
|
||||
parsed_remote = urllib.parse.urlsplit(remote_address)
|
||||
if parsed_remote.hostname != "github.com":
|
||||
continue
|
||||
mirror_path = parsed_remote.path.lstrip("/")
|
||||
|
||||
mirror_path = mirror_path.removesuffix(".git").strip("/")
|
||||
path_parts = mirror_path.split("/")
|
||||
if len(path_parts) != 2 or not all(path_parts):
|
||||
raise RuntimeError(
|
||||
"Could not determine the GitHub owner and repository "
|
||||
"from a configured push mirror."
|
||||
)
|
||||
github_targets.append(tuple(path_parts))
|
||||
|
||||
if len(github_targets) != 1:
|
||||
raise RuntimeError(
|
||||
"Expected exactly one GitHub push mirror for this repository; "
|
||||
f"found {len(github_targets)}."
|
||||
)
|
||||
|
||||
github_owner, github_repo = github_targets[0]
|
||||
github_api_url = (
|
||||
"https://api.github.com/repos/"
|
||||
f"{urllib.parse.quote(github_owner, safe='')}/"
|
||||
f"{urllib.parse.quote(github_repo, safe='')}"
|
||||
)
|
||||
github_headers = {
|
||||
"Authorization": f"Bearer {github_token.strip()}",
|
||||
"Accept": "application/vnd.github+json",
|
||||
"X-GitHub-Api-Version": "2022-11-28",
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
|
||||
request_json(
|
||||
github_api_url,
|
||||
method="PATCH",
|
||||
headers=github_headers,
|
||||
body={"description": source.get("description") or ""},
|
||||
)
|
||||
request_json(
|
||||
f"{github_api_url}/topics",
|
||||
method="PUT",
|
||||
headers=github_headers,
|
||||
body={"names": source.get("topics") or []},
|
||||
)
|
||||
|
||||
print(f"Synced description and topics to {github_owner}/{github_repo}.")
|
||||
PY
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
# Local secrets - never commit these
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
|
||||
# Local, per-clone files generated/copied from framework/ - never commit
|
||||
# these (see framework/README.md "Using it in a project"):
|
||||
# .agents/skills/ <- install-skills.sh's copy of framework/.agents/skills/
|
||||
# .claude/skills/ <- same copy, for the standalone Claude Code CLI
|
||||
/.agents/
|
||||
/.claude/
|
||||
@@ -0,0 +1,3 @@
|
||||
[submodule "framework"]
|
||||
path = framework
|
||||
url = ssh://git@git.tirsystem.com:10022/TirSystem/SQA-QC-Framework.git
|
||||
@@ -0,0 +1,53 @@
|
||||
# AGENTS.md
|
||||
|
||||
This project uses the SQA and QC framework mounted at `framework/`.
|
||||
|
||||
For any document under `docs/` (create, edit or review) use the `artifact`
|
||||
skill. For planning a project or a phase into tasks, and syncing phases and
|
||||
tasks to Gitea/GitHub as Milestones and Issues, use the `project-planning`
|
||||
skill. For writing or reviewing source code (Python, C, C++, C#) use the
|
||||
`coding-conventions` skill. Skills are read from `.agents/skills/` (this harness and Codex CLI)
|
||||
and `.claude/skills/` (standalone Claude Code CLI), both copies made by
|
||||
`bash framework/scripts/install-skills.sh` — re-run it after updating the
|
||||
framework.
|
||||
|
||||
**Never commit, push or open a PR unless asked.** The user reviews changes in
|
||||
the working tree first; edit, summarise and stop. The commit/PR rules below
|
||||
apply once a commit has been asked for.
|
||||
|
||||
## Workflow order
|
||||
|
||||
Business Case, Stakeholder Analysis, Project Plan, milestones, tasks synced as
|
||||
issues, then code. Nothing goes under `src/` or `tests/` unless a milestone
|
||||
document (`MIL-*`) is accepted and the task is a row in it (ideally a synced
|
||||
issue). If those are missing, plan with the `project-planning` skill, show the
|
||||
dry-run output of `bash framework/scripts/sync-project.sh`, and stop. The rule
|
||||
is defined once in `framework/process/plan-first-gate.md`.
|
||||
|
||||
A "build X" request is planning-first: produce the plan and issues, then ask
|
||||
for a go-ahead. Only the user can waive the plan, in chat, for that request.
|
||||
Before planning, find the Product Owner's language (the prompt, or the
|
||||
`Languages` section of `docs/artifact-registry.md`); if neither states it, ask.
|
||||
|
||||
To enforce the gate at commit time, run
|
||||
`bash framework/scripts/install-git-hooks.sh --enable-plan-gate`: a commit that
|
||||
changes `src/` or `tests/` then needs a `Task: MIL-NNN#N` trailer.
|
||||
|
||||
Rules that apply to every document:
|
||||
|
||||
1. Get the short name from `framework/registry/artifact-catalog.md` and the
|
||||
next version from `docs/artifact-registry.md`. Create files with
|
||||
`bash framework/scripts/new-artifact.sh <SHORT>`.
|
||||
2. Owners, reviewers and RACI use stakeholder IDs from the project's
|
||||
Stakeholder Analysis, never invented role names.
|
||||
3. Every QC criterion is tagged with an ISO/IEC 25010:2023 characteristic.
|
||||
4. Every reviewed instance gets an `RC-*` record in `docs/sqa/reviews/`.
|
||||
5. Do not edit `framework/` from this project; propose changes upstream.
|
||||
6. Every PR description closes the issues its work completes, one
|
||||
`Closes #N` per line (`Refs #N` for partial work); see the
|
||||
`project-planning` skill.
|
||||
7. Every document's `## Version History` has `Change` and `Commit` columns and
|
||||
keeps the two latest rows. After committing, run
|
||||
`framework/scripts/resolve-pending-commits.sh` and commit the result before
|
||||
opening the PR (no amend). Never ask for or perform the merge: a reviewer
|
||||
merges.
|
||||
@@ -1,6 +1,6 @@
|
||||
# 🚀 TirSystem GitHub Actions sync github metadata
|
||||
# 🚀 TirSystem GitHub Actions
|
||||
|
||||
Global, reusable GitHub Actions and workflows for TirSystem projects.
|
||||
A collection of reusable Gitea Actions workflows for DevOps professionals and software engineers working on TirSystem projects.
|
||||
|
||||
## 📚 Table of Contents
|
||||
|
||||
@@ -14,9 +14,9 @@ Global, reusable GitHub Actions and workflows for TirSystem projects.
|
||||
|
||||
## 🧭 Overview
|
||||
|
||||
This repository is the central collection of global GitHub Actions used across TirSystem repositories. The primary repository lives at `git.tirsystem.com` (Gitea) and is push-mirrored to GitHub.
|
||||
This repository is the central collection of reusable automation for TirSystem repositories. The primary repository lives at `git.tirsystem.com` (Gitea) and is push-mirrored to GitHub.
|
||||
|
||||
It currently contains the Gitea workflow [`sync-github-metadata.yml`](.gitea/workflows/sync-github-metadata.yml), which copies the repository description and topics from Gitea to the GitHub mirror on every push to `main`, on manual dispatch, and daily at 03:17 UTC.
|
||||
It currently contains the Gitea workflow [`sync-github-metadata.yml`](.gitea/scoped_workflows/sync-github-metadata.yml), which copies the repository description and topics from Gitea to the GitHub mirror on every push to `main`, on manual dispatch, and daily at 03:17 UTC.
|
||||
|
||||
## 📋 Requirements
|
||||
|
||||
@@ -43,7 +43,83 @@ Workflows run automatically on push to `main` and on the daily schedule. To run
|
||||
|
||||
## 🧪 Tests
|
||||
|
||||
There are no automated tests. Verify changes by running the workflow manually and checking the description and topics on the GitHub mirror.
|
||||
There are no automated tests yet (planned in MIL-001). `tests/test_.ps1` is a manual probe, not a test. Verify changes by running the workflow manually and checking the description and topics on the GitHub mirror.
|
||||
|
||||
## 🟢 Capabilities
|
||||
|
||||
Only the first row is implemented. The rest is planned and tracked in the [Project Plan](docs/project-plan.md), which also describes each use case.
|
||||
|
||||
| Capability | Status |
|
||||
| --- | --- |
|
||||
| Synchronize repository metadata (Gitea to GitHub mirror) | Implemented in `.gitea/scoped_workflows/`; the script there currently has a syntax error (`exit 0`) that MIL-001 fixes |
|
||||
| Configure and validate workflow credentials | Partial: secrets are checked for format only |
|
||||
| Onboard a repository, manage runners, diagnose failures | Planned (MIL-002) |
|
||||
| Shared quality checks | Planned (MIL-003) |
|
||||
| Versioned release and publishing | Planned (MIL-004) |
|
||||
|
||||
### 🔄 Sync GitHub metadata
|
||||
|
||||
Syncs the Gitea repository description and topics to the GitHub push mirror.
|
||||
|
||||
#### 🕒 Cron
|
||||
|
||||
Daily at 03:17 UTC (`17 3 * * *`), plus every push to `main` and manual `workflow_dispatch`.
|
||||
|
||||
#### 🔀 Workflow
|
||||
|
||||
```plantuml
|
||||
@startuml
|
||||
title Sync Gitea repository description and topics to GitHub
|
||||
|
||||
autonumber
|
||||
|
||||
actor "Push to main,\nmanual trigger, or schedule" as Trigger
|
||||
participant "Gitea Actions\nsync-github-metadata" as Workflow
|
||||
participant "Gitea REST API" as Gitea
|
||||
participant "GitHub REST API" as GitHub
|
||||
|
||||
Trigger -> Workflow: Start workflow
|
||||
activate Workflow
|
||||
|
||||
Workflow -> Workflow: Read and validate secrets
|
||||
note right
|
||||
TOKEN_FOR_GITEA
|
||||
CREDENTIALS_FOR_GITHUB
|
||||
end note
|
||||
|
||||
alt secret validation failed
|
||||
Workflow --> Trigger: workflow failed
|
||||
end
|
||||
|
||||
|
||||
Workflow -> Gitea: GET /repos/{owner}/{repo}
|
||||
Gitea --> Workflow: Description and topics
|
||||
|
||||
Workflow -> Gitea: GET /repos/{owner}/{repo}/push_mirrors
|
||||
Gitea --> Workflow: Push mirror response
|
||||
|
||||
alt mirrors is not a list
|
||||
Workflow --> Trigger: Workflow fails\n"Gitea returned an invalid push mirror list."
|
||||
else mirrors is a list
|
||||
Workflow -> Workflow: Find GitHub owner and repo from remote_address
|
||||
|
||||
Workflow -> GitHub: PATCH /repos/{owner}/{repo}\n{description}
|
||||
GitHub --> Workflow: Repository updated
|
||||
|
||||
Workflow -> GitHub: PUT /repos/{owner}/{repo}/topics\n{names}
|
||||
GitHub --> Workflow: Topics updated
|
||||
|
||||
Workflow --> Trigger: Sync completed
|
||||
else Gitea API denies access
|
||||
Gitea --> Workflow: HTTP 403 Forbidden
|
||||
Workflow --> Trigger: Workflow fails
|
||||
else GitHub API denies access
|
||||
GitHub --> Workflow: HTTP error
|
||||
Workflow --> Trigger: Workflow fails
|
||||
end
|
||||
deactivate Workflow
|
||||
@enduml
|
||||
```
|
||||
|
||||
## 📄 License
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
# Artifact Registry
|
||||
|
||||
This project's artifact state. Types, short names and `CrossReference
|
||||
Candidates` come from the framework catalog
|
||||
(`framework/registry/artifact-catalog.md`); this file only records where
|
||||
each document lives in *this* project and the next version to use.
|
||||
|
||||
| Short Name | Artifact Type | Primary File | Next Available Version |
|
||||
| --- | --- | --- | --- |
|
||||
| BC | Business Case | docs/business-case.md | 002 |
|
||||
| SA | Stakeholder Analysis | docs/stakeholder-analysis.md | 002 |
|
||||
| PP | Project Plan | docs/project-plan.md | 002 |
|
||||
| MIL | Milestone / Gateway | docs/milestones/*.md | 005 |
|
||||
| RC | SQA Review Record | docs/sqa/reviews/rc-*.md | 001 |
|
||||
|
||||
## Languages
|
||||
|
||||
| Setting | Value |
|
||||
| --- | --- |
|
||||
| PO language | en (English, IT) |
|
||||
| High-level register | IT Executive English |
|
||||
| Technical register | IT Professional English |
|
||||
|
||||
The PO language is English, so no PO-language translations are kept.
|
||||
|
||||
| Artifact types | Register | Also kept as a PO-language file |
|
||||
| --- | --- | --- |
|
||||
| BC, KPI, PP, MIL | IT Executive English | No (PO language is English) |
|
||||
| SA, BMC, BPMN, UCD, US, UC, SSD, DM, RA, GOV, DICT | IT Professional English | No (PO language is English) |
|
||||
| OC, SD, DCD, ERD, ADR, TM, RC, QC, source code | IT Professional English | No |
|
||||
|
||||
## Notes
|
||||
|
||||
- "Next Available Version" is the zero-padded (3-digit) version to use the
|
||||
*next* time a new document of that type is created. Increment it only when
|
||||
a brand-new document is created, not when an existing document's
|
||||
`## Version History` gets a row.
|
||||
- `ADR` uses 4 digits (`0001`); `RC` is sequential across all artifact types.
|
||||
@@ -0,0 +1,150 @@
|
||||
# Business Case: TirSystem Reusable Workflows
|
||||
|
||||
## Metadata
|
||||
| Key | Value |
|
||||
| --- | --- |
|
||||
| ID | BC-001 |
|
||||
| CrossReference | [SA-001] |
|
||||
|
||||
## Version History
|
||||
| Date | Status | Author | Reviewer | Change | Commit |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 2026-10-03 | Rejected | Jens Tirsvad Nielsen | S01 | Initial version | pending |
|
||||
| 2026-10-03 | Accepted | Jens Tirsvad Nielsen | S01 | Added S05 to assumptions, risks and stakeholders | pending |
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
|
||||
TirSystem repositories are hosted on a self-hosted Gitea instance and
|
||||
push-mirrored to GitHub. Automation that every repository needs is currently
|
||||
copied by hand. This project turns this repository into the single, reviewed
|
||||
collection of reusable Gitea Actions workflows for DevOps professionals and
|
||||
software engineers. Today it contains one workflow, which copies repository
|
||||
description and topics from Gitea to the GitHub mirror. The proposal is to
|
||||
harden that workflow first, document how repositories adopt it, and then add
|
||||
shared quality-check and release workflows in priority order.
|
||||
|
||||
## Methodological and Standards Foundation
|
||||
|
||||
Documents follow the SQA and QC framework in `framework/` (use cases after
|
||||
Larman, *Applying UML and Patterns*). Quality criteria are tagged with
|
||||
ISO/IEC 25010:2023 characteristics. The domain language is English (IT).
|
||||
|
||||
## Problem Statement
|
||||
|
||||
- The only workflow exists as three diverging copies (`src/`,
|
||||
`.gitea/scoped_workflows/`, and others), and the scoped copy
|
||||
currently contains a Python syntax error (`exit 0`).
|
||||
- There is no automated check of workflow files, so defects such as that one
|
||||
are found only when the workflow runs.
|
||||
- There is no documented way to onboard a repository, set up credentials,
|
||||
choose runners or diagnose a failed run.
|
||||
- Quality checks and release steps are not shared, so each project would
|
||||
reinvent them.
|
||||
|
||||
## Business Opportunity
|
||||
|
||||
A small, versioned, documented workflow collection lets every TirSystem
|
||||
repository adopt the same secure automation in minutes, and lets a fix be
|
||||
made once.
|
||||
|
||||
## Objectives
|
||||
|
||||
- **O1:** Keep the existing metadata sync correct, tested and documented as
|
||||
the single implemented capability.
|
||||
- **O2:** Provide one canonical copy of each workflow with a defined way to
|
||||
reach consuming repositories (scoped workflow or copy).
|
||||
- **O3:** Document and validate credentials, runner labels and failure
|
||||
diagnosis for every workflow.
|
||||
- **O4:** Offer shared quality-check and release workflows, versioned so
|
||||
consumers can adopt changes deliberately.
|
||||
|
||||
## Scope
|
||||
|
||||
### In Scope
|
||||
|
||||
- Gitea Actions workflows (and GitHub-compatible syntax where it is the same).
|
||||
- Documentation for DevOps professionals and software engineers.
|
||||
- Validation of workflow files and their documentation.
|
||||
|
||||
### Out of Scope
|
||||
|
||||
- Operating the Gitea instance or runners themselves.
|
||||
- Application code of consuming repositories.
|
||||
- Features not backed by an accepted milestone task.
|
||||
- Behavior that is only proposed: documentation describes implemented
|
||||
workflows only; planned ones are marked as planned.
|
||||
|
||||
## Expected Benefits
|
||||
|
||||
### Tangible Benefits
|
||||
|
||||
- One fix reaches all consuming repositories.
|
||||
- Fewer failed runs caused by missing or over-broad credentials.
|
||||
- Less time to onboard a repository.
|
||||
|
||||
### Intangible Benefits
|
||||
|
||||
- Consistent, reviewable automation across TirSystem projects.
|
||||
- Higher trust in automation that touches credentials.
|
||||
|
||||
## Strategic Alignment
|
||||
|
||||
Supports TirSystem's practice of a self-hosted primary repository with
|
||||
public GitHub mirrors, and its SQA framework requirement that code follows
|
||||
accepted plans.
|
||||
|
||||
## Success Criteria
|
||||
|
||||
| # | Criterion | Target | Measure |
|
||||
| --- | --- | --- | --- |
|
||||
| 1 | Workflow files that fail static validation | 0 on the default branch | Result of the validation task in MIL-001 |
|
||||
| 2 | Canonical copies per workflow | 1 | Count of files per workflow name |
|
||||
| 3 | Documented use cases that are implemented or marked planned | 100% | README capability table against workflows present |
|
||||
| 4 | Repositories consuming a shared workflow | At least 1 besides this one by MIL-002 | Consumer list in README |
|
||||
|
||||
## Risks
|
||||
|
||||
| Risk | Impact | Mitigation |
|
||||
| --- | --- | --- |
|
||||
| Gitea scoped workflows behave differently from assumption (feature and version not verified) | Onboarding design is wrong | Verify against the actual instance in MIL-002 before documenting it as supported. |
|
||||
| Token with excess scope is stored in secrets | Compromise of repositories | Document minimum scope; validate token permissions before use. |
|
||||
| A breaking change reaches consumers unannounced | Consumer pipelines fail | Version and changelog every release (MIL-004). |
|
||||
| Security-relevant change accepted without security review | Credential or permission defect reaches production | S05 reviews every change to credentials, tokens, permissions and workflow code. |
|
||||
|
||||
## Assumptions
|
||||
|
||||
- The Gitea instance has Actions enabled and an `ubuntu-latest` runner with `python3`.
|
||||
- Each mirrored repository has exactly one GitHub push mirror.
|
||||
- S01 (Product Owner) and S05 (DevOps owner and security authority) are both maintainers.
|
||||
|
||||
## Constraints
|
||||
|
||||
- Workflows use only tools available on the runner; the current workflow uses the Python standard library only.
|
||||
- Secrets are supplied as repository or organization secrets, never committed.
|
||||
- Nothing is written under `src/` or `tests/` before the matching milestone is accepted.
|
||||
|
||||
## Cost–Benefit Assessment
|
||||
|
||||
| Costs | Benefits |
|
||||
| --- | --- |
|
||||
| Maintainer time for planning, documentation and tests (qualitative; no external cost) | Avoided duplication and fewer credential and configuration failures across repositories |
|
||||
|
||||
## Stakeholders
|
||||
|
||||
| Stakeholder ID (SA) | Interest in this project |
|
||||
| --- | --- |
|
||||
| S01 | Owns scope, accepts the documents and co-maintains the workflows |
|
||||
| S02 | Consumes shared workflows |
|
||||
| S03 | Operates runners and secrets |
|
||||
| S05 | Co-maintains the workflows and reviews their security |
|
||||
| S04 | Sees mirror metadata |
|
||||
|
||||
## Recommendation
|
||||
|
||||
Proceed — the first milestone fixes a known defect in the only existing workflow and the later ones add value only after it is accepted.
|
||||
|
||||
---
|
||||
|
||||
[SA-001]: ./stakeholder-analysis.md
|
||||
@@ -0,0 +1,75 @@
|
||||
# MIL-001: Stabilise the metadata sync
|
||||
|
||||
## Metadata
|
||||
| Key | Value |
|
||||
| --- | --- |
|
||||
| ID | MIL-001 |
|
||||
| CrossReference | [BC-001] |
|
||||
|
||||
## Version History
|
||||
| Date | Status | Author | Reviewer | Change | Commit |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 2026-10-03 | Rejected | Jens Tirsvad Nielsen | S01 | Initial version | pending |
|
||||
| 2026-10-03 | Accepted | Jens Tirsvad Nielsen | S01 | Owner is S05 (security authority); S01 remains approving reviewer | pending |
|
||||
|
||||
---
|
||||
|
||||
## Purpose
|
||||
|
||||
Decide whether the only implemented workflow is correct, tested and has one
|
||||
canonical copy, so later milestones build on a sound base.
|
||||
|
||||
## Deliverable
|
||||
|
||||
A single canonical metadata-sync workflow that passes automated validation
|
||||
and tests, a README that separates implemented from planned behavior, and a
|
||||
successful manual run on the Gitea instance.
|
||||
|
||||
## Go / No-Go Criteria
|
||||
|
||||
| # | Criterion (objectively checkable) | Go | No-Go |
|
||||
| --- | --- | --- | --- |
|
||||
| 1 | The embedded Python compiles and every workflow YAML parses in an automated check | Check passes | Any failure |
|
||||
| 2 | Exactly one canonical source per workflow; other copies are generated or removed | Count is 1 | Count above 1 |
|
||||
| 3 | Unit tests cover credential parsing and push mirror URL parsing, and run without network access or `.env` | Tests pass | Missing or failing |
|
||||
| 4 | README capability table matches the workflows present | Matches | Claims unimplemented behavior |
|
||||
| 5 | Manual `workflow_dispatch` run on Gitea succeeds and the GitHub description and topics match | Match | Mismatch or failure |
|
||||
|
||||
## Dependencies
|
||||
|
||||
| Depends on | Reason |
|
||||
| --- | --- |
|
||||
| None | First milestone |
|
||||
|
||||
## Traceability
|
||||
|
||||
| Business Case objective / KPI / user story | Reference |
|
||||
| --- | --- |
|
||||
| O1, O2 | [BC-001] |
|
||||
|
||||
## Ownership
|
||||
|
||||
| Role | Stakeholder ID (SA) |
|
||||
| --- | --- |
|
||||
| Owner | S05 |
|
||||
| Approving reviewer | S01 |
|
||||
|
||||
## Target Date
|
||||
|
||||
2026-10-17 — proposed; to be confirmed by S01 and S05 (the Business Case sets no deadline).
|
||||
|
||||
## Tasks
|
||||
|
||||
| # | Task | Summary | Needs its own Use Case/User Story? | Reference |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 1 | Decide behavior for an invalid push mirror response | The committed workflow fails with an error when Gitea returns a non-list push mirror response. The uncommitted scoped copy instead prints a message and tries to exit successfully, and the README diagram showed that. Choose fail or skip, record the decision, and align code and README. | No | |
|
||||
| 2 | Fix the Python syntax error in the scoped workflow | `exit 0` is not valid Python, so the whole embedded script fails to compile in the scoped copy. Replace it according to the decision in task 1 (`sys.exit(0)` also needs `import sys`). | No | |
|
||||
| 3 | Reduce the workflow to one canonical source | The same workflow exists in `src/` and `.gitea/scoped_workflows/` (and a legacy copy that is no longer documented), and the scoped copy has diverged. Choose the canonical location, and generate or remove the others. | No | |
|
||||
| 4 | Add static validation of workflow files | Parse each workflow YAML and compile its embedded Python in an automated check that runs on pull requests, so defects such as task 2 fail before merge. | No | |
|
||||
| 5 | Add offline unit tests for the sync logic | Move credential and mirror URL parsing into testable code and test bare token, JSON token, SSH and HTTPS mirror addresses, and zero or several mirrors. Replace `tests/test_.ps1`, which probes a hard-coded real repository and reads `.env`. | No | |
|
||||
| 6 | Keep the README accurate | Maintain the capability table (implemented, partial, planned), and align the sequence diagram with the final behavior from task 1. | No | |
|
||||
| 7 | Verify a live run | Trigger the workflow manually on Gitea and compare the GitHub description and topics with the Gitea repository. | No | |
|
||||
|
||||
---
|
||||
|
||||
[BC-001]: ../business-case.md
|
||||
@@ -0,0 +1,75 @@
|
||||
# MIL-002: Onboarding and operations guide
|
||||
|
||||
## Metadata
|
||||
| Key | Value |
|
||||
| --- | --- |
|
||||
| ID | MIL-002 |
|
||||
| CrossReference | [BC-001] |
|
||||
|
||||
## Version History
|
||||
| Date | Status | Author | Reviewer | Change | Commit |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 2026-10-03 | Proposed | Jens Tirsvad Nielsen | S01 | Initial version | pending |
|
||||
| 2026-10-03 | Proposed | Jens Tirsvad Nielsen | S01 | Owner is S05 (security authority); S01 remains approving reviewer | pending |
|
||||
|
||||
---
|
||||
|
||||
## Purpose
|
||||
|
||||
Decide whether a DevOps professional can onboard a repository, set up
|
||||
credentials, choose runners and diagnose a failed run from the documentation
|
||||
alone.
|
||||
|
||||
## Deliverable
|
||||
|
||||
An onboarding and operations guide verified against the real Gitea instance,
|
||||
a credential validation step in the workflow, and categorized failure messages.
|
||||
|
||||
## Go / No-Go Criteria
|
||||
|
||||
| # | Criterion (objectively checkable) | Go | No-Go |
|
||||
| --- | --- | --- | --- |
|
||||
| 1 | Support for scoped workflows on the instance is verified and the Gitea version is recorded | Recorded | Unverified |
|
||||
| 2 | A second repository is onboarded by following only the guide | Run succeeds | Needs undocumented steps |
|
||||
| 3 | Minimum token permissions are documented for both secrets and checked by the workflow before use | Documented and checked | Missing |
|
||||
| 4 | Every failure message names its category: configuration, credentials, permissions, API response or runner | Verified for all messages | Uncategorized message exists |
|
||||
| 5 | Supported runner labels and prerequisites are listed | Listed | Missing |
|
||||
|
||||
## Dependencies
|
||||
|
||||
| Depends on | Reason |
|
||||
| --- | --- |
|
||||
| MIL-001 | The guide describes the stabilised workflow |
|
||||
|
||||
## Traceability
|
||||
|
||||
| Business Case objective / KPI / user story | Reference |
|
||||
| --- | --- |
|
||||
| O2, O3 | [BC-001] |
|
||||
|
||||
## Ownership
|
||||
|
||||
| Role | Stakeholder ID (SA) |
|
||||
| --- | --- |
|
||||
| Owner | S05 |
|
||||
| Approving reviewer | S01 |
|
||||
|
||||
## Target Date
|
||||
|
||||
2026-10-31 — proposed; to be confirmed by S01 and S05.
|
||||
|
||||
## Tasks
|
||||
|
||||
| # | Task | Summary | Needs its own Use Case/User Story? | Reference |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 1 | Write brief use cases for the P1 candidates | Turn the DevOps use cases in the Project Plan appendix into use case documents under `docs/uc-NNN/`, after a use case diagram and user stories exist. | No | |
|
||||
| 2 | Verify scoped workflow support on the instance | The Business Case lists this as an unverified assumption. Check the Gitea version and how a scoped workflow reaches a repository, then record the result. | No | |
|
||||
| 3 | Write the repository onboarding steps | Describe how to consume the workflow (scoped or copied), which secrets to set and how to test the first run. | No | |
|
||||
| 4 | Document least-privilege tokens | Determine the minimum Gitea token scope and GitHub fine-grained token permission that the sync needs, and document them. | No | |
|
||||
| 5 | Add a credential preflight step | Before the sync, check that both tokens authenticate and have the needed access, and report the result without printing any secret. | No | |
|
||||
| 6 | Categorize failure messages | Group the error messages and HTTP errors into configuration, credentials, permissions, API response and runner, and write the diagnosis table. | No | |
|
||||
| 7 | Document runner labels and prerequisites | State the supported labels (`ubuntu-latest`), the required `python3`, and how to recognize an offline or incompatible runner. | No | |
|
||||
|
||||
---
|
||||
|
||||
[BC-001]: ../business-case.md
|
||||
@@ -0,0 +1,71 @@
|
||||
# MIL-003: Shared quality checks
|
||||
|
||||
## Metadata
|
||||
| Key | Value |
|
||||
| --- | --- |
|
||||
| ID | MIL-003 |
|
||||
| CrossReference | [BC-001] |
|
||||
|
||||
## Version History
|
||||
| Date | Status | Author | Reviewer | Change | Commit |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 2026-10-03 | Proposed | Jens Tirsvad Nielsen | S01 | Initial version | pending |
|
||||
| 2026-10-03 | Proposed | Jens Tirsvad Nielsen | S01 | Owner is S05 (security authority); S01 remains approving reviewer | pending |
|
||||
|
||||
---
|
||||
|
||||
## Purpose
|
||||
|
||||
Decide whether a software engineer can run the same quality checks on any
|
||||
TirSystem project through a shared workflow.
|
||||
|
||||
## Deliverable
|
||||
|
||||
A reusable quality-check workflow for Python and shell, used by at least one
|
||||
other repository, and a guide for reading its results.
|
||||
|
||||
## Go / No-Go Criteria
|
||||
|
||||
| # | Criterion (objectively checkable) | Go | No-Go |
|
||||
| --- | --- | --- | --- |
|
||||
| 1 | The instance supports reusable workflows (`workflow_call`), verified and recorded | Verified | Unsupported (re-plan as a copied template) |
|
||||
| 2 | One other repository runs the workflow and its status check appears on pull requests | Appears | Does not run |
|
||||
| 3 | A failing check shows the tool, file and message in the log | Verified with a seeded failure | Cause not visible |
|
||||
| 4 | Inputs and defaults are documented | Documented | Missing |
|
||||
|
||||
## Dependencies
|
||||
|
||||
| Depends on | Reason |
|
||||
| --- | --- |
|
||||
| MIL-002 | Uses the onboarding guide and runner list |
|
||||
|
||||
## Traceability
|
||||
|
||||
| Business Case objective / KPI / user story | Reference |
|
||||
| --- | --- |
|
||||
| O4 | [BC-001] |
|
||||
|
||||
## Ownership
|
||||
|
||||
| Role | Stakeholder ID (SA) |
|
||||
| --- | --- |
|
||||
| Owner | S05 |
|
||||
| Approving reviewer | S01 |
|
||||
|
||||
## Target Date
|
||||
|
||||
2026-11-14 — proposed; to be confirmed by S01 and S05.
|
||||
|
||||
## Tasks
|
||||
|
||||
| # | Task | Summary | Needs its own Use Case/User Story? | Reference |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 1 | Verify reusable workflow support | Check whether this Gitea version supports `workflow_call` and cross-repository `uses`; if not, choose a copied template instead. | No | |
|
||||
| 2 | Define the quality-check contract | Decide the inputs (language, tool versions, working directory) and what a pass and a fail mean. Start with Python and shell. This is something an engineer does, so it needs a use case first. | Yes | Use case candidate: run project quality checks |
|
||||
| 3 | Implement the reusable quality workflow | Create the workflow in the canonical location with pinned tool versions and least-privilege permissions. | No | |
|
||||
| 4 | Adopt it in one other repository | Onboard a real project and fix the gaps in the guide that appear. | No | |
|
||||
| 5 | Write the guide for reading results | Explain status checks, logs and typical fixes so an engineer can make a failing check pass. | No | |
|
||||
|
||||
---
|
||||
|
||||
[BC-001]: ../business-case.md
|
||||
@@ -0,0 +1,72 @@
|
||||
# MIL-004: Versioned release and publishing
|
||||
|
||||
## Metadata
|
||||
| Key | Value |
|
||||
| --- | --- |
|
||||
| ID | MIL-004 |
|
||||
| CrossReference | [BC-001] |
|
||||
|
||||
## Version History
|
||||
| Date | Status | Author | Reviewer | Change | Commit |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 2026-10-03 | Proposed | Jens Tirsvad Nielsen | S01 | Initial version | pending |
|
||||
| 2026-10-03 | Proposed | Jens Tirsvad Nielsen | S01 | Owner is S05 (security authority); S01 remains approving reviewer | pending |
|
||||
|
||||
---
|
||||
|
||||
## Purpose
|
||||
|
||||
Decide whether workflow updates can be released with a version and adopted
|
||||
safely, and whether a project can publish an artifact through a shared
|
||||
workflow.
|
||||
|
||||
## Deliverable
|
||||
|
||||
A documented release process for this repository (validation, version tag,
|
||||
changelog), a consumer upgrade guide and a reusable release workflow.
|
||||
|
||||
## Go / No-Go Criteria
|
||||
|
||||
| # | Criterion (objectively checkable) | Go | No-Go |
|
||||
| --- | --- | --- | --- |
|
||||
| 1 | A release is validated by the MIL-001 checks before a tag is created | Enforced | Tag possible without checks |
|
||||
| 2 | Each release has a version tag and a changelog entry that marks breaking changes | Present | Missing |
|
||||
| 3 | A consumer pinned to the previous version keeps working after a release | Verified | Breaks |
|
||||
| 4 | The publish destination (release page or package registry) is decided and its token scope documented | Decided | Open |
|
||||
|
||||
## Dependencies
|
||||
|
||||
| Depends on | Reason |
|
||||
| --- | --- |
|
||||
| MIL-003 | Reuses the reusable workflow mechanism |
|
||||
|
||||
## Traceability
|
||||
|
||||
| Business Case objective / KPI / user story | Reference |
|
||||
| --- | --- |
|
||||
| O4 | [BC-001] |
|
||||
|
||||
## Ownership
|
||||
|
||||
| Role | Stakeholder ID (SA) |
|
||||
| --- | --- |
|
||||
| Owner | S05 |
|
||||
| Approving reviewer | S01 |
|
||||
|
||||
## Target Date
|
||||
|
||||
2026-11-28 — proposed; to be confirmed by S01 and S05.
|
||||
|
||||
## Tasks
|
||||
|
||||
| # | Task | Summary | Needs its own Use Case/User Story? | Reference |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 1 | Define the release process for this repository | Describe how a workflow change is validated, tagged and announced, and how consumers pin a version. | Yes | Use case candidate: release a reusable workflow |
|
||||
| 2 | Add a changelog and tag convention | Create `CHANGELOG.md` and a tag scheme (for example `v1` plus `v1.2.3`) that separates breaking from compatible changes. | No | |
|
||||
| 3 | Write the consumer upgrade guide | Explain how to read a changelog entry, test an update in one repository and roll back. | Yes | Use case candidate: consume a workflow update |
|
||||
| 4 | Decide the publish destination | Choose the release or package destination for built artifacts and document the token scope it needs. | No | |
|
||||
| 5 | Implement the reusable release workflow | Validate the version, build release notes from the changelog and publish to the chosen destination. | Yes | Use case candidate: publish a release |
|
||||
|
||||
---
|
||||
|
||||
[BC-001]: ../business-case.md
|
||||
@@ -0,0 +1,247 @@
|
||||
# Project Plan: TirSystem Reusable Workflows
|
||||
|
||||
## Metadata
|
||||
| Key | Value |
|
||||
| --- | --- |
|
||||
| ID | PP-001 |
|
||||
| CrossReference | [BC-001], [SA-001], [MIL-001], [MIL-002], [MIL-003], [MIL-004] |
|
||||
|
||||
## Version History
|
||||
| Date | Status | Author | Reviewer | Change | Commit |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 2026-10-03 | Rejected | Jens Tirsvad Nielsen | S01 | Initial version<br>Added use case candidates and prioritisation appendix | pending |
|
||||
| 2026-10-03 | Accepted | Jens Tirsvad Nielsen | S01 | Owner of every gateway is S05<br>Actors updated | pending |
|
||||
|
||||
---
|
||||
|
||||
## Purpose
|
||||
|
||||
This plan schedules four milestones that move the repository from one
|
||||
workflow with known defects to a documented, versioned collection of shared
|
||||
workflows. The Business Case sets no deadline, so the dates are proposals.
|
||||
|
||||
## Planning Assumptions
|
||||
|
||||
- Week 1 starts 2026-10-05; phases are two weeks long; the last decision is 2026-11-28.
|
||||
- Work on a milestone starts only after its `MIL-*` document is `Accepted`.
|
||||
- Dates are proposed by the author and need confirmation by S01 and S05.
|
||||
|
||||
## Gateway Schedule
|
||||
|
||||
| Gateway | Document | Window | Decision date | Owner | Stories | Main deliverable | Milestone |
|
||||
| --- | --- | --- | --- | --- | --- | --- | --- |
|
||||
| Stabilise the metadata sync | [MIL-001] | 2026-10-05 to 2026-10-17 | 2026-10-17 | S05 | None yet | Single tested workflow, accurate README | [Milestone MIL-001] |
|
||||
| Onboarding and operations guide | [MIL-002] | 2026-10-19 to 2026-10-31 | 2026-10-31 | S05 | None yet | Verified onboarding, credential and diagnosis guide | [Milestone MIL-002] |
|
||||
| Shared quality checks | [MIL-003] | 2026-11-02 to 2026-11-14 | 2026-11-14 | S05 | None yet | Reusable quality-check workflow | [Milestone MIL-003] |
|
||||
| Versioned release and publishing | [MIL-004] | 2026-11-16 to 2026-11-28 | 2026-11-28 | S05 | None yet | Release process and reusable release workflow | [Milestone MIL-004] |
|
||||
|
||||
```plantuml
|
||||
@startgantt
|
||||
Project starts 2026-10-05
|
||||
[MIL-001 Stabilise] starts 2026-10-05 and ends 2026-10-17
|
||||
[MIL-002 Onboarding] starts 2026-10-19 and ends 2026-10-31
|
||||
[MIL-003 Quality checks] starts 2026-11-02 and ends 2026-11-14
|
||||
[MIL-004 Release] starts 2026-11-16 and ends 2026-11-28
|
||||
[MIL-001 Go/No-Go] happens 2026-10-17
|
||||
[MIL-002 Go/No-Go] happens 2026-10-31
|
||||
[MIL-003 Go/No-Go] happens 2026-11-14
|
||||
[MIL-004 Go/No-Go] happens 2026-11-28
|
||||
@endgantt
|
||||
```
|
||||
|
||||
## Scope Coverage
|
||||
|
||||
| Business Case scope item | Gateway |
|
||||
| --- | --- |
|
||||
| Validation of workflow files and their documentation | [MIL-001] |
|
||||
| Documentation for DevOps professionals | [MIL-002] |
|
||||
| Gitea Actions workflows for software engineers (quality checks) | [MIL-003] |
|
||||
| Gitea Actions workflows for software engineers (release and publishing) | [MIL-004] |
|
||||
|
||||
## Dependencies
|
||||
|
||||
```
|
||||
MIL-001 → MIL-002 → MIL-003 → MIL-004
|
||||
```
|
||||
|
||||
A No-Go moves every later window by the rework time of the failed milestone.
|
||||
|
||||
## Plan Risks
|
||||
|
||||
| Risk | Impact | Mitigation |
|
||||
| --- | --- | --- |
|
||||
| Reusable workflow support is missing on the instance | MIL-003 and MIL-004 change shape | Verify first in each milestone (MIL-002 task 2, MIL-003 task 1); fall back to copied templates. |
|
||||
| Security review by S05 is a bottleneck | Milestone decisions slip | Keep milestones small and plan review time before each decision date. |
|
||||
|
||||
## Open Issues
|
||||
|
||||
- Confirm the proposed dates and the start week (S01 and S05).
|
||||
- Decide the behavior for an invalid push mirror response (MIL-001 task 1).
|
||||
- Decide the canonical location of the workflow source (MIL-001 task 3).
|
||||
- No QC checklist exists for the Project Plan.
|
||||
- Formal use cases need a use case diagram and user stories first; neither exists yet.
|
||||
|
||||
## Appendix: Use Case Candidates and Prioritisation
|
||||
|
||||
This appendix evaluates the candidate use cases against the repository as it
|
||||
is today. It is analysis, not a use case document; formal use cases follow in
|
||||
MIL-002 task 1. Priority: P1 do first, P2 next, P3 later.
|
||||
|
||||
**Recommended order:** (1) Synchronize repository metadata, because it is
|
||||
the only implemented capability and has a defect, (2) Configure workflow
|
||||
credentials, (3) Onboard a repository, (4) Diagnose a failed workflow, then
|
||||
(5) Run project quality checks and (6) Release a reusable workflow. The first
|
||||
four make the existing workflow dependable and adoptable; the others add new
|
||||
workflows and only pay off once adoption works.
|
||||
|
||||
Current repository state used for the status column: one workflow
|
||||
(`sync-github-metadata`), one manual probe script (`tests/test_.ps1`), no CI,
|
||||
no tests, no release process.
|
||||
|
||||
| # | Use case | Actor | Priority | Status today |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 1 | Synchronize repository metadata | DevOps professional (S05) | P1 | Implemented in `.gitea/scoped_workflows/`; script has a syntax error |
|
||||
| 2 | Configure workflow credentials | DevOps professional (S05, S03) | P1 | Partial: secrets are format-checked only |
|
||||
| 3 | Onboard a repository to standard workflows | DevOps professional (S05) | P1 | Not documented; scoped workflow support unverified |
|
||||
| 4 | Diagnose a failed workflow | DevOps professional (S05, S03) | P1 | Partial: errors are messages only, uncategorized |
|
||||
| 5 | Run project quality checks | Software engineer (S02) | P2 | Planned |
|
||||
| 6 | Release a reusable workflow | DevOps professional (S05) | P2 | Planned |
|
||||
| 7 | Manage workflow runners | DevOps professional (S03, S05) | P2 | Partial: only `ubuntu-latest` with `python3` assumed |
|
||||
| 8 | Consume a workflow update | Software engineer (S02) | P2 | Planned; needs versioning |
|
||||
| 9 | Review workflow results | Software engineer (S02) | P3 | Gitea provides logs; guidance planned |
|
||||
| 10 | Create a project workflow | Software engineer (S02) | P3 | Planned; needs MIL-003 |
|
||||
| 11 | Build and publish an artifact | Software engineer (S02) | P3 | Planned; destination undecided |
|
||||
| 12 | Publish a release | Software engineer (S02) | P3 | Planned; overlaps with use case 6 and 11 |
|
||||
|
||||
### UC candidate 1: Synchronize repository metadata (P1)
|
||||
|
||||
- **Actor:** DevOps professional. **Goal:** the GitHub mirror shows the Gitea description and topics.
|
||||
- **Trigger:** push to `main`, manual dispatch, or daily at 03:17 UTC.
|
||||
- **Preconditions:** both secrets set; exactly one GitHub push mirror configured; runner available.
|
||||
- **Success:** description and topics on GitHub equal those on Gitea.
|
||||
- **Main flow:** the workflow validates the secrets, reads description and topics from Gitea, finds the single GitHub mirror, updates the description, replaces the topics.
|
||||
- **Alternatives and failures:** missing or invalid secret: fail with a message; zero or several GitHub mirrors: fail; non-list mirror response: behavior undecided (MIL-001 task 1); Gitea or GitHub API error: fail with the HTTP code.
|
||||
- **Dependencies:** none.
|
||||
|
||||
### UC candidate 2: Configure workflow credentials (P1)
|
||||
|
||||
- **Actor:** DevOps professional. **Goal:** least-privilege tokens that a workflow accepts.
|
||||
- **Trigger:** onboarding a repository or rotating a token.
|
||||
- **Preconditions:** access to Gitea and GitHub settings.
|
||||
- **Success:** a workflow run passes credential checks with the documented minimum permissions.
|
||||
- **Main flow:** create the tokens, store `TOKEN_FOR_GITEA` and `CREDENTIALS_FOR_GITHUB`, run the workflow, read the validation result.
|
||||
- **Alternatives and failures:** malformed JSON, empty value, expired token, missing permission: the check names the category without printing the secret.
|
||||
- **Dependencies:** use case 1.
|
||||
|
||||
### UC candidate 3: Onboard a repository to standard workflows (P1)
|
||||
|
||||
- **Actor:** DevOps professional. **Goal:** a repository consumes the shared workflows.
|
||||
- **Trigger:** a new or existing repository needs standard automation.
|
||||
- **Preconditions:** Actions enabled; runner available; secrets possible to set.
|
||||
- **Success:** the first run in the repository passes using only the guide.
|
||||
- **Main flow:** choose scoped or copied delivery, set secrets, configure the push mirror, trigger a run.
|
||||
- **Alternatives and failures:** scoped workflows unsupported: use a copied template; no runner: see use case 7.
|
||||
- **Dependencies:** use cases 1 and 2; verification of scoped workflow support.
|
||||
|
||||
### UC candidate 4: Diagnose a failed workflow (P1)
|
||||
|
||||
- **Actor:** DevOps professional. **Goal:** find the cause of a failed run.
|
||||
- **Trigger:** a failed or missing run.
|
||||
- **Preconditions:** access to the run log.
|
||||
- **Success:** the cause is placed in one category: configuration, credentials, permissions, API response or runner.
|
||||
- **Main flow:** open the log, read the categorized message, look up the category in the guide, apply the fix, re-run.
|
||||
- **Alternatives and failures:** no run starts: check runner and trigger; message uncategorized: raise as a defect.
|
||||
- **Dependencies:** use cases 1 and 2.
|
||||
|
||||
### UC candidate 5: Run project quality checks (P2)
|
||||
|
||||
- **Actor:** software engineer. **Goal:** tests, lint, format and type checks run on every change.
|
||||
- **Trigger:** pull request or push.
|
||||
- **Preconditions:** shared workflow available; project onboarded.
|
||||
- **Success:** a status check reports pass or fail.
|
||||
- **Main flow:** the shared workflow detects inputs, runs the checks, reports the result.
|
||||
- **Alternatives and failures:** a check fails: the log names tool, file and message; unsupported language: workflow fails with a clear message.
|
||||
- **Dependencies:** use case 3; reusable workflow support.
|
||||
|
||||
### UC candidate 6: Release a reusable workflow (P2)
|
||||
|
||||
- **Actor:** DevOps professional. **Goal:** a validated, versioned workflow update that consumers can pin.
|
||||
- **Trigger:** a workflow change is ready.
|
||||
- **Preconditions:** validation checks exist; version scheme defined.
|
||||
- **Success:** a tag and changelog entry exist and pinned consumers are unaffected.
|
||||
- **Main flow:** run validation, update the changelog, tag, announce.
|
||||
- **Alternatives and failures:** validation fails: no tag; breaking change: new major version.
|
||||
- **Dependencies:** use case 1 (validation from MIL-001).
|
||||
|
||||
### UC candidate 7: Manage workflow runners (P2)
|
||||
|
||||
- **Actor:** DevOps professional (runner operator). **Goal:** workflows run on a supported runner.
|
||||
- **Trigger:** a job waits, fails to start, or a new label is needed.
|
||||
- **Preconditions:** access to the runner list.
|
||||
- **Success:** a runner with the required label and tools is online.
|
||||
- **Main flow:** compare the workflow's `runs-on` label with online runners, check prerequisites such as `python3`, fix or register a runner.
|
||||
- **Alternatives and failures:** runner offline: restart or replace; label missing: add the label or change the workflow.
|
||||
- **Dependencies:** use case 3.
|
||||
|
||||
### UC candidate 8: Consume a workflow update (P2)
|
||||
|
||||
- **Actor:** software engineer. **Goal:** adopt a new workflow version safely.
|
||||
- **Trigger:** a new release or a changelog notice.
|
||||
- **Preconditions:** releases are versioned and have a changelog.
|
||||
- **Success:** the project uses the new version and its pipeline still passes.
|
||||
- **Main flow:** read the changelog, change the pinned version in one branch, run, merge.
|
||||
- **Alternatives and failures:** breaking change: follow upgrade notes; failure: roll back to the previous pin.
|
||||
- **Dependencies:** use case 6.
|
||||
|
||||
### UC candidate 9: Review workflow results (P3)
|
||||
|
||||
- **Actor:** software engineer. **Goal:** understand a failed check and make it pass.
|
||||
- **Trigger:** a failed status check on a pull request.
|
||||
- **Preconditions:** a run exists.
|
||||
- **Success:** the next run passes.
|
||||
- **Main flow:** open the check, read the failing step, fix locally, push.
|
||||
- **Alternatives and failures:** the cause is infrastructure, not code: hand over to use case 4.
|
||||
- **Dependencies:** use case 5.
|
||||
|
||||
### UC candidate 10: Create a project workflow (P3)
|
||||
|
||||
- **Actor:** software engineer. **Goal:** compose shared workflows into a project pipeline.
|
||||
- **Trigger:** a project needs build and delivery steps.
|
||||
- **Preconditions:** shared workflows are documented with inputs.
|
||||
- **Success:** the project workflow runs the chosen shared workflows in order.
|
||||
- **Main flow:** pick workflows, set inputs, set triggers and permissions, test.
|
||||
- **Alternatives and failures:** missing input: the workflow fails with the input name.
|
||||
- **Dependencies:** use cases 5 and 8.
|
||||
|
||||
### UC candidate 11: Build and publish an artifact (P3)
|
||||
|
||||
- **Actor:** software engineer. **Goal:** a package or binary is built and published.
|
||||
- **Trigger:** a tag or a manual run.
|
||||
- **Preconditions:** destination decided; token with write access.
|
||||
- **Success:** the artifact is available at the destination with a version.
|
||||
- **Main flow:** build, test, package, publish, report the location.
|
||||
- **Alternatives and failures:** publish denied: permission failure; version exists: fail without overwrite.
|
||||
- **Dependencies:** use cases 5 and 6; destination decision (MIL-004 task 4).
|
||||
|
||||
### UC candidate 12: Publish a release (P3)
|
||||
|
||||
- **Actor:** software engineer. **Goal:** a release with validated version and notes.
|
||||
- **Trigger:** a version tag.
|
||||
- **Preconditions:** changelog entry exists.
|
||||
- **Success:** a release page with notes from the changelog.
|
||||
- **Main flow:** validate the version, build notes, create the release.
|
||||
- **Alternatives and failures:** invalid version or missing notes: fail. This overlaps with use cases 6 and 11 and should be merged with one of them when the use cases are written.
|
||||
- **Dependencies:** use cases 6 and 11.
|
||||
|
||||
---
|
||||
|
||||
[BC-001]: ./business-case.md
|
||||
[SA-001]: ./stakeholder-analysis.md
|
||||
[MIL-001]: ./milestones/mil-001-stabilise-metadata-sync.md
|
||||
[MIL-002]: ./milestones/mil-002-onboarding-and-operations.md
|
||||
[MIL-003]: ./milestones/mil-003-quality-checks.md
|
||||
[MIL-004]: ./milestones/mil-004-versioned-release.md
|
||||
[Milestone MIL-001]: https://git.tirsystem.com/TirSystem/github-action/milestone/18
|
||||
[Milestone MIL-002]: https://git.tirsystem.com/TirSystem/github-action/milestone/19
|
||||
[Milestone MIL-003]: https://git.tirsystem.com/TirSystem/github-action/milestone/20
|
||||
[Milestone MIL-004]: https://git.tirsystem.com/TirSystem/github-action/milestone/21
|
||||
@@ -0,0 +1,103 @@
|
||||
# Stakeholder Analysis: TirSystem Reusable Workflows
|
||||
|
||||
## Metadata
|
||||
| Key | Value |
|
||||
| --- | --- |
|
||||
| ID | SA-001 |
|
||||
| CrossReference | [BC-001] |
|
||||
|
||||
## Version History
|
||||
| Date | Status | Author | Reviewer | Change | Commit |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 2026-10-03 | Rejected | Jens Tirsvad Nielsen | S01 | Initial version | pending |
|
||||
| 2026-10-03 | Accepted | Jens Tirsvad Nielsen | S01 | Added S05 Michael Kragh (DevOps owner, maintainer, cyber security)<br>S01 stays Product Owner and maintainer | pending |
|
||||
|
||||
---
|
||||
|
||||
## Purpose
|
||||
|
||||
This analysis identifies who depends on, operates or is affected by the
|
||||
TirSystem reusable workflow collection, so that scope, priorities and
|
||||
communication follow real needs. It uses a power/interest grid and maps
|
||||
each concern to a FURPS+ attribute.
|
||||
|
||||
## Stakeholder Summary Table
|
||||
|
||||
| ID | Name | Role/Title | Organization | Power Level | Interest Level | Quadrant | Primary Concern (Business Language) |
|
||||
| --- | --- | --- | --- | --- | --- | --- | --- |
|
||||
| S01 | Jens Tirsvad Nielsen | Product Owner and maintainer | TirSystem | HIGH | HIGH | Manage Closely | TirSystem repositories get the same automation without per-repository copy and paste, and scope follows business needs. |
|
||||
| S02 | Software engineers on TirSystem projects | Consumers of shared workflows (group) | TirSystem | MEDIUM | HIGH | Keep Informed | Quality checks, builds and releases work out of the box, and failures say what to fix. |
|
||||
| S03 | Gitea instance and runner operator | Platform operator (group; holders to be confirmed) | TirSystem | HIGH | MEDIUM | Keep Satisfied | Workflows run only on supported runners, use least-privilege tokens and never leak secrets. |
|
||||
| S04 | GitHub mirror audience | Visitors of the GitHub push mirrors (group) | Public | LOW | LOW | Monitor | The mirror shows the same description and topics as the primary Gitea repository. |
|
||||
| S05 | Michael Kragh | DevOps owner, maintainer and cyber security | TirSystem | HIGH | HIGH | Manage Closely | Shared automation is maintainable, and credentials, tokens and workflow permissions meet security requirements. |
|
||||
|
||||
## Power/Interest Classification Rationale
|
||||
|
||||
- **Manage Closely (S01):** decides scope and priorities, accepts documents and co-maintains the workflows.
|
||||
- **Manage Closely (S05):** owns and co-maintains the workflows and is the security
|
||||
authority; credential, token and permission design cannot be accepted
|
||||
without this role.
|
||||
- **Keep Informed (S02):** does not decide scope, but adoption by these
|
||||
engineers is the success measure; a breaking workflow change hits them first.
|
||||
- **Keep Satisfied (S03):** controls runners, secrets and Gitea features
|
||||
(Actions, scoped workflows) that the workflows rely on; an incompatible
|
||||
change can be blocked here.
|
||||
- **Monitor (S04):** passive consumers of mirror metadata; no action needed
|
||||
beyond keeping the metadata correct.
|
||||
|
||||
## Primary Concerns and FURPS+ Mapping
|
||||
|
||||
| ID | Concern | FURPS+ attribute |
|
||||
| --- | --- | --- |
|
||||
| S01 | One reviewed source of truth for shared automation | Supportability |
|
||||
| S01 | Documentation never claims behavior that is not implemented | Functionality |
|
||||
| S05 | One maintainable, reviewed source of truth for shared automation | Supportability |
|
||||
| S05 | Least-privilege tokens, no secret in logs, pinned and reviewed workflow code | Functionality (security) |
|
||||
| S02 | Clear failure messages that name the cause | Usability |
|
||||
| S02 | Pinned, versioned workflows with a changelog | Supportability |
|
||||
| S03 | Least-privilege tokens and no secret in logs | Functionality (security) |
|
||||
| S03 | Declared runner labels and prerequisites | Supportability |
|
||||
| S04 | Correct, current mirror description and topics | Functionality |
|
||||
|
||||
## Communication Requirements
|
||||
|
||||
| ID | Channel | Frequency | Deliverable | Phase / Milestone |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| S01 | Pull request review and chat | Per milestone | Accepted documents, issue list | All milestones |
|
||||
| S02 | README and changelog in the repository | Per workflow release | Usage guide, upgrade notes | MIL-002 onward |
|
||||
| S05 | Pull request review and issue tracker | Per change to workflows, credentials or runners | Security-reviewed changes, token scope list | All milestones |
|
||||
| S03 | Issue tracker | Per change to credentials or runners | Prerequisites and token scope list | MIL-001, MIL-002 |
|
||||
| S04 | None (mirror metadata only) | Daily sync | Updated description and topics | MIL-001 |
|
||||
|
||||
## Conflicting Interests and Mitigations
|
||||
|
||||
| Conflict | Stakeholders | Mitigation |
|
||||
| --- | --- | --- |
|
||||
| Fast adoption of new workflow behavior versus stable consuming repositories | S01, S02 | Version every shared workflow and document breaking changes before release (MIL-004). |
|
||||
| Convenient broad tokens versus least privilege | S02, S03 | Document the minimum token scope per workflow and validate it before use (MIL-002). |
|
||||
| Delivery speed versus security review | S01, S05 | S05 reviews every change to credentials, tokens, permissions and workflow code before the milestone is accepted by S01. |
|
||||
| Operator role holder not yet identified | S03, S05 | Confirm who operates runners and the Gitea instance; until then S05 is the contact. |
|
||||
|
||||
## Traceability Analysis
|
||||
|
||||
### Business Goal Alignment
|
||||
|
||||
| Stakeholder | Concern | Business Case objective |
|
||||
| --- | --- | --- |
|
||||
| S01 | One source of truth for shared automation | O1, O2 |
|
||||
| S02 | Clear failures, versioned updates | O3, O4 |
|
||||
| S03 | Least privilege, runner compatibility | O3 |
|
||||
| S05 | Maintainable shared automation, security | O1, O2, O3 |
|
||||
| S04 | Correct mirror metadata | O1 |
|
||||
|
||||
Objectives are defined in [BC-001]. Use-case mapping: S05 and S03 are the
|
||||
primary actors of the DevOps use cases, S02 of the software engineer use
|
||||
cases; both are listed in the Project Plan appendix.
|
||||
|
||||
## Sign-Off
|
||||
|
||||
Pending review by S01 and S05.
|
||||
|
||||
---
|
||||
|
||||
[BC-001]: ./business-case.md
|
||||
Submodule
+1
Submodule framework added at 14d221ec1c
@@ -1,161 +0,0 @@
|
||||
name: Sync GitHub mirror metadata
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: "17 3 * * *"
|
||||
|
||||
jobs:
|
||||
sync-metadata:
|
||||
permissions:
|
||||
contents: read
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Sync description and topics
|
||||
env:
|
||||
GITEA_API_URL: ${{ gitea.api_url }}
|
||||
GITEA_CREDENTIALS: ${{ secrets.TOKEN_FOR_GITEA }}
|
||||
SOURCE_REPOSITORY: ${{ gitea.repository }}
|
||||
GITHUB_CREDENTIALS: ${{ secrets.CREDENTIALS_FOR_GITHUB }}
|
||||
run: |
|
||||
python3 - <<'PY'
|
||||
import json
|
||||
import os
|
||||
import urllib.error
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
|
||||
def request_json(url, method="GET", headers=None, body=None):
|
||||
request = urllib.request.Request(
|
||||
url,
|
||||
data=json.dumps(body).encode("utf-8") if body is not None else None,
|
||||
headers=headers or {},
|
||||
method=method,
|
||||
)
|
||||
try:
|
||||
with urllib.request.urlopen(request, timeout=30) as response:
|
||||
content = response.read()
|
||||
return json.loads(content) if content else None
|
||||
except urllib.error.HTTPError as error:
|
||||
raise RuntimeError(
|
||||
f"API request failed with HTTP {error.code} ({error.reason})"
|
||||
) from None
|
||||
|
||||
raw_credentials = os.environ.get("GITHUB_CREDENTIALS", "").strip()
|
||||
if not raw_credentials:
|
||||
raise RuntimeError("CREDENTIALS_FOR_GITHUB is missing or empty.")
|
||||
|
||||
try:
|
||||
credentials = json.loads(raw_credentials)
|
||||
except json.JSONDecodeError:
|
||||
raise RuntimeError(
|
||||
"CREDENTIALS_FOR_GITHUB must contain valid JSON."
|
||||
) from None
|
||||
|
||||
if not isinstance(credentials, dict):
|
||||
raise RuntimeError(
|
||||
"CREDENTIALS_FOR_GITHUB must be a JSON object."
|
||||
)
|
||||
|
||||
github_token = credentials.get("GITHUB_PAT")
|
||||
if not isinstance(github_token, str) or not github_token.strip():
|
||||
raise RuntimeError(
|
||||
"CREDENTIALS_FOR_GITHUB must contain a non-empty GITHUB_PAT."
|
||||
)
|
||||
|
||||
raw_gitea_credentials = os.environ.get("GITEA_CREDENTIALS", "").strip()
|
||||
if not raw_gitea_credentials:
|
||||
raise RuntimeError("TOKEN_FOR_GITEA is missing or empty.")
|
||||
|
||||
try:
|
||||
gitea_credentials = json.loads(raw_gitea_credentials)
|
||||
except json.JSONDecodeError:
|
||||
gitea_token = raw_gitea_credentials
|
||||
else:
|
||||
if isinstance(gitea_credentials, dict):
|
||||
gitea_token = gitea_credentials.get("GITEA_TOKEN")
|
||||
elif isinstance(gitea_credentials, str):
|
||||
gitea_token = gitea_credentials
|
||||
else:
|
||||
gitea_token = None
|
||||
|
||||
if not isinstance(gitea_token, str) or not gitea_token.strip():
|
||||
raise RuntimeError(
|
||||
"TOKEN_FOR_GITEA must contain a non-empty GITEA_TOKEN."
|
||||
)
|
||||
source_owner, separator, source_repo = os.environ[
|
||||
"SOURCE_REPOSITORY"
|
||||
].partition("/")
|
||||
if not separator or not source_owner or not source_repo:
|
||||
raise RuntimeError("Could not determine the Gitea source repository.")
|
||||
|
||||
gitea_api_url = os.environ["GITEA_API_URL"].rstrip("/")
|
||||
source_url = f"{gitea_api_url}/repos/{source_owner}/{source_repo}"
|
||||
gitea_headers = {
|
||||
"Authorization": f"token {gitea_token.strip()}",
|
||||
"Accept": "application/json",
|
||||
}
|
||||
source = request_json(source_url, headers=gitea_headers)
|
||||
mirrors = request_json(
|
||||
f"{source_url}/push_mirrors",
|
||||
headers=gitea_headers,
|
||||
)
|
||||
if not isinstance(mirrors, list):
|
||||
raise RuntimeError("Gitea returned an invalid push mirror list.")
|
||||
|
||||
github_targets = []
|
||||
for mirror in mirrors:
|
||||
remote_address = mirror.get("remote_address", "")
|
||||
if remote_address.startswith("git@github.com:"):
|
||||
mirror_path = remote_address.split(":", 1)[1]
|
||||
else:
|
||||
parsed_remote = urllib.parse.urlsplit(remote_address)
|
||||
if parsed_remote.hostname != "github.com":
|
||||
continue
|
||||
mirror_path = parsed_remote.path.lstrip("/")
|
||||
|
||||
mirror_path = mirror_path.removesuffix(".git").strip("/")
|
||||
path_parts = mirror_path.split("/")
|
||||
if len(path_parts) != 2 or not all(path_parts):
|
||||
raise RuntimeError(
|
||||
"Could not determine the GitHub owner and repository "
|
||||
"from a configured push mirror."
|
||||
)
|
||||
github_targets.append(tuple(path_parts))
|
||||
|
||||
if len(github_targets) != 1:
|
||||
raise RuntimeError(
|
||||
"Expected exactly one GitHub push mirror for this repository; "
|
||||
f"found {len(github_targets)}."
|
||||
)
|
||||
|
||||
github_owner, github_repo = github_targets[0]
|
||||
github_api_url = (
|
||||
"https://api.github.com/repos/"
|
||||
f"{urllib.parse.quote(github_owner, safe='')}/"
|
||||
f"{urllib.parse.quote(github_repo, safe='')}"
|
||||
)
|
||||
github_headers = {
|
||||
"Authorization": f"Bearer {github_token.strip()}",
|
||||
"Accept": "application/vnd.github+json",
|
||||
"X-GitHub-Api-Version": "2022-11-28",
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
|
||||
request_json(
|
||||
github_api_url,
|
||||
method="PATCH",
|
||||
headers=github_headers,
|
||||
body={"description": source.get("description") or ""},
|
||||
)
|
||||
request_json(
|
||||
f"{github_api_url}/topics",
|
||||
method="PUT",
|
||||
headers=github_headers,
|
||||
body={"names": source.get("topics") or []},
|
||||
)
|
||||
|
||||
print(f"Synced description and topics to {github_owner}/{github_repo}.")
|
||||
PY
|
||||
Reference in New Issue
Block a user