Skip to content

release

release #106

Workflow file for this run

name: release
on:
# Auto-trigger after all build/test workflows complete
workflow_run:
workflows: ["wheels", "wheels-docker", "wstest", "main"]
types: [completed]
# Manual dispatch for debugging
workflow_dispatch:
jobs:
check-all-workflows:
name: Check if all workflows completed
runs-on: ubuntu-latest
outputs:
all_complete: ${{ steps.check.outputs.all_complete }}
wheels_run_id: ${{ steps.check.outputs.wheels_run_id }}
wheels_docker_run_id: ${{ steps.check.outputs.wheels_docker_run_id }}
wstest_run_id: ${{ steps.check.outputs.wstest_run_id }}
main_run_id: ${{ steps.check.outputs.main_run_id }}
steps:
- name: Check all required workflows completed
id: check
uses: actions/github-script@v7
with:
script: |
const requiredWorkflows = ['wheels', 'wheels-docker', 'wstest', 'main'];
const commitSha = context.payload.workflow_run.head_sha;
console.log('─────────────────────────────────────────────────');
console.log('🔍 Checking workflow completion status');
console.log('─────────────────────────────────────────────────');
console.log(`Commit SHA: ${commitSha}`);
console.log(`Triggered by: ${context.payload.workflow_run.name}`);
console.log('');
// Get all workflow runs for this commit
const { data: runs } = await github.rest.actions.listWorkflowRunsForRepo({
owner: context.repo.owner,
repo: context.repo.repo,
head_sha: commitSha,
per_page: 100
});
// Group by workflow name and find latest run for each
const latestRuns = {};
for (const run of runs.workflow_runs) {
const workflowName = run.name;
if (requiredWorkflows.includes(workflowName)) {
if (!latestRuns[workflowName] || run.id > latestRuns[workflowName].id) {
latestRuns[workflowName] = run;
}
}
}
// Check if all required workflows completed successfully
console.log('Required workflows status:');
const allComplete = requiredWorkflows.every(name => {
const run = latestRuns[name];
const complete = run && run.status === 'completed' && run.conclusion === 'success';
const status = run ? `${run.status}/${run.conclusion}` : 'not found';
console.log(` ${complete ? '✅' : '⏳'} ${name.padEnd(20)} : ${status}`);
return complete;
});
console.log('');
if (!allComplete) {
console.log('⏳ Not all workflows complete yet - exiting early');
console.log(' This is normal! Release will proceed once all workflows finish.');
} else {
console.log('✅ All workflows complete - proceeding with release!');
}
console.log('─────────────────────────────────────────────────');
core.setOutput('all_complete', allComplete ? 'true' : 'false');
// Output run IDs for artifact downloads (using sanitized names)
core.setOutput('wheels_run_id', latestRuns['wheels']?.id || '');
core.setOutput('wheels_docker_run_id', latestRuns['wheels-docker']?.id || '');
core.setOutput('wstest_run_id', latestRuns['wstest']?.id || '');
core.setOutput('main_run_id', latestRuns['main']?.id || '');
identifiers:
needs: check-all-workflows
if: needs.check-all-workflows.outputs.all_complete == 'true'
# 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: [check-all-workflows, identifiers]
runs-on: ubuntu-latest
# Only create releases for nightly and stable builds (explicit positive list)
if: |
needs.check-all-workflows.outputs.all_complete == 'true' &&
github.event_name == 'workflow_run' &&
github.event.workflow_run.conclusion == 'success' &&
(needs.identifiers.outputs.release_type == 'nightly' || needs.identifiers.outputs.release_type == 'stable')
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/
run-id: ${{ needs.check-all-workflows.outputs.wheels_run_id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
continue-on-error: true
- name: Download source distribution
uses: actions/download-artifact@v4
with:
name: source-distribution
path: dist/
run-id: ${{ needs.check-all-workflows.outputs.wheels_run_id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
continue-on-error: true
- name: Download Linux wheels without NVX
uses: actions/download-artifact@v4
with:
name: linux-wheels-no-nvx
path: dist/
run-id: ${{ needs.check-all-workflows.outputs.wheels_run_id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
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/
run-id: ${{ needs.check-all-workflows.outputs.wheels_docker_run_id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
continue-on-error: true
- name: Download wstest conformance summary
uses: actions/download-artifact@v4
with:
pattern: conformance-summary-*
merge-multiple: true
path: wstest-results/
run-id: ${{ needs.check-all-workflows.outputs.wstest_run_id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
continue-on-error: true
- name: Download FlatBuffers schema artifacts
uses: actions/download-artifact@v4
with:
pattern: flatbuffers-schema-*
merge-multiple: true
path: flatbuffers-schema/
run-id: ${{ needs.check-all-workflows.outputs.main_run_id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
continue-on-error: true
- name: Consolidate all artifacts
run: |
echo "==> Consolidating all artifacts into unified release directory..."
mkdir -p release-artifacts
# Copy wheels 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 wheels 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
# Copy wstest conformance results
if [ -d "wstest-results" ]; then
echo "Copying wstest conformance results..."
find wstest-results -type f -exec cp {} release-artifacts/ \;
fi
# Package FlatBuffers schema as tarball
if [ -d "flatbuffers-schema" ]; then
echo "Packaging FlatBuffers schema..."
tar -czf release-artifacts/flatbuffers-schema.tar.gz -C flatbuffers-schema .
fi
echo ""
echo "==> Unified release artifact inventory:"
ls -la release-artifacts/ || echo "No artifacts found"
echo ""
echo "Wheels: $(find release-artifacts -name "*.whl" | wc -l)"
echo "Source dists: $(find release-artifacts -name "*.tar.gz" ! -name "flatbuffers-schema.tar.gz" | wc -l)"
echo "Wstest reports: $(find release-artifacts -name "*wstest*" | wc -l)"
echo "FlatBuffers schema: $(ls release-artifacts/flatbuffers-schema.tar.gz 2>/dev/null && echo 'packaged' || echo 'not found')"
- 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: [check-all-workflows, identifiers, release-nightly]
runs-on: ubuntu-latest
# Only publish to PyPI for stable releases (explicit positive list)
if: |
needs.check-all-workflows.outputs.all_complete == 'true' &&
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/
run-id: ${{ needs.check-all-workflows.outputs.wheels_run_id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
continue-on-error: true
- name: Download Windows wheels
uses: actions/download-artifact@v4
with:
name: wheels-windows-x86_64
path: dist/
run-id: ${{ needs.check-all-workflows.outputs.wheels_run_id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
continue-on-error: true
- name: Download source distribution
uses: actions/download-artifact@v4
with:
name: source-distribution
path: dist/
run-id: ${{ needs.check-all-workflows.outputs.wheels_run_id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
continue-on-error: true
- name: Download Linux wheels without NVX
uses: actions/download-artifact@v4
with:
name: linux-wheels-no-nvx
path: dist/
run-id: ${{ needs.check-all-workflows.outputs.wheels_run_id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
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/
run-id: ${{ needs.check-all-workflows.outputs.wheels_docker_run_id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
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