Create Release #3
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
| # .github/workflows/create-release.yml | |
| # Manual workflow to create GitHub releases from stored build artifacts. | |
| # This allows you to test builds locally before making them public. | |
| name: Create Release | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| tag_name: | |
| description: 'Tag version to release (e.g., v1.0.0)' | |
| required: true | |
| type: string | |
| release_type: | |
| description: 'Release type' | |
| required: true | |
| default: 'release' | |
| type: choice | |
| options: | |
| - release | |
| - prerelease | |
| make_latest: | |
| description: 'Mark as latest release' | |
| required: true | |
| default: true | |
| type: boolean | |
| jobs: | |
| create-release: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write # To create the release | |
| actions: read # To download artifacts | |
| pull-requests: read # To read PRs for the changelog | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| ref: ${{ inputs.tag_name }} | |
| - name: Validate tag exists | |
| run: | | |
| if ! git tag --list | grep -q "^${{ inputs.tag_name }}$"; then | |
| echo "ERROR: Tag '${{ inputs.tag_name }}' does not exist!" | |
| exit 1 | |
| fi | |
| echo "SUCCESS: Tag '${{ inputs.tag_name }}' found" | |
| - name: Find build artifact | |
| id: find_artifact | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const artifactName = `PyFlowGraph-Windows-${{ inputs.tag_name }}`; | |
| // Get the commit SHA for the tag | |
| const tagRef = await github.rest.git.getRef({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| ref: 'tags/${{ inputs.tag_name }}' | |
| }); | |
| const tagSha = tagRef.data.object.sha; | |
| console.log(`Tag ${{ inputs.tag_name }} points to commit: ${tagSha}`); | |
| // Search for workflow runs that built this specific commit | |
| const workflowRuns = await github.rest.actions.listWorkflowRunsForRepo({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| workflow_id: 'windows-build.yml', | |
| head_sha: tagSha, | |
| status: 'completed', | |
| conclusion: 'success' | |
| }); | |
| if (workflowRuns.data.workflow_runs.length === 0) { | |
| throw new Error(`No successful build found for tag ${{ inputs.tag_name }} (commit: ${tagSha})`); | |
| } | |
| const runId = workflowRuns.data.workflow_runs[0].id; | |
| console.log(`Found build run: ${runId}`); | |
| // Find the artifact in that run | |
| const artifacts = await github.rest.actions.listWorkflowRunArtifacts({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| run_id: runId | |
| }); | |
| const artifact = artifacts.data.artifacts.find(a => a.name === artifactName); | |
| if (!artifact) { | |
| throw new Error(`Artifact '${artifactName}' not found in build run ${runId}`); | |
| } | |
| console.log(`Found artifact: ${artifact.name} (ID: ${artifact.id})`); | |
| core.setOutput('artifact_id', artifact.id); | |
| core.setOutput('run_id', runId); | |
| core.setOutput('artifact_name', artifact.name); | |
| - name: Download build artifact | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: PyFlowGraph-Windows-${{ inputs.tag_name }} | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| run-id: ${{ steps.find_artifact.outputs.run_id }} | |
| - name: Verify and prepare artifact for release | |
| id: verify | |
| run: | | |
| zip_file="PyFlowGraph-Windows-${{ inputs.tag_name }}.zip" | |
| # Check if PyFlowGraph.exe exists (artifact was extracted directly to working directory) | |
| if [ ! -f "PyFlowGraph.exe" ]; then | |
| echo "ERROR: PyFlowGraph.exe not found in current directory" | |
| echo "Directory contents:" | |
| ls -la | |
| exit 1 | |
| fi | |
| # Verify key components exist | |
| if [ ! -d "examples" ] || [ ! -d "python_runtime" ]; then | |
| echo "ERROR: Missing required directories (examples or python_runtime)" | |
| ls -la | |
| exit 1 | |
| fi | |
| echo "SUCCESS: Found all required artifact components" | |
| echo "- PyFlowGraph.exe: $(ls -lh PyFlowGraph.exe | awk '{print $5}')" | |
| echo "- Examples directory: $(ls examples | wc -l) files" | |
| echo "- Python runtime directory exists: ✅" | |
| # Create release zip file from current directory contents | |
| echo "Creating release zip: $zip_file" | |
| if command -v zip >/dev/null 2>&1; then | |
| # Exclude git and other unnecessary files | |
| zip -r "$zip_file" . -x ".git/*" ".github/*" "src/*" "tests/*" "test_reports/*" "docs/*" "images/*" "pre-release/*" "*.md" "run.sh" "run_test_gui.bat" "*.txt" | |
| else | |
| # Fallback for systems without zip command | |
| tar -czf "${zip_file%.zip}.tar.gz" --exclude='.git' --exclude='.github' --exclude='src' --exclude='tests' --exclude='test_reports' --exclude='docs' --exclude='images' --exclude='pre-release' --exclude='*.md' --exclude='run.sh' --exclude='run_test_gui.bat' --exclude='*.txt' . | |
| zip_file="${zip_file%.zip}.tar.gz" | |
| fi | |
| file_size=$(stat -f%z "$zip_file" 2>/dev/null || stat -c%s "$zip_file" 2>/dev/null) | |
| echo "SUCCESS: Created release package: $zip_file ($file_size bytes)" | |
| echo "zip_file=$zip_file" >> $GITHUB_OUTPUT | |
| - name: Generate changelog | |
| id: changelog | |
| uses: mikepenz/release-changelog-builder-action@v4 | |
| with: | |
| fromTag: ${{ inputs.tag_name }} | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Create GitHub release | |
| id: create_release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: ${{ inputs.tag_name }} | |
| name: ${{ inputs.tag_name }} | |
| body: | | |
| ## What's Changed | |
| --- | |
| ${{ steps.changelog.outputs.changelog }} | |
| --- | |
| **Build Information:** | |
| - Built from commit: ${{ github.sha }} | |
| - Artifact: ${{ steps.verify.outputs.zip_file }} | |
| - Build run: [#${{ steps.find_artifact.outputs.run_id }}](https://github.com/${{ github.repository }}/actions/runs/${{ steps.find_artifact.outputs.run_id }}) | |
| files: ${{ steps.verify.outputs.zip_file }} | |
| prerelease: ${{ inputs.release_type == 'prerelease' }} | |
| make_latest: ${{ inputs.make_latest }} | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Release summary | |
| run: | | |
| echo "## Release Created Successfully" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "**Tag:** ${{ inputs.tag_name }}" >> $GITHUB_STEP_SUMMARY | |
| echo "**Type:** ${{ inputs.release_type }}" >> $GITHUB_STEP_SUMMARY | |
| echo "**Latest:** ${{ inputs.make_latest }}" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "**Release URL:** ${{ steps.create_release.outputs.url }}" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "The release has been published and is now available for download." |