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
1 change: 1 addition & 0 deletions WORKFLOW.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ The required behaviors, organized by domain. Each is a **MUST**, stated as input
- **D1.3 Smoke never publishes and never uploads.** Input: `smoke: true`. Output: full compile/lint/test, but no registry/image push, no release, and **no** artifact uploads (every `upload-artifact`, including any aggregation job, is gated `!smoke`). *Prevents: a PR publishing; orphaned artifacts churning the storage quota.*
- **D1.4 Workflow-file changes are not smoke-built.** Input: a PR changing only `.github/workflows/**`. Output: the paths-filter excludes workflow files, so smoke-build skips. *Implication: there is no CI workflow-lint; lint workflow edits locally (actionlint).*
- **D1.5 One required aggregator gates merge.** Input: any PR. Output: a single aggregator job must **succeed**, `needs:` the changes job and the validation job, treat a **skipped** smoke build as pass, and **block** on `failure`/`cancelled`. Its name is ruleset-bound: the job `name:` and the ruleset `context:` are the same string and MUST be renamed together, never independently. *Prevents: a paths-filter error letting a target-changing PR merge unbuilt.*
- **D1.6 Coverage is reported to Codecov (C# and Python).** Input: a C# or Python repo's validation/test job. Output: tests run with coverage collection (`dotnet test --collect:"XPlat Code Coverage"` or `pytest --cov-report=xml`) and a `codecov/codecov-action` step uploads it, **best-effort** (`continue-on-error` and/or `fail_ci_if_error: false`, so a Codecov outage or an absent token never reds the gate); `CODECOV_TOKEN` lives in the repo's **actions** secret store and reaches the reusable validator via `secrets: inherit`. Required for **every** C# and Python repo that has tests (see `spec/secrets.json` `typeMechanisms`). *Prevents: coverage silently going unreported; a stale, unused token.*

### D2 - Input/State Validation at Entry

Expand Down
6 changes: 4 additions & 2 deletions spec/project-types.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
"requiredFiles": [".editorconfig", ".gitattributes"],
"checks": [
{ "id": "csharp.editorconfig.ruleblock", "verdict": "letter", "assert": ".editorconfig carries the shared [*.cs] plus ReSharper rule block.", "intentRef": "CODESTYLE.md" },
{ "id": "csharp.analyzers.zerowarnings", "verdict": "intent", "assert": "Analyzer severities are enforced; warnings are not relaxed or suppressed wholesale.", "intentRef": "CODESTYLE.md" }
{ "id": "csharp.analyzers.zerowarnings", "verdict": "intent", "assert": "Analyzer severities are enforced; warnings are not relaxed or suppressed wholesale.", "intentRef": "CODESTYLE.md" },
{ "id": "csharp.coverage.codecov", "verdict": "letter", "assert": "The unit-test job collects coverage (dotnet test --collect:\"XPlat Code Coverage\" --results-directory ./coverage) and uploads it to Codecov via codecov/codecov-action, best-effort (fail_ci_if_error: false so a Codecov outage or an absent token never reds the gate); CODECOV_TOKEN is stored in the repo actions secrets and reaches the reusable validator via secrets: inherit. Required for every C# repo with tests.", "intentRef": "WORKFLOW.md" }
]
},
"nuget": {
Expand All @@ -34,7 +35,8 @@
{ "id": "python.ruff.config", "verdict": "intent", "assert": "A ruff configuration is present.", "intentRef": "CODESTYLE.md" },
{ "id": "python.pyright.config", "verdict": "intent", "assert": "pyright is configured and runs strict on first-party code (src or the integration package) - the strong typing baseline; third-party strictness is relaxed only where a dependency has no usable types.", "intentRef": "CODESTYLE.md" },
{ "id": "python.config.placement", "verdict": "letter", "assert": "ruff and pyright config live in pyproject.toml (canonical); standalone .ruff.toml / pyrightconfig.json is a drift finding. A Home Assistant integration is the exception - it follows home-assistant/core standalone-config conventions and is scored by ha.python.conventions instead.", "intentRef": "CODESTYLE.md" },
{ "id": "python.mypy.allowed", "verdict": "intent", "assert": "mypy is permitted as an additional type checker (not banned); required for a Home Assistant integration (platinum strict-typing). When used it runs in CI and the editor.", "intentRef": "CODESTYLE.md" }
{ "id": "python.mypy.allowed", "verdict": "intent", "assert": "mypy is permitted as an additional type checker (not banned); required for a Home Assistant integration (platinum strict-typing). When used it runs in CI and the editor.", "intentRef": "CODESTYLE.md" },
{ "id": "python.coverage.codecov", "verdict": "letter", "assert": "The test job collects coverage (pytest --cov-report=xml) and uploads it to Codecov via codecov/codecov-action, best-effort (continue-on-error and fail_ci_if_error: false); CODECOV_TOKEN is stored in the repo actions secrets. Required for every Python repo with tests.", "intentRef": "WORKFLOW.md" }
]
},
"console": {
Expand Down
6 changes: 5 additions & 1 deletion spec/secrets.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"$schema": "./secrets.schema.json",
"note": "Secrets the audit cross-checks. `baseline` applies to every fleet repo (the App-signed merge-bot runs everywhere). `mechanisms` are per-target/per-feature additions: a repo requires the baseline plus the mechanisms whose publish target (`targetMechanisms`) or feature workflow (`featureMechanisms`, detected by the mechanism's `workflowNeeds` action) it uses. Baseline secrets are implicit and are NOT repeated in a repo's registry `requiredSecrets`, which lists only the domain-specific additions. A configured secret that no applicable mechanism claims is a stale-secret finding; a present `forbids` secret is a defect.",
"note": "Secrets the audit cross-checks. `baseline` applies to every fleet repo (the App-signed merge-bot runs everywhere). `mechanisms` are per-target/per-feature additions: a repo requires the baseline plus the mechanisms whose publish target (`targetMechanisms`) or feature workflow (`featureMechanisms`, detected by the mechanism's `workflowNeeds` action) it uses. Baseline secrets are implicit and are NOT repeated in a repo's registry `requiredSecrets`, which lists only the domain-specific additions. `typeMechanisms` are per-language requirements: a `csharp` or `python` repo must carry the mapped mechanism (codecov) regardless of opt-in. A configured secret that no applicable mechanism claims is a stale-secret finding; a present `forbids` secret is a defect.",
"baseline": {
"requires": ["CODEGEN_APP_CLIENT_ID", "CODEGEN_APP_PRIVATE_KEY"],
"forbids": ["CODEGEN_APP_ID"],
Expand Down Expand Up @@ -46,5 +46,9 @@
},
"featureMechanisms": {
"codecov": "codecov"
},
"typeMechanisms": {
"csharp": "codecov",
"python": "codecov"
}
}
4 changes: 4 additions & 0 deletions spec/secrets.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@
"featureMechanisms": {
"type": "object",
"additionalProperties": { "type": ["string", "null"] }
},
"typeMechanisms": {
"type": "object",
"additionalProperties": { "type": ["string", "null"] }
}
Comment thread
ptr727 marked this conversation as resolved.
}
}
7 changes: 7 additions & 0 deletions spec/validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,13 @@ def check_secret_set(label, entry, need_kind):
for f, v in feature_mech.items():
if not (v is None or isinstance(v, str)):
errors.append(f"secrets.json: featureMechanisms['{f}'] must be a mechanism name or null")
type_mech = secrets.get("typeMechanisms", {})
if not isinstance(type_mech, dict):
errors.append("secrets.json: 'typeMechanisms' is not an object")
else:
for t, v in type_mech.items():
if not (v is None or isinstance(v, str)):
errors.append(f"secrets.json: typeMechanisms['{t}'] must be a mechanism name or null")
if errors:
print("Spec validation FAILED:")
for e in errors:
Expand Down