mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-26 21:47:20 +00:00
ad1cfd33c3
The grouped action upgrade leaves signing, network and publication consumer assertions on superseded pins. Align those contracts and check every consumer against reviewed immutable upstream manifests, retaining exact dispatch and release trust boundaries without claiming hosted execution. Change-source: pulse-maintainer
209 lines
8.0 KiB
Python
209 lines
8.0 KiB
Python
import unittest
|
|
from unittest.mock import patch
|
|
|
|
from release_promotion_policy_support import (
|
|
missing_workflow_dispatch_inputs,
|
|
parse_workflow_dispatch_inputs,
|
|
slice_requires_staged_governance_inputs,
|
|
staged_governance_input_errors,
|
|
)
|
|
|
|
|
|
class ReleasePromotionPolicySupportTest(unittest.TestCase):
|
|
def test_action_inputs_do_not_satisfy_dispatch_requirements(self) -> None:
|
|
# Signing action inputs are nested consumer data, not callable workflow
|
|
# inputs. A pin upgrade must not make this dispatch guard accept them.
|
|
content = """
|
|
on:
|
|
workflow_dispatch:
|
|
inputs:
|
|
version:
|
|
type: string
|
|
jobs:
|
|
sign:
|
|
runs-on: ubuntu-24.04
|
|
steps:
|
|
- uses: signpath/github-action-submit-signing-request@c92b958760219087e01f8d67a1669ed57afe2627
|
|
with:
|
|
organization-id: fixture
|
|
api-token: fixture
|
|
expected_head_sha: fixture
|
|
"""
|
|
self.assertEqual(parse_workflow_dispatch_inputs(content), ("version",))
|
|
with patch("release_promotion_policy_support.branch_workflow_text", return_value=content), patch(
|
|
"release_promotion_policy_support.origin_default_branch", return_value="main"
|
|
):
|
|
self.assertEqual(
|
|
missing_workflow_dispatch_inputs(
|
|
workflow_path=".github/workflows/signpath-test-signing.yml",
|
|
required_inputs=("version", "expected_head_sha"),
|
|
),
|
|
("main", ("expected_head_sha",)),
|
|
)
|
|
|
|
def test_parse_workflow_dispatch_inputs_reads_top_level_inputs(self) -> None:
|
|
content = """
|
|
name: Example
|
|
on:
|
|
workflow_dispatch:
|
|
inputs:
|
|
version:
|
|
required: true
|
|
type: string
|
|
promoted_from_tag:
|
|
required: false
|
|
type: string
|
|
ga_date:
|
|
required: false
|
|
type: string
|
|
jobs:
|
|
example:
|
|
runs-on: ubuntu-24.04
|
|
"""
|
|
self.assertEqual(
|
|
parse_workflow_dispatch_inputs(content),
|
|
("version", "promoted_from_tag", "ga_date"),
|
|
)
|
|
|
|
def test_missing_workflow_dispatch_inputs_reports_missing_keys(self) -> None:
|
|
with (
|
|
patch(
|
|
"release_promotion_policy_support.branch_workflow_text",
|
|
return_value="""
|
|
on:
|
|
workflow_dispatch:
|
|
inputs:
|
|
version:
|
|
type: string
|
|
note:
|
|
type: string
|
|
""",
|
|
),
|
|
patch(
|
|
"release_promotion_policy_support.origin_default_branch",
|
|
return_value="main",
|
|
),
|
|
):
|
|
self.assertEqual(
|
|
missing_workflow_dispatch_inputs(
|
|
workflow_path=".github/workflows/release-dry-run.yml",
|
|
required_inputs=("version", "promoted_from_tag", "ga_date"),
|
|
),
|
|
("main", ("promoted_from_tag", "ga_date")),
|
|
)
|
|
|
|
def test_slice_requires_staged_governance_inputs_for_promotion_paths(self) -> None:
|
|
self.assertTrue(
|
|
slice_requires_staged_governance_inputs(
|
|
[
|
|
".github/workflows/release-dry-run.yml",
|
|
"scripts/other_helper.py",
|
|
]
|
|
)
|
|
)
|
|
self.assertTrue(
|
|
slice_requires_staged_governance_inputs(
|
|
[
|
|
"scripts/check-workflow-dispatch-inputs.py",
|
|
]
|
|
)
|
|
)
|
|
self.assertTrue(
|
|
slice_requires_staged_governance_inputs(
|
|
[
|
|
"scripts/trigger-release-dry-run.sh",
|
|
]
|
|
)
|
|
)
|
|
self.assertTrue(
|
|
slice_requires_staged_governance_inputs(
|
|
[
|
|
"scripts/release_control/record_rc_to_ga_rehearsal.py",
|
|
]
|
|
)
|
|
)
|
|
|
|
def test_slice_skips_staged_governance_inputs_for_unrelated_paths(self) -> None:
|
|
self.assertFalse(
|
|
slice_requires_staged_governance_inputs(
|
|
[
|
|
"scripts/release_control/release_promotion_policy_support.py",
|
|
"scripts/release_control/verify_commit_slice.py",
|
|
"scripts/release_control/verify_commit_slice_test.py",
|
|
]
|
|
)
|
|
)
|
|
|
|
def test_returns_no_errors_when_not_using_staged_governance(self) -> None:
|
|
self.assertEqual(staged_governance_input_errors(use_staged_governance=False), [])
|
|
|
|
def test_reports_missing_template_and_missing_checklist_update(self) -> None:
|
|
with (
|
|
patch(
|
|
"release_promotion_policy_support.missing_staged_repo_paths",
|
|
return_value=["docs/release-control/v6/internal/RC_TO_GA_REHEARSAL_TEMPLATE.md"],
|
|
),
|
|
patch(
|
|
"release_promotion_policy_support.read_repo_text",
|
|
return_value="# Pulse v6 Pre-Release Checklist\n",
|
|
),
|
|
):
|
|
self.assertEqual(
|
|
staged_governance_input_errors(use_staged_governance=True),
|
|
[
|
|
"stage the canonical promotion proof inputs:\n- docs/release-control/v6/internal/RC_TO_GA_REHEARSAL_TEMPLATE.md",
|
|
"stage the updated docs/release-control/v6/internal/PRE_RELEASE_CHECKLIST.md that records the rc-to-ga-rehearsal-summary gate input",
|
|
"stage the updated docs/release-control/v6/internal/PRE_RELEASE_CHECKLIST.md that records the canonical promotion metadata envelope",
|
|
"stage the updated docs/release-control/v6/internal/RC_TO_GA_REHEARSAL_TEMPLATE.md that preserves the artifact-backed promotion metadata record shape",
|
|
],
|
|
)
|
|
|
|
def test_skips_checklist_read_when_checklist_itself_is_unstaged(self) -> None:
|
|
with (
|
|
patch(
|
|
"release_promotion_policy_support.missing_staged_repo_paths",
|
|
return_value=["docs/release-control/v6/internal/PRE_RELEASE_CHECKLIST.md"],
|
|
),
|
|
patch(
|
|
"release_promotion_policy_support.read_repo_text",
|
|
return_value="# Pulse v6 Prerelease-to-GA Rehearsal Record\n",
|
|
) as read_repo_text,
|
|
):
|
|
self.assertEqual(
|
|
staged_governance_input_errors(use_staged_governance=True),
|
|
[
|
|
"stage the canonical promotion proof inputs:\n- docs/release-control/v6/internal/PRE_RELEASE_CHECKLIST.md",
|
|
"stage the updated docs/release-control/v6/internal/PRE_RELEASE_CHECKLIST.md that records the rc-to-ga-rehearsal-summary gate input",
|
|
"stage the updated docs/release-control/v6/internal/PRE_RELEASE_CHECKLIST.md that records the canonical promotion metadata envelope",
|
|
"stage the updated docs/release-control/v6/internal/RC_TO_GA_REHEARSAL_TEMPLATE.md that preserves the artifact-backed promotion metadata record shape",
|
|
],
|
|
)
|
|
read_repo_text.assert_called_once_with(
|
|
"docs/release-control/v6/internal/RC_TO_GA_REHEARSAL_TEMPLATE.md",
|
|
staged=True,
|
|
strict_staged=True,
|
|
)
|
|
|
|
def test_reports_missing_checklist_and_template_contract_language(self) -> None:
|
|
with patch(
|
|
"release_promotion_policy_support.missing_staged_repo_paths",
|
|
return_value=[],
|
|
), patch(
|
|
"release_promotion_policy_support.read_repo_text",
|
|
side_effect=[
|
|
"# Pulse v6 Pre-Release Checklist\n- rc-to-ga-rehearsal-summary\n",
|
|
"# Pulse v6 Prerelease-to-GA Rehearsal Record\n",
|
|
],
|
|
):
|
|
self.assertEqual(
|
|
staged_governance_input_errors(use_staged_governance=True),
|
|
[
|
|
"stage the updated docs/release-control/v6/internal/PRE_RELEASE_CHECKLIST.md that records the canonical promotion metadata envelope",
|
|
"stage the updated docs/release-control/v6/internal/RC_TO_GA_REHEARSAL_TEMPLATE.md that preserves the artifact-backed promotion metadata record shape",
|
|
],
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|