Skip to content

build-and-release

build-and-release #192

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