Recover Python SDK from latest public release plan #64
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Release plan recovery | |
| run-name: Recover Python SDK from ${{ inputs.plan_tag || 'latest public release plan' }} | |
| on: | |
| schedule: | |
| - cron: '43 * * * *' | |
| workflow_dispatch: | |
| inputs: | |
| plan_tag: | |
| description: Immutable release-plan tag; empty selects the newest public plan | |
| required: false | |
| type: string | |
| default: '' | |
| permissions: | |
| actions: write | |
| attestations: read | |
| contents: write | |
| concurrency: | |
| group: release-plan-recovery-sdk-python-${{ inputs.plan_tag || 'latest' }} | |
| cancel-in-progress: false | |
| jobs: | |
| recover: | |
| name: Recover exact Python SDK release | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Discover plan and verify upstream public artifacts | |
| id: recovery | |
| env: | |
| GITHUB_TOKEN: ${{ github.token }} | |
| REQUESTED_PLAN_TAG: ${{ inputs.plan_tag }} | |
| run: | | |
| arguments=( | |
| resolve | |
| --component sdk-python | |
| --plan-output release-plan.json | |
| --preparation-output release-preparation.json | |
| --evidence release-recovery-evidence.json | |
| --github-output "$GITHUB_OUTPUT" | |
| ) | |
| if [ "$GITHUB_EVENT_NAME" = schedule ]; then | |
| arguments+=(--allow-empty) | |
| elif [ -n "$REQUESTED_PLAN_TAG" ]; then | |
| arguments+=(--plan-tag "$REQUESTED_PLAN_TAG") | |
| fi | |
| python scripts/ci/component-release-recovery.py "${arguments[@]}" | |
| - name: Create the exact source tag | |
| if: steps.recovery.outputs.action == 'publish' | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| RELEASE_TAG: ${{ steps.recovery.outputs.version }} | |
| RELEASE_COMMIT: ${{ steps.recovery.outputs.commit }} | |
| run: | | |
| set -euo pipefail | |
| if ! gh api "repos/$GITHUB_REPOSITORY/git/ref/tags/$RELEASE_TAG" >/dev/null 2>&1; then | |
| gh api --method POST "repos/$GITHUB_REPOSITORY/git/refs" \ | |
| -f ref="refs/tags/$RELEASE_TAG" -f sha="$RELEASE_COMMIT" >/dev/null | |
| fi | |
| - name: Start or resume repository-owned publication | |
| if: steps.recovery.outputs.action == 'publish' | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| PLAN_TAG: ${{ steps.recovery.outputs.plan_tag }} | |
| RELEASE_TAG: ${{ steps.recovery.outputs.version }} | |
| RELEASE_COMMIT: ${{ steps.recovery.outputs.commit }} | |
| run: | | |
| set -euo pipefail | |
| decision= | |
| for attempt in 1 2 3 4 5 6; do | |
| gh run list --workflow publish.yml --limit 100 \ | |
| --json databaseId,displayTitle,headBranch,headSha,status,conclusion \ | |
| > publication-runs.json | |
| decision="$(python scripts/ci/component-release-recovery.py select-publication-run \ | |
| --release-tag "$RELEASE_TAG" --release-commit "$RELEASE_COMMIT" \ | |
| --runs publication-runs.json)" | |
| IFS=$'\t' read -r publication_action run_id status conclusion <<< "$decision" | |
| if [ "$publication_action" != dispatch ]; then | |
| break | |
| fi | |
| if [ "$attempt" -lt 6 ]; then | |
| sleep 5 | |
| fi | |
| done | |
| if [ "$publication_action" = dispatch ]; then | |
| gh workflow run publish.yml --ref "$RELEASE_TAG" \ | |
| -f release_tag="$RELEASE_TAG" -f release_plan="$PLAN_TAG" -f publish=true | |
| else | |
| printf 'Durable publication run %s is %s/%s; no duplicate dispatch is needed.\n' \ | |
| "$run_id" "$status" "${conclusion:-pending}" | |
| fi | |
| - name: Retain recovery evidence | |
| if: always() | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: sdk-python-release-recovery-${{ steps.recovery.outputs.plan || github.run_id }} | |
| path: | | |
| release-plan.json | |
| release-preparation.json | |
| release-recovery-evidence.json | |
| publication-runs.json | |
| if-no-files-found: warn |