Repository navigation
[P1] Prevent tag-based workflow dry runs from entering the NuGet publish job #19
Description
Priority and scope
P1: A tag-selected manual rehearsal can publish packages even when the maintainer explicitly selected dry run and left publish disabled. Treat this as release-safety work before the next rehearsal.
Reviewed default develop at 612e5c03bba3e811741385cd7440ffb5af43b8df. main at b9697f2f91aa737360479a434f7d3cc01ccaa8fd has the identical source tree, so this is not develop-only.
Evidence and cause
The publish condition starts with github.ref_type == 'tag' || .... The checks for inputs.dry_run == false and inputs.publish == true apply only to the second branch. The dry-run handoff can report a no-push rehearsal immediately before that publish job. The final step pushes all three packages.
Minimal reproduction (proposed, not executed)
Evaluate the workflow's job conditions with event_name=workflow_dispatch, ref_type=tag, ref=refs/tags/, inputs.dry_run=true, inputs.publish=false, and successful prerequisite jobs. Do not test against the real NuGet endpoint; use a condition-unit test or a mocked publish command.
Actual behavior predicted from source
The publish condition is true solely because ref_type is tag. The job can request production-environment approval and, if that gate permits it and credentials are present, invoke NuGet push. Actual production protection settings were not inspected, so automatic publication without approval is not asserted.
Expected behavior
Every manual dry run must exclude the publish job regardless of branch/tag selection. The publish=false input must not be bypassed by selecting a tag.
Fix direction
Separate tag-push events from manual dispatches. Require a push event for automatic tag publishing, and evaluate dry_run/publish explicitly for every manual-dispatch path. Keep production approval as defense in depth.
Regression acceptance
- Test the full event × ref-type × publish × dry_run matrix
- Tag dispatch with dry_run=true never schedules a publish or calls push
- Branch dry runs remain nonpublishing
- Authorized valid tag pushes and approved manual production runs still publish
- Validate conditions themselves, rather than just searching YAML for the word dry_run
Duplicate review
This is a narrow follow-up to closed #17 and merged #18: their broad dry-run requirement was implemented, but the tag-dispatch condition bypasses it. All 18 existing issue/PR records and 72 issue-conversation comments were inspected; none currently tracks this specific unresolved case.
Verification limits
This is a static source finding. The reproduction was not compiled or run, and no repository code, release workflow, or benchmark was executed in this review. The existing exact-head CI run passed its build/test and Roslyn-host jobs; release pack, artifact-verification, and publish jobs were skipped. That existing run does not validate this proposed regression.
Prepared with OpenAI Codex.
Validation update (2026-10-02)
Confirmed by source-extracted condition evaluation. The earlier static-only verification statement is superseded for the checks below. The source remains 612e5c03bba3e811741385cd7440ffb5af43b8df; no fix was applied.
Executed checks
An independent Python evaluator read the original job conditions directly from .github/workflows/ci.yml and evaluated their documented boolean/string expression subset, including the needs success gate. It completed 72 event/ref/input cases and 12 failed/cancelled/skipped-prerequisite controls. This was not a GitHub Actions workflow execution.
For workflow_dispatch on refs/tags/1.0.0, assuming successful prerequisites and a valid tag matching GitVersion:
| publish | dry_run | Pack reachable | Artifact verification reachable | Publish reachable |
|---|---|---|---|---|
| false | false | false | false | false |
| false | true | true | true | true |
| true | false | false | false | false |
| true | true | true | true | true |
Both dry-run tag dispatches therefore reach the publish job, including publish=false. Branch dry runs remained nonpublishing. All 12 prerequisite failure/cancellation/skip controls blocked publication.
Scope and limits
The raw publish expression is true in all four tag-dispatch rows, but the two dry_run=false rows do not reach publishing because pack is skipped. A true publish expression alone is insufficient; the successful dependency chain is part of this reproduction.
The matrix assumes later package and artifact validation succeeds. Production-environment protections and credential availability were not inspected. No workflow was dispatched and no package was published, so this confirms that the condition can reach the production approval gate, not that approval can be bypassed.
The evaluator follows GitHub's documented expression behavior and job dependency rules. Synthetic pull-request/tag combinations in the matrix are negative controls, not normal pull-request contexts.
Overall cloud-validation scope
The dated evidence above supersedes the earlier static-only verification statement for the listed cases; unexecuted regression-acceptance variants remain proposed. The unchanged full solution built in Release with zero warnings/errors. The existing suites recorded 653 passes and 2 failures across 655 tests, with no skips. Both failures, TrimmedConsumerPublishesAndRunsWithoutLiteMapperWarnings and NativeAotConsumerPublishesAndRunsWithoutLiteMapperWarnings, were cloud infrastructure blocked: ILLink's out-of-process ComputeManagedAssemblies task host failed with MSB4216 and Unix-domain pipe SocketException (13), permission denied. Trimming/AOT behavior was not established, and these tests are not intrinsically Windows-only. The repository's separate win-x64 final-package release lane still requires a Windows execution environment and Windows/MSVC toolchain.
The 20 focused generator tests comprised 14 intentional contract assertion failures reproducing defects and 6 passing controls/measurement cases; they are not an all-green acceptance suite. No production fixes or releases were made.
Activity
AGiorgetti commentedon Oct 3, 2026
Execution plan
Continuing only #19 in an isolated checkout from develop at
612e5c03bba3e811741385cd7440ffb5af43b8df, branchfeat/codex-issue-19-publish-gate. No existing fix PR or blocking dependencies were found. This comment records ownership for this task; the user's explicit issue instruction supplies readiness.No release workflow dispatch, tags, NuGet publication, merge, security setting changes, or software installation. Installed AgentStack cannot perform protocol operations:
agentstack doctorexpects.agent-stack/active-tracker.json, while the repository uses.agent-stack/modules/protocol/active-tracker.json. Using the repository-authorized GitHub CLI fallback without changing the tooling.Prepared with OpenAI Codex.
AGiorgetti commentedon Oct 3, 2026
Regression baseline confirmed
Added source-based MSTest coverage before modifying production YAML. The initial run executed 154 cases: 20 failed, 134 passed, zero skipped.
dry_run=true,publish=false/true) reached pack, artifact verification, and publish. Executing the extracted publish step with a local mockeddotnetfunction recorded three push calls per case. No real push occurred.Next: require
github.event_name == 'push'for the automatic tag branch of the publish condition. Keep manual authorization, the production environment, dependency gates, and all package validation unchanged. Dependency restore succeeded on the approved unrestricted retry after sandbox NU1301 SSL errors.Evidence:
artifacts/validation/issue-19/red/issue-19-red.trx. These are local condition/dependency tests and mocked command execution, not a GitHub release run.Prepared with OpenAI Codex.
AGiorgetti commentedon Oct 3, 2026
Publishing gate fixed and focused validation passed
The publish condition now requires a push event for its automatic tag branch. Manual production publishing still requires publish=true and dry_run=false on main/release//hotfix/.
Full solution tests are running. This machine has .NET 8/9/10 runtimes but no MSVC C++ toolchain, so local Native AOT validation may be inconclusive; ordinary exact-head PR CI will check Windows/Linux. The production environment, dependency gates, SemVer/package/content/checksum/provenance validation, and actual publish command are preserved. No release has been performed.
Prepared with OpenAI Codex.
AGiorgetti commentedon Oct 3, 2026
Draft PR opened
Draft PR #33 targets develop. The pushed remote head exactly matches validated local commit
34d0549132b50bf0c0e09dd6e143ea1d26f7ea66. The test-first regression commit isc228266aa82e89204039a1fab57acf75a5167be9; both commits carry Codex co-author attribution.NETSDK1064), one missing-MSVC Native AOT prerequisite skip. Regenerating assets and rebuilding recovered both failures; affected sample/usage tests plus release regressions then passed 175/175, zero skipped. The full-run and recovery evidence establish passing results for all 808 executed cases without claiming a single all-green full invocation.Checking ordinary pull_request CI on that exact head now. Local Native AOT remains inconclusive; live production environment behavior is outside the local evaluator's scope. No release workflow dispatch, real NuGet push, tags, merge, or repository security-setting change occurred.
Prepared with OpenAI Codex.
AGiorgetti commentedon Oct 3, 2026
Ready for human review
Draft PR #33 against develop is ready. Ordinary PR CI passed on exact head
34d0549132b50bf0c0e09dd6e143ea1d26f7ea66, matching the remote branch and PR head.One production condition line changed. Publishing command execution was mocked locally; no live production approval or NuGet publication was tested. No release, workflow dispatch, tag push, merge, security-setting change, or unrelated issue change occurred. Both commits have Codex co-author attribution. PR remains draft and #19 remains open. Stopping here as requested.
Prepared with OpenAI Codex.
test: reproduce tag-dispatch publishing bypass
fix: restrict automatic NuGet publishing to tag pushes
fix: stop release stages on native command failures