Skip to content

Latest commit

 

History

History
50 lines (30 loc) · 11.7 KB

OPERATIONS.md

File metadata and controls

50 lines (30 loc) · 11.7 KB

Operations

How this repo is run, day to day, beyond what CI covers. AGENTS.md and GOVERNANCE.md hold the cross-cutting rules; this file holds LanguageTags' own operational detail.

Local Verification

What verifying a change in this repo requires, and which part of the repo's contract CI structurally cannot exercise.

  • Clean-compile. Run the .NET Format VS Code task (chains CSharpier Format -> .NET Build -> dotnet format style --verify-no-changes) after every code change; see .vscode/tasks.json for the exact command sequence, and CODESTYLE.md "Clean-Compile Verification" for why it must match exactly.
  • Tests. dotnet test --coverage --coverage-output-format cobertura --results-directory ./coverage (native Microsoft.Testing.Platform mode, per global.json, driving Microsoft.Testing.Extensions.CodeCoverage). This is also the invocation CI's Validate job / Unit test job runs, so a local pass predicts the CI result.
  • Codegen drift. dotnet run --project ./LanguageTagsCreate/LanguageTagsCreate.csproj -- --codepath . --skip-download then dotnet tool restore && dotnet csharpier format . regenerates the embedded language data offline from the committed LanguageData/ (no network) and reformats it. CI runs exactly this as Validate job / Validate repository job, which invokes this repository's own hook at .github/actions/validate/action.yml, and fails if the tree drifts from what is committed. Run this whenever you change a codegen emitter (SaveCodeAsync / SaveJsonAsync / GetCodeGenString in LanguageTags/*Data.cs or LanguageTags/LanguageSchema.cs) or the committed LanguageData/ source, and commit the regenerated LanguageTags/*DataGen.cs and LanguageData/*.json in the same PR. --skip-download never fetches upstream. A full refresh from the official ISO 639-2/3, RFC 5646, and UN M.49 registries is the scheduled codegen workflow's job, not something to run in a feature PR.
  • Linters. See "Tool Usage" below for the known-working invocations; CI is the authoritative run, a local run is only for fast feedback.
  • Workflow changes. WORKFLOW.md is a machine-followable rulebook, not just documentation: it defines a static audit (5A), end-to-end trace scenarios (5B), a live probe (5C), and a repository-configuration audit (5D) that together yield a binary operational / not-operational verdict. When asked to check, change, or troubleshoot the CI/CD workflows, drive that methodology, audit the workflow files and repository configuration against the section-4 contract, trace the affected scenarios, and report the verdict with file:line citations, rather than reasoning about the YAML ad hoc. A workflow change is not done until it has been re-validated this way (probe without publishing).

Runbooks

Dependabot and codegen target both main and develop directly, rather than one promoting to the other. This is deliberate: running a bot on one branch and merging its changes across to the other causes conflicts as the feature -> develop -> main flow moves underneath it, whereas landing the same dependency or data update directly in each branch keeps bot changes conflict-free regardless of what else is in flight, and keeps the main package fresh without waiting on a promotion. Dependabot security PRs open against main only.

