Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
Release merge that turns the template into a polyglot (.NET + Python) repo, updates release/CI plumbing accordingly, documents devcontainer + SSH signing setup, and completes the .NET “Library” → “NuGetLibrary” rename while removing Husky as the default hook runner.
Changes:
- Add Python
PyPiLibrary/(uv + hatchling + ruff + pyright + pytest) with CI build workflow and PyPI Trusted Publishing job. - Split tooling into per-language devcontainers + VS Code workspaces, and add host/devcontainer/SSH-signing documentation.
- Rename the .NET library project/folder to
NuGetLibrary/and update workflows + references; remove Husky from tasks/tools/workflows and rely on CI as the lint backstop.
Reviewed changes
Copilot reviewed 45 out of 51 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| docs/ssh-signing.md | Adds SSH commit signing setup/verification/troubleshooting docs. |
| docs/host-setup.md | Documents per-OS host prerequisites (SSH key, allowed_signers, gh auth). |
| docs/devcontainer.md | Documents per-language devcontainers, mounts, lifecycle, and verification steps. |
| Tests/Tests.csproj | Updates test project reference to NuGetLibrary. |
| Tests/LoggingTests.cs | Updates namespace import to ptr727.ProjectTemplate.NuGetLibrary. |
| README.md | Adds PyPI surface area, devcontainer-first setup path, and optional hook guidance; reorders link refs. |
| Python.code-workspace | Adds Python-focused VS Code workspace settings/extensions. |
| PyPiLibrary/uv.lock | Adds committed uv lockfile for reproducible Python CI. |
| PyPiLibrary/tests/test_example.py | Adds basic pytest coverage for the sample module and version export. |
| PyPiLibrary/tests/init.py | Initializes Python tests package (empty marker). |
| PyPiLibrary/src/ptr727_projecttemplate_library/py.typed | Marks the Python package as typed (PEP 561). |
| PyPiLibrary/src/ptr727_projecttemplate_library/example.py | Adds a trivial example function (greet). |
| PyPiLibrary/src/ptr727_projecttemplate_library/_version.py | Adds single-source version module for hatchling dynamic version. |
| PyPiLibrary/src/ptr727_projecttemplate_library/init.py | Re-exports __version__ and greet as the public surface. |
| PyPiLibrary/pyproject.toml | Defines Python packaging + tool config (hatchling/ruff/pyright/pytest; Python >=3.14). |
| PyPiLibrary/README.md | Documents local dev loop and Trusted Publishing setup + template adoption notes. |
| PyPiLibrary/CODESTYLE.md | Adds Python-specific style guide and local/CI command loop. |
| ProjectTemplate.slnx | Updates .NET project paths and adds the new PyPI build workflow to solution folders. |
| NuGetLibrary/Options.cs | Updates namespace to ptr727.ProjectTemplate.NuGetLibrary. |
| NuGetLibrary/NuGetLibrary.csproj | Renames RootNamespace to ptr727.ProjectTemplate.NuGetLibrary. |
| NuGetLibrary/LogOptions.cs | Updates namespace to ptr727.ProjectTemplate.NuGetLibrary. |
| NuGetLibrary/Library.cs | Updates namespace to ptr727.ProjectTemplate.NuGetLibrary. |
| NuGetLibrary/GlobalUsings.cs | Adds library-level global usings for logging abstractions. |
| NuGetLibrary/Extensions.cs | Updates namespace to ptr727.ProjectTemplate.NuGetLibrary. |
| NuGetLibrary/.editorconfig | Adds scoped suppression for CS1591 missing XML-doc warnings in library. |
| DotNet.code-workspace | Renames/splits the main workspace into a .NET-focused workspace file. |
| Console/Program.cs | Updates namespace import to ptr727.ProjectTemplate.NuGetLibrary. |
| Console/Console.csproj | Updates console project reference to NuGetLibrary. |
| CodeGen/CodeGen.cs | Updates generated quote/date content. |
| CODESTYLE.md | Re-scopes root style guide to .NET and documents CI lint backstop (no default hooks). |
| Benchmarks/Benchmarks.csproj | Updates benchmarks project reference to NuGetLibrary. |
| AGENTS.md | Expands cross-cutting conventions (branch model, workflow conventions, devcontainers, review loop). |
| .vscode/tasks.json | Removes Husky task and keeps .NET build/format tasks. |
| .vscode/launch.json | Updates debug launch configs and adds CodeGen --apikey env wiring. |
| .husky/task-runner.json | Removes Husky.Net task runner config. |
| .husky/pre-commit | Removes Husky pre-commit hook script. |
| .gitignore | Adds Python/uv ignores (.venv, dist, caches, etc.). |
| .github/workflows/test-release-task.yml | Replaces Husky invocation with dotnet csharpier check + dotnet format --verify-no-changes. |
| .github/workflows/test-pull-request.yml | Renames a step to match naming conventions (“… step”). |
| .github/workflows/run-codegen-pull-request-task.yml | Removes Husky install from codegen formatting step. |
| .github/workflows/run-codegen-app-pull-request-task.yml | Removes Husky install from codegen formatting step. |
| .github/workflows/publish-release.yml | Adds a publish-pypi job using OIDC Trusted Publishing. |
| .github/workflows/merge-bot-pull-request.yml | Documents token/recursion-guard behavior for merge-bot merges. |
| .github/workflows/build-release-task.yml | Switches to NuGetLibrary build task, adds PyPiLibrary build task, and pins release target_commitish. |
| .github/workflows/build-pypilibrary-task.yml | Adds reusable Python CI build task (uv sync, ruff, pyright, pytest, uv build, artifact upload). |
| .github/workflows/build-nugetlibrary-task.yml | Renames reusable workflow/job/artifacts from “library” to “nugetlibrary”. |
| .github/dependabot.yml | Adds Dependabot uv ecosystem updates for /PyPiLibrary. |
| .github/copilot-instructions.md | Refactors Copilot instructions to defer to AGENTS.md + adds review runbook mechanics. |
| .devcontainer/python/post-create.sh | Installs pinned uv and pre-syncs PyPiLibrary in the Python devcontainer. |
| .devcontainer/python/devcontainer.json | Adds Python devcontainer with bind mounts + matching extension list. |
| .devcontainer/dotnet/post-create.sh | Restores .NET local tools in the .NET devcontainer. |
| .devcontainer/dotnet/devcontainer.json | Adds .NET devcontainer with bind mounts + matching extension list. |
| .config/dotnet-tools.json | Removes Husky local tool from the tool manifest. |
Comments suppressed due to low confidence (1)
DotNet.code-workspace:6
- This workspace file is indented with tabs, but the repo
.editorconfigsetsindent_style = spaceglobally. Converting the indentation to spaces will keep EditorConfig/formatting tools from constantly flagging this file as non-compliant.
## Summary
Three independent hardenings ported from the `homeassistant-purpleair`
sibling repo, plus convention docs for future PRs:
- **Tag pinning on releases** — `softprops/action-gh-release` is pinned
to commit SHA `3bb12739` (v2.6.2) with a trailing version comment, and
`target_commitish: ${{ github.sha }}` is set explicitly. Without it,
GitHub's REST API silently retargets the new tag to the repository
default branch (`main`) — so prerelease tags created on `develop` pushes
were attaching to `main`'s tip instead of the develop commit that built
the artifact, leaving "Browse files" and `git checkout <tag>` pointing
at unrelated code.
- **Merge-bot token comment** — documents the `GITHUB_TOKEN` vs
App-token recursion-guard tradeoff. No behavior change. The codegen-app
job already side-steps the recursion guard via the App identity;
dependabot and PAT-codegen targets feed `main` where releases are
dispatched manually, so the missing trigger is fine there.
- **Conventions documented** — `AGENTS.md` and
`.github/copilot-instructions.md` now codify PR title rules (≤72 chars,
imperative, no `Co-Authored-By` unless asked, no release-bump magnitude
in title), markdown style (reference-style links, alphabetized),
workflow YAML conventions (action SHA pinning + version comment, naming,
concurrency, `set -euo pipefail`, `if: >-` over `if: |`, boolean input
mirroring), and the develop=squash / main=merge branching model.
## Test plan
- [ ] CI green on the PR (test-pull-request workflow)
- [ ] After merge to develop, observe the next prerelease tag on the
Releases page — the tag's "Browse files" link should resolve to the same
commit SHA that ran `publish-release.yml`, not `main`'s tip
- [ ] Markdown renders correctly on the PR page (no MD025/MD031/MD032
warnings in editor preview)
- [ ] Subsequent PRs in this refactor series follow the new
title/body/workflow conventions
## Summary
Disambiguates the .NET project name from the upcoming Python
`PyPiLibrary` sibling. Folder, csproj filename, `RootNamespace`, and
namespace declarations move from `Library` to `NuGetLibrary`. The
companion GitHub Actions reusable workflow `build-library-task.yml` is
renamed to `build-nugetlibrary-task.yml`; the produced artifact name and
7z filename track the rename.
## Preserved on purpose
- `<PackageId>ptr727.ProjectTemplate.Library</PackageId>` is **kept** so
the published nupkg keeps its identity. No orphaned package, no 404 on
existing consumers, no new badge URL needed.
- README NuGet badges (`nuget-link`, `nugetreleaseversion-shield`,
`nugetprereleaseversion-shield`) still point at
`ptr727.ProjectTemplate.Library` and continue to work.
- Class names `TemplateLibrary` and `StaticTemplateLibrary` are
unchanged — they describe types, not the project.
- `InternalsVisibleTo` declarations stay (`Console`, `Tests`,
`Benchmarks` assembly names are unchanged).
## Files touched
- `git mv Library/ NuGetLibrary/` (folder)
- `git mv NuGetLibrary/Library.csproj NuGetLibrary/NuGetLibrary.csproj`
- `git mv .github/workflows/build-library-task.yml
.github/workflows/build-nugetlibrary-task.yml` — paths, job key
`build-nugetlibrary`, artifact name `nugetlibrary-build`, zip
`NuGetLibrary.7z`
- `.github/workflows/build-release-task.yml` — caller updated: job key,
`uses:`, `needs:` array, artifact-id reference
- `Console/Console.csproj`, `Tests/Tests.csproj`,
`Benchmarks/Benchmarks.csproj` — `<ProjectReference>` paths
- `Console/Program.cs`, `Tests/LoggingTests.cs` — `using` statement
- `NuGetLibrary/{Library,Options,LogOptions,Extensions}.cs` — namespace
declaration
- `NuGetLibrary/NuGetLibrary.csproj` — `RootNamespace`
- `ProjectTemplate.slnx` — 4 project path entries + 1 GitHub Actions
folder entry
- `ProjectTemplate.code-workspace` — `cSpell.words` add `nugetlibrary`
- `AGENTS.md`, `.github/copilot-instructions.md` — project list,
namespace examples, structure section
## Test plan
- [x] `dotnet build` — 0 warnings, 0 errors locally
- [x] `dotnet test` — 15 passed, 0 failed
- [x] `dotnet pack ./NuGetLibrary/NuGetLibrary.csproj` — produces
`ptr727.ProjectTemplate.Library.1.0.0-pre.nupkg` (PackageId preserved)
- [x] Repo-wide grep for `Library/Library.csproj`, `build-library-task`
— zero remaining hits
- [ ] CI green on the PR (test-pull-request workflow invokes the renamed
reusable workflow)
- [ ] After merge, next prerelease produces `NuGetLibrary.7z` artifact
attached to the GitHub release
## Summary Adds a single unified [Dev Container](https://containers.dev/) hosting both the .NET 10 SDK and the upcoming Python `uv` toolchain, plus three focused docs files that decompose host setup, devcontainer setup, and SSH commit signing per-OS. On Windows, the **devcontainer flow** requires WSL2 (the bind-mounts use POSIX paths). The **host-install flow** in `README.md` supports native Windows via winget — see `docs/host-setup.md` for the per-flow scope. > **Stacked on [#62](#62) (NuGetLibrary rename)**. The diff against `develop` will show PR #62's changes until that PR merges; once it does, the diff here cleans up to just the devcontainer + docs work. ## Changes **New files**: - `.devcontainer/devcontainer.json` — base `mcr.microsoft.com/devcontainers/dotnet:1-10.0`, `gh` and `common-utils` features. Bind-mounts `~/.ssh/id_ed25519.pub` (read-only), `~/.config/git/allowed_signers` (read-only), `~/.config/gh` (read-write). `${localEnv:HOME}${localEnv:USERPROFILE}` form covers Linux/macOS and WSL2 hosts. Extension list mirrors `ProjectTemplate.code-workspace` `recommendations`. - `.devcontainer/post-create.sh` (executable, mode 100755) — installs `uv` from `astral.sh` (pinned to a specific version via the version-prefixed install URL; re-installs on version mismatch so the pin holds even when uv is already on PATH), runs `dotnet tool restore`, installs Husky.Net hooks, pre-syncs `PyPiLibrary` if it exists (guarded so this script works before PR 5 lands). - `docs/host-setup.md` — git identity, Ed25519 SSH key, `allowed_signers`, `gh auth login`, per-OS ssh-agent setup, verify checklist. - `docs/devcontainer.md` — bind-mount table, lifecycle commands, `gh` credential-store nuance (Keychain vs libsecret vs file), verify checklist, troubleshooting. - `docs/ssh-signing.md` — per-OS deltas (systemd ssh-agent on Linux, Apple Keychain on macOS, WSL2 caveats), `allowed_signers` format, devcontainer interaction, troubleshooting. **Modified**: - `README.md` — adds a "Recommended (devcontainer)" path to the Development Environment Setup section linking to the new docs; the existing host-install path stays. Template Project Setup section now points at the docs files for prerequisites instead of inlining them. - `ProjectTemplate.code-workspace` — adds `ms-python.python` and `charliermarsh.ruff` to `recommendations` (mirrors the devcontainer list); adds `unwantedRecommendations` for `ms-pyright.pyright` (deprecated; pyright is provided by Pylance which `ms-python.python` auto-installs), `ms-python.mypy-type-checker`, `ms-python.pylint`, `ms-python.flake8`, `ms-python.isort`, and `ms-python.black-formatter` so contributors aren't prompted to install tools that overlap with ruff + Pylance and would surface "could not find binary" connection errors against the venv. Also adds `astral`, `devcontainer`, `hatchling`, `Keychain`, `libsecret`, `onCreateCommand`, `postCreateCommand`, `pyproject`, `pypi`, `pypilibrary`, `pyright`, `ruff` to `cSpell.words`. ## Why a single unified container VS Code Dev Containers does not support per-folder containers in the same multi-root window — only a picker per session. A single image with both .NET and `uv` is the simplest mental model and lets downstream users delete the language they don't need by removing a feature line and a postCreateCommand step. See [VS Code Dev Containers docs](https://code.visualstudio.com/remote/advancedcontainers/connect-multiple-containers) for the limitation. ## Why bind-mount the public key, not the private key The private key never enters the container. Signing happens via the SSH agent socket forwarded by VS Code Dev Containers (`SSH_AUTH_SOCK`). The public key plus `allowed_signers` is enough for git to know which key to delegate signing to and to verify signatures in `git log --show-signature`. ## Why an `onCreateCommand` chown On macOS hosts the bind-mount surfaces `/home/vscode/.ssh` as root-owned inside the container, which would block `gh` from updating `known_hosts`. The chown is idempotent on Linux and WSL2 so it stays unconditional rather than gated on host detection. ## Test plan - [x] `gh pr create --base develop --head devcontainer-docs` succeeded - [ ] CI green on the PR (test-pull-request workflow) - [ ] Build the devcontainer on Linux, run `git -c gpg.format=ssh commit -S --allow-empty -m verify` inside, verify it signs - [ ] Build the devcontainer on macOS host, verify the `onCreateCommand` chown lets `gh auth status` work - [ ] Build on WSL2 host, verify behavior matches Linux - [ ] Confirm `recommendations` in `code-workspace` and `customizations.vscode.extensions` in `devcontainer.json` are identical - [ ] Markdown lint passes on the new docs files
## Summary Adds a Python PyPi template project alongside the .NET `NuGetLibrary`, completing the polyglot template. Modern 2026 stack: hatchling backend, `uv` for env/deps/publish, ruff for lint+format, pyright for typing, pytest for tests, PyPI Trusted Publishing via OIDC. > **Stacked on [#62](#62) (NuGetLibrary rename, merged) and [#63](#63) (devcontainer + docs)**. Once #63 merges this PR's diff cleans up to just the PyPiLibrary work. ## Naming - Folder: `PyPiLibrary/` — qualifier on disk to disambiguate from `NuGetLibrary/` - Published PyPI name: `ptr727-projecttemplate-library` — **no `pypi` qualifier**, mirrors the NuGet identity - Python import name: `ptr727_projecttemplate_library` ## New tree ```text PyPiLibrary/ pyproject.toml # hatchling backend, ruff/pyright/pytest config, PEP 735 [dependency-groups] README.md # what this PyPi template is + uv quickstart + Trusted Publisher setup uv.lock # committed for reproducible CI src/ ptr727_projecttemplate_library/ __init__.py _version.py # __version__ = "0.0.0" placeholder; see README.md "Template Adoption" for version-scheme options example.py # trivial greet() function tests/ test_example.py # 3 tests ``` ## Workflow plumbing The split-by-purpose layout was chosen so `id-token: write` (required by Trusted Publishing) only has to be granted on the entry-point job, not propagated through reusable-workflow chains: - **New** `.github/workflows/build-pypilibrary-task.yml` — reusable workflow that **only builds**: setup uv (pinned to `0.11.8` to match the devcontainer), sync, ruff check, ruff format --check, pyright, pytest, `uv build`, upload artifact. **No publish job here**, no `id-token: write`. - **Modified** `.github/workflows/build-release-task.yml` — adds a `build-pypilibrary` job calling the new reusable workflow, includes it in the `github-release` `needs:` list. Build runs unconditionally (matches the always-validate-on-PR semantic of the rest of the workflow). **No `pypi: bool` input** — would require id-token propagation through the test-pull-request chain (and triggered `startup_failure`, fixed in 4c939f6). - **Modified** `.github/workflows/publish-release.yml` — adds a top-level `publish-pypi` job that runs after `create-release`, downloads the `pypilibrary-build` artifact by name (artifacts uploaded by reusable workflows are accessible to sibling jobs in the same run), and publishes via Trusted Publishing. **`id-token: write` lives only here**, alongside the explicit `contents: read` and `actions: read` needed for `actions/download-artifact`. Uses `skip-existing: true` so the placeholder `0.0.0` version doesn't fail the workflow on repeated pushes. - **Modified** `.github/workflows/test-release-task.yml` — no PyPi-specific input needed; the build runs as part of the existing chain. ## Other plumbing - **`.github/dependabot.yml`** — adds `package-ecosystem: "uv"` targeting `/PyPiLibrary` with the `pypi-deps` group label. Existing `nuget` and `github-actions` blocks normalized to standard two-space indentation under `updates:`. - **`.husky/task-runner.json`** — adds `Ruff Format` and `Ruff Check` tasks scoped to `PyPiLibrary/**/*.py`. Both pass `${staged}` as positional args via `bash -c "..." -- ${staged}` so paths with spaces survive; both gate on `command -v uv` so a `.cs`-only commit on a contributor without uv installed doesn't fail. - **`ProjectTemplate.code-workspace`** — adds Python format-on-save with ruff, the `[python]` formatter binding, `python.terminal.activateEnvironment: false`. No hard-coded venv paths (those caused "could not find ruff binary" popups before `uv sync` ran). Adds `unwantedRecommendations` for mypy / pylint / flake8 / isort / black / standalone pyright so contributors aren't prompted to install tools that overlap with ruff and Pylance. - **`ProjectTemplate.slnx`** — adds `build-pypilibrary-task.yml` to the GitHub Actions folder. - **`.gitignore`** — adds `.venv/`, `dist/`, `__pycache__/`, `*.py[cod]`, `*.egg-info/`, `.pytest_cache/`, `.ruff_cache/`, `.pyright/`. - **`README.md`** — PyPI badge + link in the build/distribution and releases sections; template TODO list reminds the deriver to delete the unused language side. `gh` "pre-authenticated" wording softened to call out the Keychain/libsecret credential-store limitation. ## Trusted Publisher setup (one-time, on PyPI side) 1. PyPI → **Account settings** → **Publishing** → **Add a new pending publisher** - Project name: `ptr727-projecttemplate-library` - Owner: `ptr727` - Repo: `ProjectTemplate` - Workflow: `publish-release.yml` - Environment: `pypi` 2. GitHub repo → **Settings** → **Environments** → create `pypi` environment (optionally with required reviewers). The first successful release converts the pending publisher to a real publisher. ## Versioning gap `_version.py` ships with `__version__ = "0.0.0"`. Trusted Publishing with `skip-existing: true` means the workflow won't fail, but no new PyPI versions land until you wire `_version.py` to something that increments — see `PyPiLibrary/README.md` "Template Adoption" for the three usual options (hatch-vcs / version.json bridge / manual bumps). ## Test plan - [x] `uv sync` clean (host: uv 0.11.8) - [x] `uv run ruff check` — All checks passed - [x] `uv run ruff format --check` — clean - [x] `uv run pyright` — 0 errors, 0 warnings, 0 informations - [x] `uv run pytest` — 3 passed - [x] `uv build` — produces `ptr727_projecttemplate_library-0.0.0.tar.gz` and wheel - [x] `dotnet build` — 0 warnings, 0 errors - [x] `dotnet test` — 15 passed (no .NET regression) - [ ] CI green on the PR (test-release-task exercises ruff, pyright, pytest, uv build via the same reusable workflow that publish uses) - [ ] After Trusted Publisher is configured on PyPI and a real version scheme is wired in `_version.py`, next merge to `main` smoke-tests the publish path
## Summary - Replace the single `.devcontainer/` + `ProjectTemplate.code-workspace` with per-language pairs: `.devcontainer/dotnet/` + `DotNet.code-workspace` and `.devcontainer/python/` + `Python.code-workspace`. Each container ships only one toolchain so editor surface, recommended extensions, and `postCreateCommand` match the language being worked on. - Drop the Husky.Net hooks framework. Hooks are repo-global, so any framework forces its runtime (`dotnet` for Husky, Python for pre-commit) into the wrong-language container. CI already runs `dotnet csharpier check`, `dotnet format --verify-no-changes`, `ruff check`, `ruff format --check`, and `pyright` on every PR — that is the lint backstop. - Document opt-in hooks per language in README under "Optional: enable git hooks locally" so downstream forks can wire up Husky.Net (.NET) or pre-commit (Python) if they want pre-commit checks locally. ## Notes - Windows host Python work is intentionally unsupported: the Python extension caches `.venv/bin/python` (Linux layout) against a venv whose actual Windows path is `.venv\Scripts\python.exe`, breaking Ruff. The python devcontainer is the supported path. - Git rename detection labels `ProjectTemplate.code-workspace -> Python.code-workspace` because `Python.code-workspace` happened to be the closer content match. Conceptually `DotNet.code-workspace` is the descendant; the actual file contents are correct in both. ## Test plan - [ ] `git diff main...HEAD` review. - [ ] Open `DotNet.code-workspace` on Windows host -> `dotnet build` + `dotnet test` succeed without Husky. - [ ] Open `DotNet.code-workspace` -> Reopen in Container -> "dotnet" -> `dotnet build` succeeds; `which uv` returns nothing; `ms-python.python` not installed. - [ ] Open `Python.code-workspace` -> Reopen in Container -> "python" -> `cd PyPiLibrary && uv sync && uv run pytest` succeeds; `which dotnet` returns nothing; Ruff extension log shows interpreter at the container venv path. - [ ] Confirm CI green on this PR (csharpier check + dotnet format --verify-no-changes; ruff check + ruff format --check + pyright). - [ ] Push a deliberately mis-formatted `.cs` file on a throwaway branch -> .NET pipeline fails on csharpier; same with a `.py` file -> Python pipeline fails on ruff. (Verifies the lint backstop without hooks.) - [ ] Confirm `.git/hooks/pre-commit` does not exist after a fresh clone + devcontainer rebuild.
Contributor
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 45 out of 51 changed files in this pull request and generated 4 comments.
Comments suppressed due to low confidence (4)
.github/workflows/test-pull-request.yml:32
- This multi-line bash run block doesn’t start with
set -euo pipefail, which is a documented workflow convention in AGENTS.md. Adding it as the first line will make failures/undefined variables surface reliably and keeps the file consistent with the repo’s workflow standards.
- name: Check workflow results step
run: |
exit_on_result() {
if [[ "$2" == "failure" || "$2" == "cancelled" ]]; then
echo "Job '$1' failed or was cancelled."
.github/workflows/build-nugetlibrary-task.yml:45
- This multi-line bash run block doesn’t start with
set -euo pipefail(required by AGENTS.md workflow conventions). Add it as the first line to fail fast on errors and undefined variables.
.github/workflows/build-nugetlibrary-task.yml:58 - This multi-line bash run block doesn’t start with
set -euo pipefail(required by AGENTS.md workflow conventions). Add it as the first line to fail fast on errors and undefined variables.
DotNet.code-workspace:6 - DotNet.code-workspace uses tab indentation, but the repo-wide .editorconfig sets indent_style=space (and indent_size=4) for all files. Please reformat this workspace file to use spaces so it stays consistent with the repo’s formatting rules and avoids noisy diffs.
- Python.code-workspace and DotNet.code-workspace: convert tab indentation to 4 spaces to match .editorconfig (DotNet bundled for consistency, same issue) - build-pypilibrary-task.yml: correct uv pin reference path to .devcontainer/python/post-create.sh - README.md: fix double-ampersand in nugetprereleaseversion-shield URL
## Summary Addresses unresolved Copilot review threads on [PR #66](#66) (the active develop→main release PR) plus the same-class items flagged in Copilot's review-summary "low-confidence" comments. Per [AGENTS.md workflow conventions](https://github.com/ptr727/ProjectTemplate/blob/develop/AGENTS.md), multi-line bash `run:` blocks must start with `set -euo pipefail` so failures and undefined variables surface reliably. ### Workflows hardened - [`.github/workflows/run-codegen-pull-request-task.yml`](.github/workflows/run-codegen-pull-request-task.yml) — codegen, format, trigger-PR steps (3 blocks). _Inline Copilot threads on PR #66._ - [`.github/workflows/run-codegen-app-pull-request-task.yml`](.github/workflows/run-codegen-app-pull-request-task.yml) — codegen, format steps (2 blocks). _Inline Copilot threads on PR #66._ - [`.github/workflows/test-pull-request.yml`](.github/workflows/test-pull-request.yml) — Check-workflow-results step that defines `exit_on_result` (1 block). _Review-summary item._ - [`.github/workflows/build-nugetlibrary-task.yml`](.github/workflows/build-nugetlibrary-task.yml) — dotnet-build and dotnet-nuget-push steps (2 blocks). _Review-summary item._ ### .gitignore housekeeping - Adds `.claude` so the local Claude harness state directory does not appear in `git status`. - Drops the now-redundant `# Python / uv` section comment. ### Why a new PR (not a fresh commit on PR #66's branch) Standing project rule: no direct commits to `develop`. Once this PR merges to `develop`, PR #66's diff will absorb the same fixes automatically (since #66 is `develop` → `main`), and the threads there can be marked resolved. ## Test plan - [ ] CI passes on this PR. - [ ] After merge, confirm PR #66's diff now includes the four workflow fixes and the `.gitignore` entry, and Copilot threads on #66 can be resolved.
3 tasks
## Summary Two follow-ups for [PR #66](#66) (the active `develop` → `main` release PR): ### 1. README.md — missing colons on NuGet/PyPI bullets Copilot review thread on PR #66 flagged that the NuGet and PyPI bullets in the **Build and Distribution** list are missing the colon after the bold label that every other bullet in the list uses. ```diff - - **NuGet Packages** [NuGet Packages][nuget-link] - .NET libraries published to NuGet.org. - - **PyPI Packages** [PyPI Packages][pypi-link] - Python library published to PyPI.org. + - **NuGet Packages**: [NuGet Packages][nuget-link] - .NET libraries published to NuGet.org. + - **PyPI Packages**: [PyPI Packages][pypi-link] - Python library published to PyPI.org. ``` ### 2. publish-release.yml — wrong SHA for `pypa/gh-action-pypi-publish@v1.14.0` The action was pinned to SHA `6733eb7d741f0b11ec6a39b58540dab7590f9b7d` with a `# v1.14.0` comment, but the upstream `v1.14.0` tag actually points at `cef221092ed1bacb1cc03d23a2d87d1d172e277b`. Because `ghcr.io/pypa/gh-action-pypi-publish` is tagged by release SHAs, no GHCR image existed at the wrong SHA — Docker bailed out with `manifest unknown`. This has caused **`Publish PyPI library job` to fail on every push to `develop`** since PR #64 added the action. CI evidence: - Run on `25c338b9` (May 4) — failed at the same step. - Run on `0da21b2` (today, the PR #68 merge) — failed at the same step. Fix: use the actual upstream `v1.14.0` SHA, keep the `# v1.14.0` comment. ## Why a new PR (not committed onto PR #66's branch) Standing project rule: no direct commits to `develop`. Once this PR merges to `develop`, PR #66's diff absorbs both fixes automatically (since #66 is `develop` → `main`), and the README Copilot thread on #66 can be resolved. ## Test plan - [ ] CI passes on this PR (in particular, the publish job won't run on a non-release push — but the resolution will only be observable on the next release push to `develop`). - [ ] After merge, PR #66's CI re-runs with both fixes and `Publish PyPI library job` succeeds. - [ ] PR #66 README Copilot thread can be replied/resolved citing this merge commit. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Release merge: brings five squashed PRs from develop into main.
Squashed PRs included
softprops/action-gh-releasepinned to a commit SHA withtarget_commitish: ${{ github.sha }}so the release tag lands on the artifact's commit, not the default branch. UpdatedAGENTS.mdworkflow YAML conventions..slnx,.csproj, build workflow, and references updated. Disambiguates from the new Python sibling.docs/host-setup.md,docs/ssh-signing.md,docs/devcontainer.md. Devcontainer bind-mounts SSH public key, allowed_signers, andghconfig so commits sign correctly inside the container.PyPiLibrary/template undersrc/-layout: pyproject.toml + uv.lock + ruff/pyright/pytest config + sample module + tests +build-pypilibrary-task.ymlworkflow +publish-pypijob inpublish-release.yml..devcontainer/dotnet/+DotNet.code-workspaceand.devcontainer/python/+Python.code-workspace. Husky.Net removed (CI is the lint backstop). Optional opt-in hooks documented in README. All Husky references removed from workflows, AGENTS, CODESTYLE, and tasks.Notes
Test plan
.devcontainer/dotnet/and.devcontainer/python/open and build cleanly.cd PyPiLibrary && uv sync && uv run pytestpasses..git/hooks/pre-commitis absent in a fresh clone (Husky removed).