Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions AGENTS.md

Large diffs are not rendered by default.

4 changes: 1 addition & 3 deletions CODESTYLE.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,9 +187,7 @@ Note: Code snippets are illustrative examples only. Replace namespaces/types to
- YAML files: 2 spaces
- JSON files: 4 spaces

5. **Line endings**
- C#, XML, YAML, JSON, Windows scripts: CRLF
- Linux scripts (`.sh`): LF
5. **Line endings**: not specified here - governed per repo by `.editorconfig` / `.gitattributes` per the [AGENTS.md][agents] "Line Endings" section.

6. **`#region`**: Do not use regions. Prefer logical file/folder/namespace organization.
7. **Member ordering (StyleCop SA1201)**: const -> static readonly -> static fields -> instance readonly fields -> instance fields -> constructors -> public (events -> properties -> indexers -> methods -> operators) -> non-public in same order -> nested types
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ This repo is the single home for the shared rules the fleet follows, a machine-r

ProjectTemplate follows the same model it documents, and audits its own rules against itself (it classifies as the source-only project type in [WORKFLOW.md][workflow]).

- **Branching.** Persistent `main` and `develop`, each with its own ruleset. Commit on feature branches only. Feature branch to `develop` is squash-merged; `develop` to `main` is a merge commit. `develop` is forward-only (no `main -> develop` back-merges). See [AGENTS.md "Branching Model"][agents-branching-model].
- **Branching.** Persistent `main` and `develop`, each with its own ruleset. This repo uses the default `release` workflow model: commit on feature branches only, feature branch to `develop` is squash-merged, `develop` to `main` is a merge commit, and `develop` is forward-only (no `main -> develop` back-merges). Live-service config repos instead use the `operational` model (registry `workflowModel`) - direct signed commits to `develop`, promoted to `main` by an occasional PR. See [AGENTS.md "Branching Model"][agents-branching-model].
- **CI is lint-only.** There is no build or unit test; the PR gate runs markdownlint, cspell, JSON validation (`jq` parses `registry/`, `spec/`, and `repo-config/`, plus the `spec/validate.py` cross-reference and shape checks), and actionlint, and exposes the ruleset-bound `Check pull request workflow status job` aggregator. The same lint configs (`.markdownlint-cli2.jsonc`, `cspell.json`) drive the editor extensions, the CLI, and CI.
- **Review loop.** Every PR is reviewed by GitHub Copilot; the agent drives the review loop to green and merges only with explicit maintainer permission. See [AGENTS.md "PR Review Etiquette"][agents-pr-review-etiquette].
- **Release.** A `develop -> main` merge is promoted through a GitHub release (tag plus a source zip, README, and LICENSE); versioning is NBGV-driven from [version.json][version]. See [WORKFLOW.md][workflow].
Expand Down
15 changes: 15 additions & 0 deletions WORKFLOW.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,25 @@ Prescriptive style/legibility rules. Cheap to check, necessary but not sufficien

### Branch Model

Two workflow models, set per repo by the registry `workflowModel` field. `release` (default) is the feature-branch pipeline this document specifies:

```mermaid
flowchart LR
feature[feature branch] -->|squash| develop
develop -->|merge commit| main
main -.->|no back-merge| develop
```

`operational` repos (live-service config; `workflowModel: operational`) commit directly to `develop` and promote a known-good snapshot to `main` via an occasional PR:

```mermaid
flowchart LR
edit[direct signed commit] -->|advisory CI| develop
develop -->|merge commit, enforced lint CI| main
```

Their CI is lint/validation only (editorconfig/EOL plus domain linters - Home Assistant or ESPHome config validation, a firmware build - **no unit tests**), so the D-guarantees below that assume a build/test pipeline are **N/A** exactly as for `source-only` (Section 6). What binds: the promotion gate - the `develop -> main` PR must pass the required `Check pull request workflow status job` - and the source-only release on manual dispatch (`releaseTrigger: dispatch-only`; tag + source zip). Branch-model rulesets are specified in [AGENTS.md "Branching Model"][agents-branching-model] and [repo-config/README.md][repo-config-readme], not here.

### Two Layers: Orchestration vs Build

