1212from pathlib import Path
1313from unittest import mock
1414
15- from cli_release_verifier_contract import CliRecoveryWorkflowSourceTest , CliReleaseAuthorityTest
15+ from cli_release_verifier_contract import ( # noqa: F401 - imported for unittest discovery
16+ CliRecoveryWorkflowSourceTest ,
17+ CliReleaseAuthorityTest ,
18+ )
1619
1720RECOVERY_SCRIPT = Path (__file__ ).with_name ("component-release-recovery.py" )
1821RUST_WORKFLOW_FIXTURE = Path (__file__ ).with_name ("sdk-rust-release-plan-recovery.fixture.yml" )
@@ -433,8 +436,8 @@ def test_rejects_skipped_nonblocking_or_decoy_scoped_steps(self) -> None:
433436 1 ,
434437 ),
435438 "run identity includes an unapproved field" : source .replace (
436- "databaseId,displayTitle,headBranch,headSha,status,conclusion" ,
437- "databaseId,displayTitle,headBranch,headSha,status,conclusion,event " ,
439+ "databaseId,event, displayTitle,headBranch,headSha,status,conclusion" ,
440+ "databaseId,event, displayTitle,headBranch,headSha,status,conclusion,actor " ,
438441 1 ,
439442 ),
440443 }
@@ -553,6 +556,27 @@ def test_other_components_keep_the_contents_api_contract(self) -> None:
553556 with self .assertRaises (self .recovery .RecoveryError ):
554557 self .recovery .verify_recovery_workflow_source ("server" , protected_only )
555558
559+ def test_python_recovery_dispatches_publication_from_protected_main (self ) -> None :
560+ recovery_source = RECOVERY_WORKFLOW .read_text ()
561+ publish_source = PUBLISH_WORKFLOW .read_text ()
562+
563+ self .recovery .verify_recovery_workflow_source ("sdk-python" , recovery_source )
564+ self .assertIn ("gh workflow run publish.yml --ref main" , recovery_source )
565+ self .assertNotIn ('gh workflow run publish.yml --ref "$RELEASE_TAG"' , recovery_source )
566+ self .assertIn ('-f release_tag="$RELEASE_TAG"' , recovery_source )
567+ self .assertIn ("github.ref == 'refs/heads/main' && inputs.publish" , publish_source )
568+ self .assertIn ("format('refs/tags/{0}', inputs.release_tag)" , publish_source )
569+ self .assertIn ('if [ "$REQUESTED_TAG" != "$package_version" ]' , publish_source )
570+ self .assertIn ('tag_commit="$(git rev-list -n 1 "$REQUESTED_TAG")"' , publish_source )
571+
572+ invalid_recovery_sources = (
573+ recovery_source .replace ("--ref main" , '--ref "$RELEASE_TAG"' , 1 ),
574+ recovery_source .replace ('-f release_tag="$RELEASE_TAG"' , '-f release_tag="$GITHUB_REF_NAME"' , 1 ),
575+ )
576+ for invalid_source in invalid_recovery_sources :
577+ with self .assertRaises (self .recovery .RecoveryError ):
578+ self .recovery .verify_recovery_workflow_source ("sdk-python" , invalid_source )
579+
556580
557581class PublicationRunSelectionTest (unittest .TestCase ):
558582 RELEASE_TAG = "1.2.3"
@@ -568,14 +592,14 @@ def run_metadata(
568592 * ,
569593 status : str ,
570594 conclusion : str | None ,
571- commit : str | None = None ,
595+ head_sha : str | None = None ,
572596 plan : str = "release-plan/continuity-plan-a" ,
573597 ) -> dict [str , object ]:
574598 return {
575599 "databaseId" : run_id ,
576600 "displayTitle" : f"Release { self .RELEASE_TAG } for { plan } " ,
577- "headBranch" : self . RELEASE_TAG ,
578- "headSha" : commit or self . RELEASE_COMMIT ,
601+ "headBranch" : "main" ,
602+ "headSha" : head_sha or "2" * 40 ,
579603 "status" : status ,
580604 "conclusion" : conclusion ,
581605 }
@@ -592,6 +616,11 @@ def test_failed_plan_a_dispatches_current_plan_b_once(self) -> None:
592616 )
593617 workflow = RECOVERY_WORKFLOW .read_text ()
594618 self .assertEqual (workflow .count ("gh workflow run publish.yml" ), 1 )
619+ self .assertIn ("gh workflow run publish.yml --ref main" , workflow )
620+ self .assertIn (
621+ "gh run list --workflow publish.yml --event workflow_dispatch --branch main" ,
622+ workflow ,
623+ )
595624 self .assertIn ('-f release_tag="$RELEASE_TAG" -f release_plan="$PLAN_TAG" -f publish=true' , workflow )
596625 self .assertNotIn ("gh run rerun" , workflow )
597626
@@ -603,13 +632,13 @@ def test_active_exact_run_waits_and_successful_exact_run_completes(self) -> None
603632 successful = self .run_metadata (3 , status = "completed" , conclusion = "success" )
604633 self .assertEqual (self .select ([failed , successful ])["action" ], "complete" )
605634
606- def test_moved_source_tag_is_rejected (self ) -> None :
607- moved = self .run_metadata (1 , status = "completed" , conclusion = "failure" , commit = "a" * 40 )
608-
609- with self .assertRaises ( self . recovery . RecoveryError ) as caught :
610- self . select ([ moved ])
635+ def test_non_main_or_different_release_runs_are_ignored (self ) -> None :
636+ tag_context = self .run_metadata (1 , status = "completed" , conclusion = "success" )
637+ tag_context [ "headBranch" ] = self . RELEASE_TAG
638+ different_release = self .run_metadata ( 2 , status = "completed" , conclusion = "success" )
639+ different_release [ "displayTitle" ] = "Release 9.9.9 for release-plan/continuity-plan-a"
611640
612- self .assertEqual (caught . exception . phase , "publication " )
641+ self .assertEqual (self . select ([ tag_context , different_release ])[ "action" ], "dispatch " )
613642
614643 def test_fresh_dispatch_keeps_partial_publication_idempotent (self ) -> None :
615644 workflow = PUBLISH_WORKFLOW .read_text ()
0 commit comments