release #26
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 | |
| on: | |
| # Auto-trigger after wheel builds complete | |
| workflow_run: | |
| workflows: ["wheels", "wheels-docker", "wstest"] | |
| types: [completed] | |
| # For PR summaries (runs in main repo context with write permissions) | |
| pull_request_target: | |
| types: [opened, synchronize, reopened] | |
| # Manual dispatch for debugging | |
| workflow_dispatch: | |
| inputs: | |
| pr_number: | |
| description: "PR number to post summary for" | |
| required: false | |
| type: string | |
| jobs: | |
| identifiers: | |
| # GitHub needs to know where .cicd/workflows/identifiers.yml lives at parse time, | |
| # and submodules aren't included in that context! thus the following does NOT work: | |
| # uses: ./.cicd/workflows/identifiers.yml | |
| # we MUST reference the remote repo directly: | |
| uses: wamp-proto/wamp-cicd/.github/workflows/identifiers.yml@main | |
| # IMPORTANT: we still need .cicd as a Git submodule in the using repo though! | |
| # because e.g. identifiers.yml wants to access scripts/sanitize.sh ! | |
| # Nightly and stable GitHub releases (consolidates wheels from both workflows) | |
| release-nightly: | |
| name: Nightly & Stable GitHub Releases | |
| needs: identifiers | |
| runs-on: ubuntu-latest | |
| # Only create releases for nightly and stable builds (explicit positive list) | |
| if: | | |
| github.event_name == 'workflow_run' && | |
| github.event.workflow_run.conclusion == 'success' && | |
| (needs.identifiers.outputs.release_type == 'nightly' || needs.identifiers.outputs.release_type == 'stable') && | |
| (github.event.workflow_run.name == 'wheels' || github.event.workflow_run.name == 'wheels-docker') | |
| env: | |
| RELEASE_TYPE: ${{ needs.identifiers.outputs.release_type }} | |
| RELEASE_NAME: ${{ needs.identifiers.outputs.release_name }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| submodules: recursive | |
| - name: Download all wheel artifacts (from wheels workflow) | |
| uses: actions/download-artifact@v4 | |
| with: | |
| pattern: wheels-* | |
| merge-multiple: true | |
| path: dist/ | |
| continue-on-error: true | |
| - name: Download source distribution | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: source-distribution | |
| path: dist/ | |
| continue-on-error: true | |
| - name: Download Linux wheels without NVX | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: linux-wheels-no-nvx | |
| path: dist/ | |
| continue-on-error: true | |
| - name: Download manylinux wheel artifacts (from wheels-docker workflow) | |
| uses: actions/download-artifact@v4 | |
| with: | |
| pattern: artifacts-* | |
| merge-multiple: true | |
| path: wheelhouse/ | |
| continue-on-error: true | |
| - name: Consolidate all artifacts | |
| run: | | |
| echo "==> Consolidating all wheel artifacts into unified release directory..." | |
| mkdir -p release-artifacts | |
| # Copy from wheels workflow | |
| if [ -d "dist" ]; then | |
| echo "Copying wheels workflow artifacts..." | |
| find dist -type f \( -name "*.whl" -o -name "*.tar.gz" \) -exec cp {} release-artifacts/ \; | |
| fi | |
| # Copy from wheels-docker workflow | |
| if [ -d "wheelhouse" ]; then | |
| echo "Copying wheels-docker workflow artifacts..." | |
| find wheelhouse -type f \( -name "*.whl" -o -name "*.tar.gz" \) -exec cp {} release-artifacts/ \; | |
| fi | |
| echo "" | |
| echo "==> Unified release artifact inventory:" | |
| ls -la release-artifacts/ || echo "No artifacts found" | |
| echo "" | |
| echo "Total wheels: $(find release-artifacts -name "*.whl" | wc -l)" | |
| echo "Source dists: $(find release-artifacts -name "*.tar.gz" | wc -l)" | |
| - name: Install jinja2-cli for template rendering | |
| run: | | |
| pip install jinja2-cli | |
| - name: Render release notes from Jinja2 template | |
| run: | | |
| echo "==> Preparing release notes using Jinja2 template..." | |
| echo "Release type: $RELEASE_TYPE" | |
| echo "Release name: $RELEASE_NAME" | |
| # Collect template variables | |
| COMMIT_SHA="${GITHUB_SHA::8}" | |
| BUILD_DATE="$(date -u +'%Y-%m-%d %H:%M:%S UTC')" | |
| WHEEL_COUNT="$(find release-artifacts -name "*.whl" | wc -l)" | |
| SDIST_COUNT="$(find release-artifacts -name "*.tar.gz" | wc -l)" | |
| # Select template based on release type | |
| if [ "$RELEASE_TYPE" = "stable" ]; then | |
| TEMPLATE=".github/templates/release-stable.md.j2" | |
| else | |
| TEMPLATE=".github/templates/release-nightly.md.j2" | |
| fi | |
| # Render template using jinja2 | |
| jinja2 "$TEMPLATE" \ | |
| -D release_name="$RELEASE_NAME" \ | |
| -D commit_sha="$COMMIT_SHA" \ | |
| -D build_date="$BUILD_DATE" \ | |
| -D wheel_count="$WHEEL_COUNT" \ | |
| -D sdist_count="$SDIST_COUNT" \ | |
| -o release-notes.md | |
| echo "" | |
| echo "==> Generated release notes:" | |
| cat release-notes.md | |
| - name: Create unified GitHub release | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| echo "==> Creating unified GitHub release..." | |
| echo "Release type: $RELEASE_TYPE" | |
| echo "Release name: $RELEASE_NAME" | |
| # Delete existing release if it exists (for nightly builds) | |
| gh release delete "$RELEASE_NAME" --repo "$GITHUB_REPOSITORY" --yes || true | |
| # Set release title based on type | |
| if [ "$RELEASE_TYPE" = "stable" ]; then | |
| TITLE="Release $RELEASE_NAME" | |
| else | |
| TITLE="Nightly Build $RELEASE_NAME" | |
| fi | |
| # Create the release using rendered notes | |
| gh release create "$RELEASE_NAME" \ | |
| --repo "$GITHUB_REPOSITORY" \ | |
| --title "$TITLE" \ | |
| --notes-file release-notes.md \ | |
| release-artifacts/* | |
| echo "✅ Release $RELEASE_NAME created successfully" | |
| # Stable release publishing: PyPI and RTD (consolidates from both wheel workflows) | |
| release-stable: | |
| name: Stable Release (PyPI & RTD) | |
| needs: [identifiers, release-nightly] | |
| runs-on: ubuntu-latest | |
| # Only publish to PyPI for stable releases (explicit positive list) | |
| if: needs.identifiers.outputs.release_type == 'stable' | |
| env: | |
| RELEASE_TYPE: ${{ needs.identifiers.outputs.release_type }} | |
| RELEASE_NAME: ${{ needs.identifiers.outputs.release_name }} | |
| environment: | |
| name: pypi | |
| url: https://pypi.org/p/autobahn | |
| permissions: | |
| id-token: write # For trusted publishing | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| submodules: recursive | |
| - name: Download macOS wheels | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: wheels-macos-arm64 | |
| path: dist/ | |
| continue-on-error: true | |
| - name: Download Windows wheels | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: wheels-windows-x86_64 | |
| path: dist/ | |
| continue-on-error: true | |
| - name: Download source distribution | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: source-distribution | |
| path: dist/ | |
| continue-on-error: true | |
| - name: Download Linux wheels without NVX | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: linux-wheels-no-nvx | |
| path: dist/ | |
| continue-on-error: true | |
| - name: Download manylinux wheels with NVX (from wheels-docker) | |
| uses: actions/download-artifact@v4 | |
| with: | |
| pattern: artifacts-* | |
| merge-multiple: true | |
| path: dist/ | |
| continue-on-error: true | |
| - name: List artifacts for PyPI publishing | |
| run: | | |
| echo "Publishing to PyPI for release: $RELEASE_NAME" | |
| ls -la dist/ | |
| echo "" | |
| echo "macOS wheels: $(find dist -name "*macos*.whl" 2>/dev/null | wc -l)" | |
| echo "Windows wheels: $(find dist -name "*win*.whl" 2>/dev/null | wc -l)" | |
| echo "Linux manylinux wheels: $(find dist -name "*manylinux*.whl" 2>/dev/null | wc -l)" | |
| echo "Linux fallback wheels: $(find dist -name "*linux*.whl" ! -name "*manylinux*.whl" 2>/dev/null | wc -l)" | |
| echo "Source distributions: $(find dist -name "*.tar.gz" 2>/dev/null | wc -l)" | |
| echo "" | |
| echo "Total PyPI artifacts: $(find dist -type f \( -name "*.whl" -o -name "*.tar.gz" \) | wc -l)" | |
| - name: Publish to PyPI | |
| uses: pypa/gh-action-pypi-publish@release/v1 | |
| with: | |
| # Uses trusted publishing - no API token needed | |
| # Configure at: https://pypi.org/manage/account/publishing/ | |
| verbose: true | |
| - name: Trigger RTD build | |
| env: | |
| RTD_TOKEN: ${{ secrets.RTD_TOKEN }} | |
| run: | | |
| if [ -n "$RTD_TOKEN" ]; then | |
| echo "Triggering Read the Docs build for autobahn..." | |
| curl -X POST \ | |
| -H "Authorization: Token $RTD_TOKEN" \ | |
| "https://readthedocs.org/api/v3/projects/autobahn/versions/latest/builds/" | |
| echo "✅ RTD build triggered successfully" | |
| else | |
| echo "⚠️ RTD_TOKEN not configured, skipping RTD build trigger" | |
| fi | |
| # Development release: Post PR summary comment for development builds | |
| release-development: | |
| name: Development Release (PR Summary) | |
| needs: identifiers | |
| runs-on: ubuntu-latest | |
| # Only run for PR/development contexts | |
| if: | | |
| (github.event_name == 'pull_request_target') || | |
| (github.event_name == 'workflow_run' && | |
| github.event.workflow_run.event == 'pull_request' && | |
| github.event.workflow_run.conclusion == 'success') | |
| env: | |
| BASE_REPO: ${{ needs.identifiers.outputs.base_repo }} | |
| BASE_BRANCH: ${{ needs.identifiers.outputs.base_branch }} | |
| PR_NUMBER: ${{ needs.identifiers.outputs.pr_number }} | |
| PR_REPO: ${{ needs.identifiers.outputs.pr_repo }} | |
| PR_BRANCH: ${{ needs.identifiers.outputs.pr_branch }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| submodules: recursive | |
| - name: Get PR information | |
| id: pr-info | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| let pr_number, head_repo, head_ref; | |
| if (context.eventName === 'workflow_dispatch') { | |
| // Manual dispatch - get PR info from input | |
| pr_number = context.payload.inputs.pr_number; | |
| const { data: pr } = await github.rest.pulls.get({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| pull_number: pr_number | |
| }); | |
| head_repo = pr.head.repo.full_name; | |
| head_ref = pr.head.ref; | |
| } else if (context.eventName === 'pull_request_target') { | |
| // pull_request_target trigger - get PR info from event | |
| pr_number = context.payload.pull_request.number; | |
| head_repo = context.payload.pull_request.head.repo.full_name; | |
| head_ref = context.payload.pull_request.head.ref; | |
| } else { | |
| // workflow_run trigger - get PR info from workflow_run event | |
| const prs = context.payload.workflow_run.pull_requests; | |
| if (!prs || prs.length === 0) { | |
| core.setFailed('No PR found in workflow_run event'); | |
| return; | |
| } | |
| pr_number = prs[0].number; | |
| head_repo = prs[0].head.repo.full_name; | |
| head_ref = prs[0].head.ref; | |
| } | |
| console.log(`PR: #${pr_number}, Repo: ${head_repo}, Branch: ${head_ref}`); | |
| // Set outputs for next steps | |
| core.setOutput('pr_number', pr_number); | |
| core.setOutput('head_repo', head_repo); | |
| core.setOutput('head_ref', head_ref); | |
| - name: Download wstest conformance summary | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: conformance-summary-${{ github.event.workflow_run.conclusion == 'success' && 'quick' || env.TEST_MODE || 'quick' }} | |
| path: summary-artifact/ | |
| continue-on-error: true | |
| - name: Install jinja2-cli for template rendering | |
| run: | | |
| pip install jinja2-cli | |
| - name: Render PR comment from Jinja2 template | |
| id: render | |
| run: | | |
| echo "==> Preparing PR comment using Jinja2 template..." | |
| # Collect template variables | |
| PR_NUMBER="${{ steps.pr-info.outputs.pr_number }}" | |
| PR_REPO="${{ steps.pr-info.outputs.head_repo }}" | |
| PR_BRANCH="${{ steps.pr-info.outputs.head_ref }}" | |
| BASE_REPO="${BASE_REPO}" | |
| BASE_BRANCH="${BASE_BRANCH}" | |
| COMMIT_SHA="${GITHUB_SHA::8}" | |
| BUILD_DATE="$(date -u +'%Y-%m-%d %H:%M:%S UTC')" | |
| WORKFLOW_RUN_URL="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" | |
| # Read wstest summary if available, otherwise use placeholder | |
| WSTEST_SUMMARY="WebSocket conformance testing results not available." | |
| SUMMARY_FILE=$(find summary-artifact/ -name "*wstest-summary.md" 2>/dev/null | head -1) | |
| if [[ -n "$SUMMARY_FILE" && -f "$SUMMARY_FILE" ]]; then | |
| echo "✅ Found wstest summary: $SUMMARY_FILE" | |
| WSTEST_SUMMARY=$(cat "$SUMMARY_FILE") | |
| WHEEL_COUNT=$(find . -name "*.whl" 2>/dev/null | wc -l) | |
| else | |
| echo "⚠️ No wstest summary found, using placeholder" | |
| WHEEL_COUNT="N/A" | |
| fi | |
| # Render template using jinja2 | |
| jinja2 .github/templates/release-development.md.j2 \ | |
| -D pr_number="$PR_NUMBER" \ | |
| -D pr_repo="$PR_REPO" \ | |
| -D pr_branch="$PR_BRANCH" \ | |
| -D base_repo="$BASE_REPO" \ | |
| -D base_branch="$BASE_BRANCH" \ | |
| -D commit_sha="$COMMIT_SHA" \ | |
| -D build_date="$BUILD_DATE" \ | |
| -D workflow_run_url="$WORKFLOW_RUN_URL" \ | |
| -D wheel_count="$WHEEL_COUNT" \ | |
| -D wstest_summary="$WSTEST_SUMMARY" \ | |
| -o pr-comment.md | |
| echo "" | |
| echo "==> Generated PR comment:" | |
| cat pr-comment.md | |
| # Check if we should post comment | |
| if [[ -n "$SUMMARY_FILE" && -f "$SUMMARY_FILE" ]]; then | |
| echo "should_comment=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "should_comment=false" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Post PR comment using GitHub CLI | |
| if: steps.render.outputs.should_comment == 'true' | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| echo "==> Posting comment to PR #${{ steps.pr-info.outputs.pr_number }}..." | |
| gh pr comment "${{ steps.pr-info.outputs.pr_number }}" \ | |
| --repo "$GITHUB_REPOSITORY" \ | |
| --body-file pr-comment.md | |
| echo "✅ PR comment posted successfully" | |
| - name: Skip PR comment | |
| if: steps.render.outputs.should_comment == 'false' | |
| run: | | |
| echo "ℹ️ Skipping PR comment - wstest summary not available" |