Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 11 additions & 27 deletions .github/workflows/release-policy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,6 @@ on:
description: "Reason for this release"
required: false
type: string
ita_slot:
description: "ITA policy slot"
required: false
default: "slot-a"
type: choice
options:
- auto
- slot-a
- slot-b
dry_run:
description: "Skip ITA upload, Sigstore signing, and GitHub release creation"
required: false
Expand All @@ -49,7 +40,11 @@ jobs:
runs-on: ubuntu-latest
environment: release
env:
ITA_SLOT: ${{ inputs.ita_slot || 'slot-a' }}
# The single managed ITA policy every deployed initdata pins. The "-a"
# suffix is historical, from a retired blue/green slot pair; the name is
# sent on every update, so changing it renames the live policy in ITA.
ITA_POLICY_ID: cbeedffa-e224-4664-b6b4-573fcd4133d3
ITA_POLICY_NAME: integritee-policy-a
REASON: ${{ inputs.reason || '' }}
steps:
- name: Checkout
Expand Down Expand Up @@ -130,32 +125,23 @@ jobs:
predicate-file: predicate.json

# ----------------------------------------------------------------
# Upload to ITA (blue/green slot selection)
# Upload to ITA
# ----------------------------------------------------------------
- name: Select ITA slot
id: slot
env:
GH_TOKEN: ${{ github.token }}
ITA_SLOT_OVERRIDE: ${{ env.ITA_SLOT }}
run: >-
python3 .github/workflows/release-policy/manage.py select-slot
--override "$ITA_SLOT_OVERRIDE" >> "$GITHUB_OUTPUT"

- name: Verify manifest targets use selected ITA slot
- name: Verify manifest targets use the managed ITA policy
if: github.event_name == 'push' || inputs.dry_run == false
uses: ./.github/actions/validate-manifest
with:
manifest-file: attestation-policy/policy-manifest.yaml
policy-id: ${{ steps.slot.outputs.policy_id }}
policy-id: ${{ env.ITA_POLICY_ID }}

- name: Upload policy to ITA
if: github.event_name == 'push' || inputs.dry_run == false
id: upload
uses: ./.github/actions/upload-ita-policy
with:
policy-file: attestation-policy.rego
policy-id: ${{ steps.slot.outputs.policy_id }}
policy-name: ${{ steps.slot.outputs.policy_name }}
policy-id: ${{ env.ITA_POLICY_ID }}
policy-name: ${{ env.ITA_POLICY_NAME }}
ita-api-key: ${{ secrets.ITA_ADMIN_API_KEY }}
ita-api-url: ${{ secrets.ITA_API_URL }}
predicate-file: predicate.json
Expand Down Expand Up @@ -203,8 +189,7 @@ jobs:
if: github.event_name == 'push' || inputs.dry_run == false
env:
BUNDLE_PATH: ${{ steps.attest.outputs.bundle-path }}
POLICY_ID: ${{ steps.slot.outputs.policy_id }}
POLICY_SLOT: ${{ steps.slot.outputs.policy_slot }}
POLICY_ID: ${{ steps.upload.outputs.policy-id }}
run: >-
python3 .github/workflows/release-policy/manage.py prepare-assets
--policy attestation-policy.rego
Expand All @@ -214,7 +199,6 @@ jobs:
--output-dir release-assets
--release-notes release-body.md
--policy-id "$POLICY_ID"
--policy-slot "$POLICY_SLOT"
--reason "$REASON"

- name: Create release
Expand Down
70 changes: 1 addition & 69 deletions .github/workflows/release-policy/manage.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import re
import shutil
import subprocess
import sys
import tarfile
from pathlib import Path

Expand All @@ -19,16 +18,6 @@
r"[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-"
r"[0-9a-f]{4}-[0-9a-f]{12}"
)
SLOTS = {
"slot-a": {
"id": "cbeedffa-e224-4664-b6b4-573fcd4133d3",
"name": "integritee-policy-a",
},
"slot-b": {
"id": "ecdf9171-2f85-47b4-9941-703118f731a8",
"name": "integritee-policy-b",
},
}


def run(*command: str, capture_output: bool = False) -> str:
Expand Down Expand Up @@ -89,45 +78,6 @@ def initialize_predicate(args: argparse.Namespace) -> None:
args.output.write_text(json.dumps(predicate, indent=2) + "\n")


def detect_last_slot() -> str:
"""Read the ITA slot used by the latest release."""
try:
body = run(
"gh",
"release",
"view",
"--json",
"body",
"-q",
".body",
capture_output=True,
)
except subprocess.CalledProcessError:
return ""
match = re.search(r"ITA Policy Slot.*?`(slot-[ab])", body)
return match.group(1) if match else ""


def select_slot(args: argparse.Namespace) -> None:
"""Select and emit the target ITA policy slot."""
if args.override != "auto":
target = args.override
print(f"Slot override: {target}", file=sys.stderr)
else:
last = detect_last_slot()
print(f"Last release used: {last or 'none detected'}", file=sys.stderr)
target = "slot-b" if last == "slot-a" else "slot-a"

slot = SLOTS[target]
print(
f"Targeting: {target} ({slot['name']} / {slot['id']})",
file=sys.stderr,
)
print(f"policy_slot={target}")
print(f"policy_id={slot['id']}")
print(f"policy_name={slot['name']}")


def prepare_assets(args: argparse.Namespace) -> None:
"""Collect release assets and generate release notes."""
if not UUID_RE.fullmatch(args.policy_id):
Expand Down Expand Up @@ -156,12 +106,7 @@ def prepare_assets(args: argparse.Namespace) -> None:
sections = []
if args.reason:
sections.append(f"**Reason:** {args.reason}")
sections.extend(
(
f"**ITA Policy ID:** `{args.policy_id}`",
f"**ITA Policy Slot:** `{args.policy_slot}`",
)
)
sections.append(f"**ITA Policy ID:** `{args.policy_id}`")
args.release_notes.write_text("\n\n".join(sections) + "\n")


Expand All @@ -180,14 +125,6 @@ def parser() -> argparse.ArgumentParser:
predicate_parser.add_argument("--previous-log-index", type=int, default=0)
predicate_parser.set_defaults(handler=initialize_predicate)

slot_parser = commands.add_parser("select-slot")
slot_parser.add_argument(
"--override",
choices=("auto", "slot-a", "slot-b"),
default="auto",
)
slot_parser.set_defaults(handler=select_slot)

assets_parser = commands.add_parser("prepare-assets")
assets_parser.add_argument("--policy", type=Path, required=True)
assets_parser.add_argument("--manifest", type=Path, required=True)
Expand All @@ -196,11 +133,6 @@ def parser() -> argparse.ArgumentParser:
assets_parser.add_argument("--output-dir", type=Path, required=True)
assets_parser.add_argument("--release-notes", type=Path, required=True)
assets_parser.add_argument("--policy-id", required=True)
assets_parser.add_argument(
"--policy-slot",
choices=("slot-a", "slot-b"),
required=True,
)
assets_parser.add_argument("--reason", default="")
assets_parser.set_defaults(handler=prepare_assets)
return result
Expand Down
Loading
Loading