Skip to content

release

release #40

Workflow file for this run

name: release
on:
# Auto-trigger after main workflow completes
workflow_run:
workflows: ["main"]
types: [completed]
# Manual dispatch for debugging
workflow_dispatch:
permissions:
id-token: write # Required for PyPI trusted publishing (OIDC)
contents: write # Required for creating GitHub releases
discussions: write # Required for posting to discussions
pull-requests: read # Required for identifiers workflow
actions: read # Required for downloading artifacts
jobs:
# ============================================================================
# Check if main workflow completed successfully
# ============================================================================
check-main-workflow:
name: Check if main workflow completed
runs-on: ubuntu-latest
outputs:
main_run_id: ${{ steps.check.outputs.main_run_id }}
should_proceed: ${{ steps.check.outputs.should_proceed }}
artifact_dist: ${{ steps.check.outputs.artifact_dist }}
steps:
- name: Check main workflow completion
id: check
uses: actions/github-script@v7
with:
script: |
const commitSha = context.payload.workflow_run?.head_sha || context.sha;
const triggeredBy = context.payload.workflow_run?.name || 'manual (workflow_dispatch)';
console.log('─────────────────────────────────────────────────');
console.log('🔍 Checking main workflow completion status');
console.log('─────────────────────────────────────────────────');
console.log(`Event: ${context.eventName}`);
console.log(`Commit SHA: ${commitSha}`);
console.log(`Triggered by: ${triggeredBy}`);
console.log('');
// Get 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
});
// Find latest main workflow run
const mainRun = runs.workflow_runs
.filter(run => run.name === 'main')
.sort((a, b) => b.id - a.id)[0];
const mainComplete = mainRun && mainRun.status === 'completed' && mainRun.conclusion === 'success';
const status = mainRun ? `${mainRun.status}/${mainRun.conclusion}` : 'not found';
console.log(`Main workflow: ${status}`);
console.log('');
// Find artifact name with checksum suffix
let artifactDist = '';
if (mainComplete && mainRun) {
const { data: artifacts } = await github.rest.actions.listWorkflowRunArtifacts({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: mainRun.id
});
const distArtifact = artifacts.artifacts.find(a => a.name.startsWith('dist-packages-'));
if (distArtifact) {
artifactDist = distArtifact.name;
console.log(`Found artifact: ${artifactDist}`);
} else {
console.log('⚠️ No dist-packages artifact found');
}
}
if (!mainComplete) {
console.log('⏳ Main workflow not complete yet - exiting early');
console.log(' This is normal! Release will proceed once main workflow finishes.');
} else {
console.log('✅ Main workflow complete - proceeding with release!');
}
console.log('─────────────────────────────────────────────────');
core.setOutput('should_proceed', mainComplete ? 'true' : 'false');
core.setOutput('main_run_id', mainRun?.id || '');
core.setOutput('artifact_dist', artifactDist);
# ============================================================================
# Identifiers (extract release info)
# ============================================================================
identifiers:
name: Identifiers
needs: check-main-workflow
if: needs.check-main-workflow.outputs.should_proceed == 'true'
uses: wamp-proto/wamp-cicd/.github/workflows/identifiers.yml@main
# ============================================================================
# Development/Nightly GitHub releases (for untagged master builds)
# ============================================================================
release-development:
name: Development GitHub Release
needs: [check-main-workflow, identifiers]
runs-on: ubuntu-24.04
# Only create releases for development/nightly builds (not stable tags)
if: |
needs.check-main-workflow.outputs.should_proceed == 'true' &&
(github.event_name == 'workflow_dispatch' ||
(github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success')) &&
(needs.identifiers.outputs.release_type == 'development' || needs.identifiers.outputs.release_type == 'nightly')
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 distribution artifacts (verified)
uses: wamp-proto/wamp-cicd/actions/download-artifact-verified@main
with:
name: ${{ needs.check-main-workflow.outputs.artifact_dist }}
path: ${{ github.workspace }}/dist
run-id: ${{ needs.check-main-workflow.outputs.main_run_id }}
- name: List downloaded packages
run: |
echo "Downloaded packages:"
ls -lh ${{ github.workspace }}/dist/
- name: Validate release fileset
uses: wamp-proto/wamp-cicd/actions/check-release-fileset@main
with:
dist-path: ${{ github.workspace }}/dist
expected-wheels: 1
expected-sdists: 1
wheel-pattern: "py3-none-any"
- name: Generate checksums
run: |
cd ${{ github.workspace }}/dist
sha256sum * > CHECKSUMS-ALL.sha256
cat CHECKSUMS-ALL.sha256
- name: Create development GitHub release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ needs.identifiers.outputs.release_name }}
name: Development Build ${{ needs.identifiers.outputs.release_name }}
body: |
## cfxdb Development Build
**Release:** ${{ needs.identifiers.outputs.release_name }}
**Type:** ${{ needs.identifiers.outputs.release_type }}
**Branch:** ${{ needs.identifiers.outputs.base_branch }}
**Commit:** ${{ needs.identifiers.outputs.head_sha }}
This is an automated development/nightly build for testing purposes.
**Installation:**
```bash
pip install https://github.com/crossbario/cfxdb/releases/download/${{ needs.identifiers.outputs.release_name }}/cfxdb-*.whl
```
**Files:**
- Universal wheel (py3-none-any)
- Source distribution (.tar.gz)
- SHA256 checksums (CHECKSUMS-ALL.sha256)
⚠️ This is a development build - for production use, install from PyPI.
files: ${{ github.workspace }}/dist/*
draft: false
prerelease: true
discussion_category_name: ci-cd
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# ============================================================================
# Production releases (for tagged stable builds) - GitHub + PyPI
# ============================================================================
release-production:
name: Production Release (GitHub + PyPI)
needs: [check-main-workflow, identifiers]
runs-on: ubuntu-24.04
# Only publish to PyPI for stable releases (explicit tag check)
if: |
needs.check-main-workflow.outputs.should_proceed == 'true' &&
(github.event_name == 'workflow_dispatch' ||
(github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success')) &&
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/cfxdb
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
submodules: recursive
- name: Validate audit file
uses: wamp-proto/wamp-cicd/actions/validate-audit-file@main
with:
audit-dir: .audit
- name: Download distribution artifacts (verified)
uses: wamp-proto/wamp-cicd/actions/download-artifact-verified@main
with:
name: ${{ needs.check-main-workflow.outputs.artifact_dist }}
path: ${{ github.workspace }}/dist
run-id: ${{ needs.check-main-workflow.outputs.main_run_id }}
- name: List downloaded packages
run: |
echo "Downloaded packages:"
ls -lh ${{ github.workspace }}/dist/
- name: Validate release fileset
uses: wamp-proto/wamp-cicd/actions/check-release-fileset@main
with:
dist-path: ${{ github.workspace }}/dist
expected-wheels: 1
expected-sdists: 1
wheel-pattern: "py3-none-any"
- name: Generate checksums (for GitHub release)
run: |
cd ${{ github.workspace }}/dist
sha256sum * > CHECKSUMS-ALL.sha256
cat CHECKSUMS-ALL.sha256
- name: Get version
id: get_version
run: |
VERSION="${RELEASE_NAME#v}"
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "Version: $VERSION"
# IMPORTANT: tag_name MUST be explicitly specified here!
#
# When this workflow runs via workflow_run trigger (after the main workflow
# completes), github.ref is "refs/heads/master" (a branch), NOT the tag.
# This is because workflow_run context inherits from the triggering workflow's
# context, and even though the main workflow was triggered by a tag push,
# the github.ref in the workflow_run context points to the default branch.
#
# The softprops/action-gh-release action defaults to using github.ref for
# the tag name when tag_name is not specified. Since github.ref is a branch
# (not a tag), the action fails with: "GitHub Releases requires a tag"
#
# Solution: Explicitly pass tag_name using the version extracted from
# the identifiers workflow, which correctly detected the tag via
# `git describe --tags --exact-match` on the head_sha.
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
tag_name: v${{ steps.get_version.outputs.version }}
name: v${{ steps.get_version.outputs.version }}
body: |
## cfxdb v${{ steps.get_version.outputs.version }}
Crossbar.io Database - Core database access classes
**Installation:**
```bash
pip install cfxdb==${{ steps.get_version.outputs.version }}
```
**Links:**
- 📦 PyPI: https://pypi.org/project/cfxdb/${{ steps.get_version.outputs.version }}/
- 📖 Docs: https://cfxdb.readthedocs.io/
- 💻 Source: https://github.com/crossbario/cfxdb/tree/v${{ steps.get_version.outputs.version }}
See [CHANGELOG](https://github.com/crossbario/cfxdb/blob/master/CHANGELOG.md) for details.
Also published to PyPI: https://pypi.org/project/cfxdb/
files: ${{ github.workspace }}/dist/*
draft: false
prerelease: false
discussion_category_name: ci-cd
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Remove non-PyPI files before upload
run: |
echo "======================================================================"
echo "Removing files that PyPI doesn't accept"
echo "======================================================================"
rm -f ${{ github.workspace }}/dist/CHECKSUMS*.sha256
echo ""
echo "Files to upload to PyPI:"
ls -lh ${{ github.workspace }}/dist/
echo "======================================================================"
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
# Use OIDC trusted publishing (no password needed)
# Automatically generates and uploads attestations
packages-dir: ${{ github.workspace }}/dist/
verify-metadata: true
skip-existing: false
print-hash: true
attestations: true