mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
Three confirmed gaps in the release fail-closed pipeline (#87/#318/#402): #87 publish_failed retry duplicates changelog: execute() only short-circuits on an existing tag. A publish_failed outcome (commit pushed + CI green, no tag) left no tag, so a retry re-ran apply_version_bumps + write_changelog_entry (re-inserting the entry above the already-present heading -> duplicate) and commit_and_push (a second chore(release) commit). Add ReleaseOps .release_commit_sha(version) detecting a prior release commit on the branch (clone already at the target version); when present, skip the bump/changelog/ gate/commit pipeline and rejoin the shared CI -> publish tail on the existing commit. No second commit, no duplicate entry. #318 wait_for_ci polls branch-latest, not the release commit: a later push to master during the ~40min wait made the latest run's head_sha != the release sha forever, exhausting _CI_MAX_POLLS -> false ci_failed on a release whose own CI was green. Thread head_sha through get_latest_ci_conclusion / _fetch_latest_ci_run (GitHub actions/runs?head_sha=) so the gate polls the release commit's own run; a concurrent push can no longer mask it. #402 release CI gate reuses self_heal_ci_workflow: that setting documents an empty-string mode for single-workflow repos which, inherited here, degraded the fail-closed gate to the all-workflows mode git.py itself flags as unreliable. Add release_ci_workflow (default ci.yml) and _resolve_release_ ci_workflow(); the release gate always resolves a NAMED workflow, never None. Refactor: bundle the CI-fetch per-project inputs into a _CiRunQuery dataclass so _fetch_latest_ci_run stays under the arg-count gate; unify the half-landed path into execute's shared tail (drops a separate _publish_existing, one return path). TDD red->green; ruff/mypy clean.