Releases.Tests.ps1 creates releases and tags on the shared test repository using hardcoded tag names (v1.0, v1.1, v1.2, v1.3, v1.4). No AfterAll cleanup removes these releases or their underlying git tags after the test context finishes.
Request
Current experience
Two categories of problem arise from the current design:
Sequential run collisions. Run N creates tag v1.0 on the shared repository. Run N+1 hits New-GitHubRelease -Tag 'v1.0' and fails because the tag already exists. The test New-GitHubRelease - Throws when tag already exists depends on a clean-slate repo where the tag was just created within this run — not a leftover from a previous run. On a persistent shared repository (the direction described in #590), this collision is guaranteed without cleanup.
Concurrent run collisions. Two concurrent workflow runs (e.g., two open PRs) both target the same persistent shared repository and race to create v1.0. The loser fails. Even without persistent repos, if GITHUB_RUN_ID were ever the same (impossible today, but a design fragility), the collision is identical.
No cleanup. The AfterAll block at the Context 'As <Type> using <Case> on <Target>' level currently only disconnects the GitHub account. It does not remove releases or git tags created during the test run. Releases and tags accumulate on the shared repository indefinitely, drifting its state across runs.
What is expected
- Tags created during a test run are scoped so they cannot collide with tags from another run.
- All releases (including drafts) and their underlying git tags created during a test run are removed by
AfterAll, leaving the shared repository in the same state it was found in.
- The test
New-GitHubRelease - Throws when tag already exists continues to function correctly, asserting against the run-scoped tag created moments earlier in the same context.
Acceptance criteria
- All tag strings in
Releases.Tests.ps1 are suffixed with the run ID: "v1.0-$id", "v1.1-$id", "v1.2-$id", "v1.3-$id", "v1.4-$id".
- The
AfterAll at the auth-case Context level removes all releases on $repo and then removes all git tags whose names end with -$id, leaving no test artefacts on the shared repository.
- Two concurrent runs targeting the same shared repository do not interfere with each other's release or tag operations.
- The test
New-GitHubRelease - Throws when tag already exists remains passing.
Environment
Regression
This issue was latent before #541. Each run previously created a uniquely named repository (containing the GITHUB_RUN_ID), so tag collisions between runs were impossible. With #541 consolidating to shared repositories, the risk became real. Moving to persistent repositories (#590) makes it a guaranteed failure on every sequential run.
Technical decisions
Tag scoping pattern: Append -$id to each hardcoded tag. This follows the same run-scoping convention used for secret names, variable names, and environment names elsewhere in the test suite. The $id variable is already set at the file BeforeAll level: $id = $env:GITHUB_RUN_ID.
Release cleanup: Get-GitHubRelease -Owner $Owner -Repository $repo -All | Remove-GitHubRelease -Confirm:$false. Removing all releases on a test-dedicated repository between test contexts is safe — no other test file writes releases, and the release sub-context assertions are self-contained within a single context run.
Tag cleanup: GitHub's REST API for deleting a release does not delete the underlying git tag. Tags must be deleted separately. Whether Remove-GitHubTag (or an equivalent function) exists in the module needs to be confirmed; if not, the direct Git refs API endpoint DELETE /repos/{owner}/{repo}/git/refs/tags/{tag} must be called. In either case, the cleanup filter is tags whose name ends with -$id to avoid touching any pre-existing tags on the repository.
Cleanup placement: AfterAll at the Context 'As <Type> using <Case> on <Target>' scope, after the existing account-disconnect step. This mirrors the pattern used in Secrets.Tests.ps1 and Variables.Tests.ps1 for their respective resource cleanup.
Implementation plan
Core changes
Verification
Releases.Tests.ps1creates releases and tags on the shared test repository using hardcoded tag names (v1.0,v1.1,v1.2,v1.3,v1.4). NoAfterAllcleanup removes these releases or their underlying git tags after the test context finishes.Request
Current experience
Two categories of problem arise from the current design:
Sequential run collisions. Run N creates tag
v1.0on the shared repository. Run N+1 hitsNew-GitHubRelease -Tag 'v1.0'and fails because the tag already exists. The testNew-GitHubRelease - Throws when tag already existsdepends on a clean-slate repo where the tag was just created within this run — not a leftover from a previous run. On a persistent shared repository (the direction described in #590), this collision is guaranteed without cleanup.Concurrent run collisions. Two concurrent workflow runs (e.g., two open PRs) both target the same persistent shared repository and race to create
v1.0. The loser fails. Even without persistent repos, ifGITHUB_RUN_IDwere ever the same (impossible today, but a design fragility), the collision is identical.No cleanup. The
AfterAllblock at theContext 'As <Type> using <Case> on <Target>'level currently only disconnects the GitHub account. It does not remove releases or git tags created during the test run. Releases and tags accumulate on the shared repository indefinitely, drifting its state across runs.What is expected
AfterAll, leaving the shared repository in the same state it was found in.New-GitHubRelease - Throws when tag already existscontinues to function correctly, asserting against the run-scoped tag created moments earlier in the same context.Acceptance criteria
Releases.Tests.ps1are suffixed with the run ID:"v1.0-$id","v1.1-$id","v1.2-$id","v1.3-$id","v1.4-$id".AfterAllat the auth-caseContextlevel removes all releases on$repoand then removes all git tags whose names end with-$id, leaving no test artefacts on the shared repository.New-GitHubRelease - Throws when tag already existsremains passing.Environment
tests/Releases.Tests.ps1Regression
This issue was latent before #541. Each run previously created a uniquely named repository (containing the
GITHUB_RUN_ID), so tag collisions between runs were impossible. With #541 consolidating to shared repositories, the risk became real. Moving to persistent repositories (#590) makes it a guaranteed failure on every sequential run.Technical decisions
Tag scoping pattern: Append
-$idto each hardcoded tag. This follows the same run-scoping convention used for secret names, variable names, and environment names elsewhere in the test suite. The$idvariable is already set at the fileBeforeAlllevel:$id = $env:GITHUB_RUN_ID.Release cleanup:
Get-GitHubRelease -Owner $Owner -Repository $repo -All | Remove-GitHubRelease -Confirm:$false. Removing all releases on a test-dedicated repository between test contexts is safe — no other test file writes releases, and the release sub-context assertions are self-contained within a single context run.Tag cleanup: GitHub's REST API for deleting a release does not delete the underlying git tag. Tags must be deleted separately. Whether
Remove-GitHubTag(or an equivalent function) exists in the module needs to be confirmed; if not, the direct Git refs API endpointDELETE /repos/{owner}/{repo}/git/refs/tags/{tag}must be called. In either case, the cleanup filter istags whose name ends with -$idto avoid touching any pre-existing tags on the repository.Cleanup placement:
AfterAllat theContext 'As <Type> using <Case> on <Target>'scope, after the existing account-disconnect step. This mirrors the pattern used inSecrets.Tests.ps1andVariables.Tests.ps1for their respective resource cleanup.Implementation plan
Core changes
-$idthroughoutReleases.Tests.ps1:v1.0→"v1.0-$id",v1.1→"v1.1-$id",v1.2→"v1.2-$id",v1.3→"v1.3-$id",v1.4→"v1.4-$id"AfterAll:Get-GitHubRelease -Owner $Owner -Repository $repo -All -ErrorAction SilentlyContinue | Remove-GitHubRelease -Confirm:$falseAfterAll: remove all git tags matching*-$idon$repo(usingRemove-GitHubTagif available, otherwise direct refs API)Verification
Remove-GitHubTag(or equivalent) exists in the module; if not, track a separate issue to add itNew-GitHubRelease - Throws when tag already existstest still passes with the scoped tag name