diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..1a43fa8 --- /dev/null +++ b/.env.example @@ -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= diff --git a/.gitea/scoped_workflows/sync-github-metadata.yml b/.gitea/scoped_workflows/sync-github-metadata.yml index 9b19961..9ed9e2e 100644 --- a/.gitea/scoped_workflows/sync-github-metadata.yml +++ b/.gitea/scoped_workflows/sync-github-metadata.yml @@ -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: diff --git a/.gitea/workflows/sync-github-metadata.yml b/.gitea/workflows/sync-github-metadata.yml deleted file mode 100644 index 9562d0b..0000000 --- a/.gitea/workflows/sync-github-metadata.yml +++ /dev/null @@ -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 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0173734 --- /dev/null +++ b/.gitignore @@ -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/ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..a759dc9 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "framework"] + path = framework + url = ssh://git@git.tirsystem.com:10022/TirSystem/SQA-QC-Framework.git diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..aaa1f03 --- /dev/null +++ b/AGENTS.md @@ -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 `. +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. diff --git a/README.md b/README.md index 8dfc5f1..83b5c6a 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/docs/artifact-registry.md b/docs/artifact-registry.md new file mode 100644 index 0000000..243d055 --- /dev/null +++ b/docs/artifact-registry.md @@ -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. diff --git a/docs/business-case.md b/docs/business-case.md new file mode 100644 index 0000000..24dadd2 --- /dev/null +++ b/docs/business-case.md @@ -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 diff --git a/docs/milestones/mil-001-stabilise-metadata-sync.md b/docs/milestones/mil-001-stabilise-metadata-sync.md new file mode 100644 index 0000000..1e1f147 --- /dev/null +++ b/docs/milestones/mil-001-stabilise-metadata-sync.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 diff --git a/docs/milestones/mil-002-onboarding-and-operations.md b/docs/milestones/mil-002-onboarding-and-operations.md new file mode 100644 index 0000000..c3094e8 --- /dev/null +++ b/docs/milestones/mil-002-onboarding-and-operations.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 diff --git a/docs/milestones/mil-003-quality-checks.md b/docs/milestones/mil-003-quality-checks.md new file mode 100644 index 0000000..6c523a1 --- /dev/null +++ b/docs/milestones/mil-003-quality-checks.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 diff --git a/docs/milestones/mil-004-versioned-release.md b/docs/milestones/mil-004-versioned-release.md new file mode 100644 index 0000000..819402a --- /dev/null +++ b/docs/milestones/mil-004-versioned-release.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 diff --git a/docs/project-plan.md b/docs/project-plan.md new file mode 100644 index 0000000..b408d79 --- /dev/null +++ b/docs/project-plan.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
Added use case candidates and prioritisation appendix | pending | +| 2026-10-03 | Accepted | Jens Tirsvad Nielsen | S01 | Owner of every gateway is S05
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 diff --git a/docs/stakeholder-analysis.md b/docs/stakeholder-analysis.md new file mode 100644 index 0000000..2f68d49 --- /dev/null +++ b/docs/stakeholder-analysis.md @@ -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)
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 diff --git a/framework b/framework new file mode 160000 index 0000000..14d221e --- /dev/null +++ b/framework @@ -0,0 +1 @@ +Subproject commit 14d221ec1cfd3966611a3eff6a607ef1964526c2 diff --git a/sync-github-metadata.yml b/sync-github-metadata.yml deleted file mode 100644 index 9b19961..0000000 --- a/sync-github-metadata.yml +++ /dev/null @@ -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