build-and-release #192
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: build-and-release | |
| on: | |
| # Run daily at 10AM UTC | |
| schedule: | |
| - cron: "0 10 * * *" | |
| # Allow manual activations. | |
| workflow_dispatch: | |
| inputs: | |
| apio_repo: | |
| description: "[Optional] Apio source repo (fpgawars/apio)" | |
| required: false | |
| apio_branch: | |
| description: "[Optional] Apio source branch (develop)" | |
| required: false | |
| commit_sha: | |
| description: "[Optional] Apio commit SHA to build (latest)" | |
| required: false | |
| permissions: | |
| # Allow release creation | |
| contents: write | |
| env: | |
| # Python version to use (e.g. for pyinstaller binary) | |
| PYTHON_VERSION: "3.14" | |
| # Apio github repo to use. | |
| APIO_REPO: ${{github.event.inputs.apio_repo || 'fpgawars/apio'}} | |
| # Apio repo branch to use. | |
| APIO_BRANCH: ${{github.event.inputs.apio_branch || 'develop'}} | |
| jobs: | |
| # ----- Parameters collection job | |
| prepare: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| python_version: ${{ steps.set-output.outputs.python_version }} | |
| steps: | |
| - name: Checkout this repo | |
| uses: actions/checkout@v4 | |
| - name: Checkout the apio repo | |
| uses: actions/checkout@v4 | |
| with: | |
| repository: ${{env.APIO_REPO}} | |
| ref: ${{env.BRANCH}} | |
| path: apio-repo | |
| fetch-depth: 0 | |
| - name: Determine Apio commit to use | |
| run: | | |
| apio_latest_commit=$(git -C apio-repo rev-parse HEAD) | |
| echo "APIO_LATEST_COMMIT=$apio_latest_commit" >> $GITHUB_ENV | |
| # Strip leading and trailing spaces | |
| sha="$(echo "${{github.event.inputs.commit_sha}}" | xargs)" | |
| # User specified commit SHA. | |
| if [ -n "$sha" ]; then | |
| echo "APIO_COMMIT=$sha" >> $GITHUB_ENV | |
| echo "COMMIT_NOTE=Using manual Apio commit" >> $GITHUB_ENV | |
| # Default behavior, use latest commit. | |
| else | |
| echo "APIO_COMMIT=$apio_latest_commit" >> $GITHUB_ENV | |
| echo "COMMIT_NOTE=Using latest Apio commit" >> $GITHUB_ENV | |
| fi | |
| - name: Check selected Apio commit | |
| run: | | |
| git -C apio-repo fetch origin | |
| git -C apio-repo checkout "$APIO_COMMIT" | |
| echo "$COMMIT_NOTE" | |
| echo "Selected: $APIO_COMMIT" | |
| echo "Latest: $APIO_LATEST_COMMIT" | |
| echo "Current: $(git -C apio-repo rev-parse HEAD)" | |
| - name: Install python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: ${{env.PYTHON_VERSION}} | |
| # We install apio so we can get its version number. | |
| - name: Pip install apio | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install -e apio-repo | |
| # Create the build configuration that we will pass to the other jobs. | |
| - name: Create build info | |
| run: | | |
| # Collect values | |
| builder_commit=$(git rev-parse HEAD) | |
| apio_commit=$(git -C apio-repo rev-parse HEAD) | |
| apio_branch=$(git -C apio-repo rev-parse --abbrev-ref HEAD) | |
| apio_version="$(pip show apio | grep Version: | cut -d ' ' -f2)" | |
| release_tag="$(date +'%Y-%m-%d')" | |
| package_tag="${release_tag//-/}" | |
| # Generate build-info.json | |
| cat > build-info.json <<EOF | |
| { | |
| "package-name": "apio", | |
| "description" : "FPGA design tool", | |
| "apio-repo": "${{env.APIO_REPO}}", | |
| "requested-apio-commit" : "${{github.event.inputs.commit_sha}}", | |
| "apio-commit": "$apio_commit", | |
| "apio-branch": "$apio_branch", | |
| "apio-version": "$apio_version", | |
| "release-tag": "$release_tag", | |
| "package-tag": "$package_tag", | |
| "build-time": "$(date +'%Y-%m-%d %H:%M:%S %Z')", | |
| "build-repo": "${{github.repository}}", | |
| "build-commit": "$builder_commit", | |
| "build-workflow": "${{ github.workflow }}", | |
| "workflow-run-id": "${{github.run_id}}", | |
| "workflow-run-number": "${{github.run_number}}" | |
| } | |
| EOF | |
| cat -n build-info.json | |
| - name: Format build info | |
| run: | | |
| npm install -g json-align | |
| json-align --in-place --spaces 2 build-info.json | |
| cat -n build-info.json | |
| - name: Upload the build info artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| if-no-files-found: error | |
| name: build-info | |
| path: build-info.json | |
| - name: Set python version output | |
| id: set-output | |
| run: | | |
| echo "python_version=${{env.PYTHON_VERSION}}" >> $GITHUB_OUTPUT | |
| # ----- Children build jobs | |
| # We dispatch separate child workflows because each pyinstaller builder needs to | |
| # run on it's target architecture. | |
| build-darwin-arm64: | |
| needs: prepare | |
| uses: ./.github/workflows/build-darwin-arm64.yaml | |
| secrets: inherit | |
| with: | |
| python_version: ${{ needs.prepare.outputs.python_version }} | |
| build-linux-x86-64: | |
| needs: prepare | |
| uses: ./.github/workflows/build-linux-x86-64.yaml | |
| secrets: inherit | |
| with: | |
| python_version: ${{ needs.prepare.outputs.python_version }} | |
| build-windows-amd64: | |
| needs: prepare | |
| uses: ./.github/workflows/build-windows-amd64.yaml | |
| secrets: inherit | |
| with: | |
| python_version: ${{ needs.prepare.outputs.python_version }} | |
| # # ----- Release creation job | |
| release: | |
| needs: | |
| [prepare, build-darwin-arm64, build-linux-x86-64, build-windows-amd64] | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Checkout this repo | |
| uses: actions/checkout@v4 | |
| - name: Download the artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| merge-multiple: true | |
| path: _artifacts | |
| # Repo: https://github.com/Cisco-Talos/clamav | |
| # Home: https://www.clamav.net/ | |
| - name: Install anti virus (ClamAV) | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y clamav clamav-daemon | |
| - name: Update virus signatures | |
| run: | | |
| sudo systemctl stop clamav-freshclam || true | |
| sudo freshclam --verbose | |
| - name: Display clamav version (app/database) | |
| run: clamscan --version | |
| # Scans recursively inside the .tgz packages. | |
| # See : https://linux.die.net/man/1/clamscan | |
| # - name: Scan the packages for viruses | |
| # run: | | |
| # clamscan -r --verbose --scan-archive=yes _artifacts/apio-*.* | |
| - name: Move build info | |
| run: | | |
| find _artifacts | |
| mv _artifacts/build-info.json . | |
| cat -n build-info.json | |
| - name: Set env vars from the build info | |
| run: | | |
| VARS=( | |
| "APIO_REPO apio-repo" | |
| "APIO_COMMIT apio-commit" | |
| "APIO_VERSION apio-version" | |
| "RELEASE_TAG release-tag" | |
| "PACKAGE_TAG package-tag" | |
| ) | |
| for pair in "${VARS[@]}"; do | |
| read var key <<< "$pair" | |
| value=$(jq -r --arg k "$key" '.[$k]' build-info.json) | |
| echo "$var=$value" | |
| echo "$var=$value" >> "$GITHUB_ENV" | |
| done | |
| # Summary for the run page. | |
| - name: Prepare run summary | |
| run: | | |
| cat > RUN-SUMMARY.txt <<EOF | |
| Build info: | |
| \`\`\` | |
| $(tr -d '",{}' < build-info.json) | |
| \`\`\` | |
| [Cutoff commit](https://github.com/${{env.APIO_REPO}}/commit/${{env.APIO_COMMIT}}) | |
| EOF | |
| ls -al | |
| cat -n $out | |
| - name: Add summary to run | |
| run: | | |
| cat RUN-SUMMARY.txt >> $GITHUB_STEP_SUMMARY | |
| # Notes for the release page. | |
| - name: Prepare release text | |
| run: | | |
| cat > RELEASE-BODY.txt <<EOF | |
| This is the daily build of the Apio \`develop\` branch. | |
| > This build repository keeps only the last N releases so make sure | |
| > to keep a copy of releases you want to preserve. | |
| Build info: | |
| \`\`\` | |
| $(tr -d '",{}' < build-info.json) | |
| \`\`\` | |
| Additional resources: | |
| * [Installation instructions](https://fpgawars.github.io/apio/installing-apio) | |
| * [Official Apio releases](https://github.com/FPGAwars/apio/releases) | |
| EOF | |
| cat -n $out | |
| # - name: Delete old releases | |
| # uses: Nats-ji/delete-old-releases@v1 | |
| # with: | |
| # token: ${{secrets.GITHUB_TOKEN}} | |
| # keep-count: ${{env.RELEASE_KEEP_COUNT}} | |
| # keep-old-minor-releases: false | |
| # # - Delete old releases that marks as 'pre release' and their | |
| # # - tags. Does not affect release that are not marked 'prerelease'. | |
| # - name: Clean up old pre-releases | |
| # uses: actions/github-script@v7 | |
| # with: | |
| # script: | | |
| # const KEEP_COUNT = 10; | |
| # console.log(`Retention policy: keep the ${KEEP_COUNT} newest pre-releases`); | |
| # console.log(`Regular (stable) releases are never deleted\n`); | |
| # const owner = "${{ github.repository_owner }}"; | |
| # const repo = "${{ github.repository }}".split("/")[1]; | |
| # console.log(`Running cleanup for ${owner}/${repo}`); | |
| # const releases = await github.rest.repos.listReleases({ | |
| # owner, | |
| # repo, | |
| # per_page: 100 | |
| # }); | |
| # // Separate pre-releases and regular releases | |
| # const preReleases = releases.data | |
| # .filter(r => r.prerelease) | |
| # .sort((a, b) => b.created_at.localeCompare(a.created_at)); | |
| # const regularReleases = releases.data.filter(r => !r.prerelease); | |
| # // Determine which pre-releases to delete (older than KEEP_COUNT) | |
| # const toKeep = preReleases.slice(0, KEEP_COUNT); | |
| # const toDelete = preReleases.slice(KEEP_COUNT); | |
| # // Log status for every single release | |
| # for (const r of preReleases) { | |
| # if (toKeep.includes(r)) { | |
| # console.log(`Kept pre-release ${r.tag_name} (${new Date(r.created_at).toLocaleDateString()})`); | |
| # } else { | |
| # console.log(`Deleted pre-release ${r.tag_name} (${new Date(r.created_at).toLocaleDateString()})`); | |
| # await github.rest.repos.deleteRelease({ owner, repo, release_id: r.id }); | |
| # await github.rest.git.deleteRef({ owner, repo, ref: `tags/${r.tag_name}` }); | |
| # } | |
| # } | |
| # for (const r of regularReleases) { | |
| # console.log(`Kept release ${r.tag_name} (${new Date(r.created_at).toLocaleDateString()})`); | |
| # } | |
| # // Final summary | |
| # console.log('\n=== Cleanup Summary ==='); | |
| # console.log(`Pre-releases kept : ${toKeep.length}`); | |
| # console.log(`Pre-releases deleted : ${toDelete.length}`); | |
| # console.log(`Regular releases found: ${regularReleases.length} (untouched)`); | |
| # console.log(`Total releases found : ${releases.data.length}`); | |
| # Delete old pre-releases, keeps all releases. | |
| - name: Delete old pre-releases | |
| uses: sgpublic/delete-release-action@v1.2 | |
| with: | |
| # Keep all stable releases . | |
| release-drop: false | |
| # Delete old pre-releases, keep the newest 10 (excluding the absolute latest) | |
| pre-release-drop: true | |
| pre-release-keep-count: 5 | |
| pre-release-drop-tag: true # Also delete tags of deleted pre-releases | |
| # Keep all drafts. | |
| draft-drop: false | |
| env: | |
| GITHUB_TOKEN: ${{ github.token }} | |
| # In case we overwrite an exiting release, we want | |
| # to update it's commit. This doesn't update the | |
| # release time. | |
| - name: Force tag update | |
| run: | | |
| git tag -f ${{env.RELEASE_TAG}} | |
| git push origin -f ${{env.RELEASE_TAG}} | |
| - name: Create GitHub pre-release | |
| uses: softprops/action-gh-release@v2.2.2 | |
| with: | |
| tag_name: ${{env.RELEASE_TAG}} | |
| name: ${{env.RELEASE_TAG}} | |
| body_path: RELEASE-BODY.txt | |
| prerelease: true | |
| preserve_order: true | |
| fail_on_unmatched_files: true | |
| # We intensionally list explicitly all the expected files. | |
| files: | | |
| build-info.json | |
| _artifacts/apio-darwin-arm64-${{env.PACKAGE_TAG}}-bundle.tgz | |
| _artifacts/apio-darwin-arm64-${{env.PACKAGE_TAG}}-installer.pkg | |
| _artifacts/apio-linux-x86-64-${{env.PACKAGE_TAG}}-bundle.tgz | |
| _artifacts/apio-linux-x86-64-${{env.PACKAGE_TAG}}-debian.deb | |
| _artifacts/apio-windows-amd64-${{env.PACKAGE_TAG}}-bundle.zip | |
| _artifacts/apio-windows-amd64-${{env.PACKAGE_TAG}}-installer.exe |