release #40
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 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 |