Detecting when develop has fallen behind a main-only fix. A change that lands on main outside the feature -> develop -> main flow (a reconciliation-branch fix resolving a promotion conflict, or a security PR that merges only to main) can leave develop behind on that content, and forward-only develop never back-merges to catch up. Before basing new work on develop, or diagnosing a defect from it, compare content rather than commit history: git fetch --prune origin main develop (a stale local remote-tracking ref reads as no drift when there is some), then run git diff origin/main origin/develop and inspect its - lines, the main-side of each difference. A -/+ pair within one hunk is usually just develop modifying that code as normal unpromoted work. The stronger staleness signal is a deletion-only hunk (- lines, no + lines): content on main that develop lacks entirely, i.e. a main-only fix develop never received, so the defect may already be fixed on main. Prefer this over git log origin/develop..origin/main, which is noisy here since it also lists routine promotion merges and the main-direct bot commits whose content develop already carries via its own parallel bot PRs. To mirror a main-only fix to develop, open a follow-up PR targeting develop directly, never a main -> develop back-merge (the branching model's forward-only rule forbids it).

Issue-closing keywords. Put Closes #N where it fires on merge to the default branch (main): GitHub closes an issue from a PR description only when that PR merges to main, so a Closes #N in a PR that targets develop never fires, put it in the develop -> main promotion PR instead. A closing keyword in a commit message does close the issue once that commit reaches main via promotion, but that is fragile across squash-merges, so prefer the promotion PR's description or close the issue manually once the fix lands on main.

Release-impact awareness before merging. A human merge never auto-publishes, on either branch. Only two things publish.

  • A bot push to main that changes a shipped input, meaning the codegen App or Dependabot merging its own pull request. A dependency bump (Directory.Packages.props) counts as a shipped input, so the published package's declared dependencies stay current. A push confined to tests, tooling, docs, CI, or GitHub-Actions bumps does not. An unrecognized actor pushing to main publishes nothing and raises a ::warning:: rather than failing, which is the signal that a release bot has changed identity.
  • A workflow_dispatch, which is the only path that publishes from develop and the only path that publishes a human promotion to main. main dispatches a stable release and develop a prerelease, and the release task refuses to publish a main version carrying a prerelease suffix or a non-main version lacking one, so the two channels cannot cross.

So a develop -> main promotion merge publishes nothing on its own. Dispatch the publisher against main when the promotion should release, which is a deliberate step rather than something to avoid.

PR review housekeeping. After the final push on a PR, sweep older review threads from earlier rounds whose code paths no longer exist, otherwise stale unresolved markers remain in the review UI. If a style/convention finding keeps recurring across rounds against code that already matches the tree's own convention, that is the signal the cited rule needs adjusting rather than the code, per pr-review-conduct's outcome 5, not a reason to keep flipping the code back and forth.

Backup and Recovery

Nothing beyond ordinary git history and GitHub's own repository backups. The repo ships no runtime state, database, or deployed service; the published artifact is the versioned NuGet package on NuGet.org, itself immutable once published.

Logs and Debugging

No runtime logging to inspect: the library exposes structured logging via ILoggerFactory/ILogger (see ARCHITECTURE.md "Public API Conventions" for the library's own logging surface, and CODESTYLE.md ".NET", which points to the hub's dotnet-codestyle Skill, for the error-handling and logging conventions) for a consumer to wire up, but this repo does not itself run as a service. Diagnosing a CI failure means reading the failed job's log in the Actions run. Diagnosing a codegen discrepancy means re-running the offline regeneration in "Local Verification" above and reading its [t:...] structured log lines.

Tool Usage

  • Config files. .editorconfig (per-file-type EOL plus the C# / ReSharper style block), .gitattributes, .markdownlint-cli2.jsonc, cspell.json, and .github/copilot-instructions.md (the Copilot review runbook) hold the repo's formatting, linting, and review-mechanics rules.
  • Clean-compile gate. Husky.Net pre-commit git hooks run the formatting/style half only, CSharpier format then dotnet format style --verify-no-changes (no build), installed with dotnet tool restore + dotnet husky install. The .vscode/tasks.json tasks .NET Build, CSharpier Format, and .NET Format are the canonical task names for the full local clean-compile chain (see CODESTYLE.md "Clean-Compile Verification"); do not loosen them. CI is the authoritative backstop: the lint job enforces CSharpier, dotnet format style, markdownlint, scoped cspell, and actionlint from the same config files, because a local hook can be bypassed.
  • Linting tools. CI is the authoritative lint run, and a local run is only for fast feedback. The dotnet checks need only the .NET SDK: dotnet format style is built into the SDK, and CSharpier is restored by dotnet tool restore against .config/dotnet-tools.json. The markdown, spelling, and workflow linters have no committed manifest, so run each from its official Docker image, the portable path that avoids a local Node or Go install, mounting the repo as the working directory: cspell from ghcr.io/streetsidesoftware/cspell, markdownlint-cli2 from davidanson/markdownlint-cli2, and actionlint (which bundles shellcheck) from rhysd/actionlint, at :latest. CI runs these three as pinned action wrappers (Dependabot bumps them) and editorconfig-checker via Docker :latest, while local Docker runs and the VS Code Lint tasks track :latest. Each takes the file globs directly, for example docker run --rm -v "$PWD":/work -w /work ghcr.io/streetsidesoftware/cspell README.md HISTORY.md or ... davidanson/markdownlint-cli2 '**/*.md'. If a local cspell or markdownlint result reports zero checked files, it scanned nothing. Ignore that result. There is intentionally no repo-local wrapper script. The supported runners are the editor, these Docker images, CI, and the hub-hosted scripts/docker_lint.py wrapper, which GOVERNANCE.md "Running the Linters Locally (Known-Working Invocations)" documents as a hub-only section read in a hub checkout.
  • Codegen. See "Local Verification" above for the regeneration command. Embedded language data is regenerated by LanguageTagsCreate/, which pulls directly from the official ISO 639-2/3 + RFC 5646 registries. There is no external codegen API key.
  • Release notes. Keep a short summary in README.md and the full history in HISTORY.md; update both when cutting a release.

Configuration Layout

The cspell accepted-word list and the path exclusions both live in cspell.json, the single source: the Code Spell Checker extension reads cspell.json ahead of the workspace cSpell settings (so GUI "Add to dictionary" lands words there), and the CLI and CI read the same file. Do not keep a parallel word list in the .code-workspace file. .editorconfig is the single source for per-file-type line endings and the C# / ReSharper style block; .gitattributes (* text=auto eol=lf) mirrors that default as git's own normalization. .markdownlint-cli2.jsonc governs the Markdown lint ruleset. Directory.Build.props and Directory.Packages.props (repo root) hold the shared MSBuild properties and centralized NuGet package versions; see GOVERNANCE.md "Repository Layout".