From d32cf6df9c3eedd324b3c3c2b12be86ae2c300c6 Mon Sep 17 00:00:00 2001 From: Pieter Viljoen Date: Mon, 13 Jul 2026 10:06:59 -0700 Subject: [PATCH] EOL rule: mixed-consumer repos + drop the standalone Vantage-Config - AGENTS.md "Line Endings": document mixed-consumer operational repos - the registry lineEndings records the primary platform's default and a subtree carries a per-path .editorconfig override for its own consumer. Example: HomeAutomation is lf-global (Linux docker/shell/dnsmasq/unbound) with its Windows-edited Vantage/** Design Center files (UTF-8 CRLF .dc XML) pinned crlf. - registry: remove Vantage-Config (Design Center is now freely available, so its installer archive is discarded; the Vantage config is consolidated into HomeAutomation, which carries the newer snapshots). Record the consolidation and dual-EOL on the HomeAutomation entry. - cspell: add dnsmasq. Co-Authored-By: Claude Opus 4.8 (1M context) --- AGENTS.md | 3 ++- cspell.json | 1 + registry/repos.json | 17 +---------------- 3 files changed, 4 insertions(+), 17 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 1cf9c73f..8726ec24 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -129,7 +129,8 @@ Applies to code and workflow (`#`) comments alike. - **[`.editorconfig`](./.editorconfig) sets the line ending:** `[*] end_of_line = crlf` is the **default** - every file type is CRLF unless pinned otherwise - with **LF** pinned for the execution-sensitive exceptions - `*.sh`, Dockerfiles, and any individual `.py` executed directly via its shebang (pinned **by path**, e.g. `spec/validate.py`; vanilla `.py` stays CRLF, since Python's universal newlines accept it and it is commonly edited on Windows). Only the LF exceptions are declared; the redundant per-type CRLF rules are intentionally omitted. `.gitattributes` mirrors it: `* -text` (git stores the exact bytes you commit and will **not** normalize) plus the matching LF pins. - **Choosing an ending for a new file type:** CRLF is the **default** - cross-platform editors on Windows produce it, and it is harmless on Linux for everything except shell. Use LF only when the type **requires** it or CRLF **breaks how it is consumed**: executable scripts/shebangs (`*.sh`, s6, husky), Dockerfiles (CRLF breaks `RUN` heredocs/continuations), and tool-owned formats with a native LF ending (KiCad). **Non-workflow YAML stays CRLF** - GitHub Actions' parser tolerates it (a repo that also runs yamllint sets `new-lines: disable` to defer to `.editorconfig`). **Workflow YAML (`.github/workflows/*.{yml,yaml}`) is pinned LF** in `.editorconfig` - Dependabot and Actions rewrite it with LF, so declaring LF keeps it consistent instead of mixed on every bump. This (and the catalog snippet workflows in `catalog/snippets/workflows/*`, pinned LF the same way) is an LF class **not** backed by a `.gitattributes` pin: git keeps `* -text` (no normalization), and CI's `editorconfig-checker` (EOL-only) catches a mismatch instead. Distinguish where a file is *consumed* from where it is *edited*: consumption on Linux alone does not force LF. A config or pattern file consumed by a Linux tool stays CRLF when the tool tolerates a trailing CR: `.dockerignore` and `.gitignore` are CRLF (their parsers strip the CR), and only a *Dockerfile* - interpreted, where a CR breaks `RUN` heredocs and line continuations - is LF. -- **Operational (config) repos: the global default follows the consuming application's native platform, not the fleet CRLF default.** A config repo (registry `workflowModel: operational`) is a *view into an application's configuration directory* - often the exact tree mounted into that app's container - so its files must use the ending the app itself reads and writes, and forcing the fleet CRLF default would fight the app. Set the `[*] end_of_line` default to the app's native ending and record it in the registry [`lineEndings`](./registry/repos.json) field (`lf` | `crlf`): **LF** for a Linux-native app whose config lives in a Linux container - ESPHome, Home Assistant, a devcontainer-only or HACS config - and **CRLF** for a Windows-native editor - e.g. Vantage-Config, edited by Design Center on Windows. The execution-sensitive LF pins (`*.sh`, Dockerfiles, workflow YAML) still apply on top, and `.gitattributes` still mirrors the chosen default. This override is for operational repos only; `release` repos keep the `[*] end_of_line = crlf` fleet default above. Do **not** re-normalize such a repo to the fleet default - that is exactly the over-normalization these per-repo endings prevent. +- **Operational (config) repos: the global default follows the consuming application's native platform, not the fleet CRLF default.** A config repo (registry `workflowModel: operational`) is a *view into an application's configuration directory* - often the exact tree mounted into that app's container - so its files must use the ending the app itself reads and writes, and forcing the fleet CRLF default would fight the app. Set the `[*] end_of_line` default to the app's native ending and record it in the registry [`lineEndings`](./registry/repos.json) field (`lf` | `crlf`): **LF** for a Linux-native app whose config lives in a Linux container - ESPHome, Home Assistant, a devcontainer-only or HACS config - and **CRLF** for a Windows-native editor - e.g. Vantage InFusion config edited by Design Center on Windows. The execution-sensitive LF pins (`*.sh`, Dockerfiles, workflow YAML) still apply on top, and `.gitattributes` still mirrors the chosen default. This override is for operational repos only; `release` repos keep the `[*] end_of_line = crlf` fleet default above. Do **not** re-normalize such a repo to the fleet default - that is exactly the over-normalization these per-repo endings prevent. + - **Mixed-consumer repos: global default = the primary consumer, with a per-path override for a differently-consumed subtree.** When one repo is consumed on two platforms, the registry `lineEndings` records the **primary** default and a subtree carries an `.editorconfig` path override (CRLF/LF) matching *its* consumer, treated like any tool-owned format (pin it; it is not fleet drift). Example: `HomeAutomation` is `lineEndings: lf` (Linux docker-compose, shell, and dnsmasq/unbound daemon configs on the Proxmox host), with its Windows-edited `Vantage/**` subtree - UTF-8 CRLF Design Center project files (`.dc`, which are really XML) - pinned `[Vantage/**] end_of_line = crlf`; the global `* -text` in `.gitattributes` already preserves those bytes, so no extra git pin is needed. - **Scripts and extensionless executables must be LF - and pinned in `.gitattributes`, not just configured.** A CRLF shebang (`#!/usr/bin/env bash\r`) breaks execution. `.editorconfig` sets `[*.sh] = lf`, but that extension-based rule does not match **extensionless** executables (s6 service scripts `run`/`up`/`finish`, husky/git hook scripts like `.husky/pre-commit`), and `* -text` enforces nothing - so a broad normalization pass or an editor can silently flip them to CRLF (it has). `.gitattributes` is the enforcement layer: it carries `*.sh text eol=lf`, and any repo whose tooling ships extensionless scripts **adds the matching path pin** - e.g. `Docker/s6-overlay/** text eol=lf` for s6 init, `.husky/pre-commit text eol=lf` for husky hooks - so git holds them at LF on checkout and `--renormalize`. This pin is mandatory for any repo that overrides s6 init, uses husky/git hooks, or otherwise ships executable scripts. The same explicit-pin rule extends to **tool-owned file formats the base config doesn't key on**: pin them to whatever ending the tool reads and writes so a normalization sweep can't churn them - e.g. KiCad project/footprint/3D files (`*.kicad_mod`, `*.kicad_sym`, `*.step`), which KiCad writes LF (`*.kicad_mod text eol=lf`, ...). The principle is general: a file class the `.editorconfig` extension rules and `* -text` don't cover needs an explicit `.gitattributes` pin matching its tool's native ending. - **Pair each such pin with a matching `.editorconfig` override - the git pin alone is not enough.** `.gitattributes` governs **git** (checkout, commit, `--renormalize`); the **editor** follows `.editorconfig`, where the `[*] end_of_line = crlf` default still applies to any file no extension rule covers. So even with the git pin, the editor writes a CRLF shebang into an extensionless hook (breaking it when run from the working tree) or re-ends/trims a byte-sensitive data file. Give every extensionless **executable** an editorconfig LF override beside its `.gitattributes` pin (`[.husky/pre-commit] end_of_line = lf`); and for a **byte-preserve data directory** (downloaded or opaque source whose exact bytes the consumer may depend on) disable *all* editor normalization, not just EOL - `[/*]` with `charset = unset`, `end_of_line = unset`, `insert_final_newline = false`, `trim_trailing_whitespace = false` (`unset` is EditorConfig's spec-defined special value that removes an inherited property, so the editor enforces neither the global `charset` nor `end_of_line` on that path). Keep these overrides with the line-ending governance (above any `.NET-only` divider), not in the language-style section. - **New files:** create them with the `.editorconfig`-mandated ending. diff --git a/cspell.json b/cspell.json index 4d1e71a7..07f8024f 100644 --- a/cspell.json +++ b/cspell.json @@ -35,6 +35,7 @@ "debuglevel", "devcontainer", "distros", + "dnsmasq", "dockerbuild", "Dockerfiles", "dockerhub", diff --git a/registry/repos.json b/registry/repos.json index 13db735b..d3b4283f 100644 --- a/registry/repos.json +++ b/registry/repos.json @@ -169,7 +169,7 @@ "requiredSecrets": [], "consumerModel": "pull", "releaseTrigger": "dispatch-only", - "driftNotes": ["Maintainer config/ops repo (docker-compose stacks, lifecycle scripts, Firewalla configs).", "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."] + "driftNotes": ["Maintainer config/ops repo (docker-compose stacks, lifecycle scripts, Firewalla configs).", "Dual-platform EOL: lf global (Linux Proxmox host - docker/shell/dnsmasq/unbound) with the Windows-edited Vantage/** Design Center subtree (UTF-8 CRLF .dc XML) pinned crlf. Consolidates the former standalone Vantage-Config repo (removed from the registry; Design Center is now freely available, so its installer archives were discarded).", "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", @@ -279,21 +279,6 @@ "releaseTrigger": "none", "driftNotes": ["Work-in-progress: pre-CI (no .github/workflows, no version.json, no repo-config). main+develop both exist."] }, - { - "name": "Vantage-Config", - "url": "https://github.com/ptr727/Vantage-Config", - "status": "cataloged", - "types": ["source-only"], - "groundTruthBranch": "develop", - "workflowModel": "operational", - "lineEndings": "crlf", - "hasDevelop": true, - "publish": [{ "target": "github-release", "mechanism": "none" }], - "requiredSecrets": [], - "consumerModel": "pull", - "releaseTrigger": "dispatch-only", - "driftNotes": ["Maintainer config/asset archive (Vantage InFusion) with vendored binaries (MSI/7z/PDF) and versioned project snapshots.", "The lone FindInFile C# helper is incidental, not a governed artifact.", "Operational model: develop holds the ground truth; main is the last promoted snapshot. Rollout pending - no .github/workflows yet: lint CI feeding the required check plus dispatch-only source-release scaffolding (version.json + NBGV get-version + publish-release.yml, tag + source zip)."] - }, { "name": "HolidayLights", "url": "https://github.com/ptr727/HolidayLights",