- **Orchestration** is generic and forms the standardization baseline **at the job level**: the single-branch publisher, the `get-version`, `validate-release`, and `github-release` jobs, the date-badge job, and the `changes -> smoke-build -> aggregator` shape of the PR workflow. These job *bodies* should not need per-repo edits.
Expand Down Expand Up @@ -263,11 +275,14 @@ Each type maps the *applicable* S-scenarios onto its targets; the differences ar
- **Docker image.** The leaf pushes the default branch multi-arch (amd64+arm64) and any other branch `amd64`-only, with a per-branch registry buildcache (`buildcache-<branch>`; a multi-image repo adds a per-image tag) (`cache-to` only the built branch and only on push, `cache-from` both branches); no `release-asset-*`, so a Docker-only repo's caller passes `expect_release_assets: false`; the readme (`peter-evans/dockerhub-description`, `DOCKER_HUB_ACCESS_TOKEN`) and date-badge jobs run **only** when the default branch publishes; the docker-readme task validates `repositories` XOR `manifest`+`manifest-jq` and a multi-image repo derives its publish matrix from the manifest. Docker **always re-pushes** the image, independently of a skipped release-create (S9). A **wrapper** repo tracks an upstream release: the upstream tracker writes a `name -> version` state file and the merge-bot auto-merges the bump PR (S11), and the leaf MUST read that file for the immutable tag instead of `SemVer2` (the template ships the tracker but not this consumer wiring). Test: S7 default leg pushes `latest` + the version tag and updates readme/badge; non-default pushes the develop tag (amd64 only); S9 still re-pushes; S11 ships the bumped upstream version next publish. 5C Docker probe needs `DOCKER_HUB_*` secrets and same-repo (not fork) runs.
- **Data / asset library.** A single new leaf: validate -> zip -> upload `release-asset-<branch>-library` (`retention-days: 1`, upload gated `!smoke` - mirror the nugetlibrary leaf's shape). Because the template has no such leaf, you **add a target** (D6.4): a new `enable_library` input + `build-library` job + `github-release` `needs:` entry in the release task, and a `library` paths-filter entry + `changes` output + `smoke-build` enable-forward in the PR workflow (without it, D1.1 never smoke-builds the library). Keep `expect_release_assets: true` (it has a file target, unlike Docker). The .NET `unit-test` job is replaced by a type-appropriate validator with the aggregator **and** `smoke-build` both re-pointed to it (D1.2/D1.5); `version.json` + the NBGV `get-version` step are retained (they own the tag). Test: S1 smoke runs validate+zip and uploads nothing; S7 attaches the zip, prerelease on the non-default leg; S9 on a *scheduled* re-run release-create + asset-delete skip (the existing zip is untouched, no registry push), while a `workflow_dispatch` re-run **refreshes** the release and re-runs the asset-delete (the asset is re-uploaded then re-deleted). N/A: the nuget/pypi/docker/executable 5A addenda and their scenario clauses.
- **Source-only / no build.** No package/image leaf: remove all four `build-*` jobs and their `github-release` `needs:` entries (leaving `get-version -> validate-release -> github-release`, which fires on `github && !smoke`), and the caller passes `expect_release_assets: false` so the release is tag + source zip + README + LICENSE with no asset download. With no target the paths-filter matches nothing, so `smoke-build` is **structurally always skipped** - validation is carried solely by the (replaced, non-.NET) validation job that the aggregator and `smoke-build`'s own `needs:` must both point at (D1.2; or drop the never-running `smoke-build` job). NBGV and `version.json` are still retained (they own the tag). Applicable scenarios: S1 (validation only), S5/S6 (publish gating), S7 (tag-only release), S8 (dispatch guard), S9 (no-op republish), S10 (classification gate). N/A: S2-S4 (assume a smoke-built target), the artifact-lifecycle and registry clauses of S7/S9, the D5/D6 artifact items, and all per-type 5A addenda - recorded N/A, not failed.
- **Operational (workflow model, not a build target).** A `workflowModel: operational` repo layers a direct-commit `develop` onto the **source-only** release shape (above). Two workflows: (1) a **lint/validation** PR workflow feeding the required `Check pull request workflow status job` - the generic linters (editorconfig/EOL, markdownlint, cspell, actionlint) plus a domain validator (Home Assistant `hass --script check_config`, `esphome config`, a firmware build), **no unit tests**; its triggers differ from the `release` template - `push` to `develop` (advisory feedback on direct commits) plus `pull_request` to `main` (the enforced promotion gate) plus `workflow_dispatch`. (2) the standard **source-only publisher** on `workflow_dispatch` only (`releaseTrigger: dispatch-only`): NBGV + `version.json` own the tag, and a manual dispatch cuts a GitHub release (tag + source zip + README + LICENSE, `expect_release_assets: false`). Applicable scenarios: S1 (validation) on the promotion PR, plus the source-only release set - S7 (tag-only release), S8 (dispatch guard), S9 (no-op republish), S10 (classification). N/A: the auto-publish paths (S5/S6 bot-push and schedule - operational repos have neither) and every build/registry scenario. See the branch-model note in Section 3 and [AGENTS.md "Branching Model"][agents-branching-model].

<!-- Workflow -->

[workflows]: ./.github/workflows/

<!-- Repo -->

[agents-branching-model]: ./AGENTS.md#branching-model
[codestyle]: ./CODESTYLE.md
[repo-config-readme]: ./repo-config/README.md
3 changes: 3 additions & 0 deletions cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"debuglevel",
"devcontainer",
"distros",
"dnsmasq",
"dockerbuild",
"Dockerfiles",
"dockerhub",
Expand All @@ -46,13 +47,15 @@
"dryrun",
"Emby",
"envsubst",
"esphome",
"extensionless",
"fanaticpythoner",
"finalizers",
"Genericize",
"gpgsign",
"gruntfuggly",
"HACS",
"hass",
"hatchling",
"heredocs",
"homeassistant",
Expand Down
45 changes: 27 additions & 18 deletions registry/repos.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
"owner": "ptr727",
"defaults": {
"groundTruthBranch": "main",
"releaseTrigger": "two-phase"
"releaseTrigger": "two-phase",
"workflowModel": "release"
},
"repos": [
{
Expand Down Expand Up @@ -156,17 +157,19 @@
"driftNotes": ["Docker image wrapping upstream Nx products; C# (CreateMatrix) is the codegen generator, not a shipped package (IsPackable=false, no nuget push).", "Release is the two-phase model (weekly schedule + workflow_dispatch publish; ordinary merges do not) plus an extra Make/Matrix.json path-scoped push that republishes when the codegen version pin bumps.", "Docker Hub README published per-image via a Matrix.json-derived matrix.", "Branch hygiene: 3 stale Dependabot nuget branches (PRs closed/superseded) linger, safe to delete; main+develop otherwise clean after the 2026-07 sweep."]
},
{
"name": "HomeAutomation",
"url": "https://github.com/ptr727/HomeAutomation",
"name": "HomeAutomation-Config",
"url": "https://github.com/ptr727/HomeAutomation-Config",
"status": "cataloged",
"types": ["source-only"],
"groundTruthBranch": "main",
"groundTruthBranch": "develop",
"workflowModel": "operational",
"lineEndings": "lf",
"hasDevelop": true,
"publish": [],
"publish": [{ "target": "github-release", "mechanism": "none" }],
"requiredSecrets": [],
"consumerModel": "pull",
"releaseTrigger": "none",
"driftNotes": ["Maintainer config/ops repo (docker-compose stacks, lifecycle scripts, Firewalla configs); in scope, file-version-history only.", "Private; README self-flags previously-committed secrets - secrets-hygiene concern."]
"releaseTrigger": "dispatch-only",
"driftNotes": ["Maintainer config/ops repo (docker-compose stacks, lifecycle scripts, Firewalla configs); Linux-consumed on the Proxmox host, so lineEndings lf.", "Renamed from HomeAutomation for fleet naming consistency (config repos are *-Config). The Vantage controller config is split out to its own Windows/CRLF Vantage-Config repo, not carried here - strip the legacy Vantage/ subtree during onboarding.", "Private; README self-flags previously-committed secrets - secrets-hygiene concern.", "Operational rollout pending: lint CI feeding the required check, dispatch-only source-release scaffolding (version.json + NBGV get-version + publish-release.yml, tag + source zip), and develop-as-ground-truth adoption."]
},
{
"name": "KiCadLibrary",
Expand Down Expand Up @@ -199,26 +202,30 @@
"url": "https://github.com/ptr727/ESPHome-Config",
"status": "cataloged",
"types": ["source-only"],
"groundTruthBranch": "main",
"groundTruthBranch": "develop",
"workflowModel": "operational",
"lineEndings": "lf",
"hasDevelop": true,
"publish": [],
"publish": [{ "target": "github-release", "mechanism": "none" }],
"requiredSecrets": [],
"consumerModel": "pull",
"releaseTrigger": "none",
"driftNotes": ["ESPHome device config YAML consumed by the cataloged ESPHome-NonRoot image at runtime; distinct from that Docker repo.", "No CI/publish; dependabot covers only the devcontainers ecosystem."]
"releaseTrigger": "dispatch-only",
"driftNotes": ["ESPHome device config YAML consumed by the cataloged ESPHome-NonRoot image at runtime; distinct from that Docker repo.", "Operational rollout pending: an esphome-config validation job feeding the required check, dispatch-only source-release scaffolding (version.json + NBGV get-version + publish-release.yml, tag + source zip), and develop-as-ground-truth adoption. Dependabot currently covers only the devcontainers ecosystem."]
},
{
"name": "HomeAssistant-Config",
"url": "https://github.com/ptr727/HomeAssistant-Config",
"status": "cataloged",
"types": ["source-only"],
"groundTruthBranch": "master",
"groundTruthBranch": "main",
"workflowModel": "operational",
"lineEndings": "lf",
"hasDevelop": true,
"publish": [],
"publish": [{ "target": "github-release", "mechanism": "none" }],
"requiredSecrets": [],
"consumerModel": "pull",
"releaseTrigger": "none",
"driftNotes": ["Home Assistant CONFIGURATION (configuration.yaml + automations/blueprints), NOT a HACS integration (no custom_components/manifest.json, no hacs.json).", "Non-conformant: ground-truth branch is 'master', not 'main' - rename to converge.", "Private; deployed by git pull into the HA config dir."]
"releaseTrigger": "dispatch-only",
"driftNotes": ["Home Assistant CONFIGURATION (configuration.yaml + automations/blueprints), NOT a HACS integration (no custom_components/manifest.json, no hacs.json).", "Operational rollout pending: 'master' renamed to 'main' and 'develop' created (done); develop is behind main by 2 commits - fast-forward develop up to main so it holds the live content, then flip groundTruthBranch to develop. Also pending: lint CI (a Home Assistant config-check feeding the required check) and dispatch-only source-release scaffolding (version.json + NBGV get-version + publish-release.yml, tag + source zip).", "Private; deployed by git pull into the HA config dir."]
},
{
"name": "DevKitCIoT",
Expand Down Expand Up @@ -278,12 +285,14 @@
"status": "cataloged",
"types": ["source-only"],
"groundTruthBranch": "develop",
"workflowModel": "operational",
"lineEndings": "crlf",
"hasDevelop": true,
"publish": [],
"publish": [{ "target": "github-release", "mechanism": "none" }],
"requiredSecrets": [],
"consumerModel": "pull",
"releaseTrigger": "none",
"driftNotes": ["Maintainer config/asset archive (Vantage InFusion) with vendored binaries (MSI/7z/PDF) and versioned project snapshots; in scope as a config repo.", "The lone FindInFile C# helper is incidental, not a governed artifact.", "The develop branch now holds the ground truth (README/tagline edits) while main is stale - converge. No CI/governance scaffolding (no .github/workflows)."]
"releaseTrigger": "dispatch-only",
"driftNotes": ["Vantage InFusion / Design Center controller config edited on Windows (UTF-8 CRLF .dc XML, really special XML), so lineEndings crlf.", "Recreated lean and single-platform: Design Center is freely available, so no installer archives are kept; split out of HomeAutomation-Config. Being repopulated from the Windows editing host - operational onboarding (baseline, lint CI, dispatch-only publisher, rulesets, develop/main) pending once content lands."]
},
{
"name": "HolidayLights",
Expand Down
14 changes: 13 additions & 1 deletion registry/repos.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
"additionalProperties": false,
"properties": {
"groundTruthBranch": { "type": "string" },
"releaseTrigger": { "$ref": "#/$defs/releaseTrigger" }
"releaseTrigger": { "$ref": "#/$defs/releaseTrigger" },
"workflowModel": { "$ref": "#/$defs/workflowModel" }
}
},
"repos": {
Expand All @@ -23,11 +24,20 @@
},
"$defs": {
"releaseTrigger": { "enum": ["two-phase", "publish-on-merge", "dispatch-only", "none"] },
"workflowModel": { "enum": ["release", "operational"] },
"lineEndings": { "enum": ["lf", "crlf"] },
"mechanism": { "enum": ["oidc", "static-secret", "none"] },
"target": { "enum": ["nuget", "pypi", "docker", "github-release"] },
"repo": {
"type": "object",
"required": ["name", "url", "status"],
"allOf": [
{
"$comment": "An operational repo must declare its line endings (release repos use the fleet CRLF default).",
"if": { "properties": { "workflowModel": { "const": "operational" } }, "required": ["workflowModel"] },
"then": { "required": ["lineEndings"] }
}
],
"additionalProperties": false,
"properties": {
"name": { "type": "string" },
Expand All @@ -36,6 +46,8 @@
"types": { "type": "array", "items": { "type": "string" } },
"classificationPending": { "type": "boolean" },
"groundTruthBranch": { "type": "string" },
"workflowModel": { "$ref": "#/$defs/workflowModel" },
"lineEndings": { "$ref": "#/$defs/lineEndings" },
"hasDevelop": { "type": "boolean" },
"publish": {
"type": "array",
Expand Down
Loading