Merge pull request #311 from nanvix/fix/304/release-to-standard-location #893
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
| # Copyright(c) The Maintainers of Nanvix. | |
| # Licensed under the MIT License. | |
| name: Nanvix CI | |
| on: | |
| schedule: | |
| - cron: "0 14 * * *" | |
| repository_dispatch: | |
| types: [cpython-release] | |
| push: | |
| branches: ["main"] | |
| pull_request: | |
| branches: ["main", "nanvix/**"] | |
| workflow_dispatch: | |
| permissions: | |
| contents: write | |
| actions: write | |
| issues: write | |
| pull-requests: write | |
| packages: read | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref_name || github.ref || 'default' }} | |
| cancel-in-progress: true | |
| jobs: | |
| ci: | |
| uses: nanvix/workflows/.github/workflows/nanvix-ci.yml@v2.2.0 | |
| with: | |
| platforms: '["microvm"]' | |
| process-modes: '["standalone"]' | |
| memory-sizes: '["256mb"]' | |
| skip-full-test-modes: "[]" | |
| caller-event-name: ${{ github.event_name }} | |
| windows-test: false | |
| python-paths: ".nanvix/z.py tests/smoke_test_l2.py" | |
| yaml-paths: ".github/workflows/ci.yml" | |
| release-notes-extra: | | |
| CPython 3.12.3 distribution for Nanvix with pure Python pip packages. | |
| docker-image: "ghcr.io/nanvix/toolchain-python@sha256:2ef7213bfff85e927f18d8f0fc53063b9c6ffed236a6b30c92f6b4e148c2dec4" # yamllint disable-line rule:line-length | |
| secrets: | |
| GH_TOKEN: ${{ secrets.GH_TOKEN || secrets.GITHUB_TOKEN }} | |
| DISPATCH_TOKEN: ${{ secrets.DISPATCH_TOKEN || secrets.GH_TOKEN || secrets.GITHUB_TOKEN }} | |
| # Custom Windows test that reuses the ramfs image from the Linux build | |
| # instead of rebuilding it (Docker can't run the Linux toolchain image | |
| # on Windows CI runners, so .py→.pyc pre-compilation would be skipped). | |
| windows-test: | |
| needs: ci | |
| if: >- | |
| !cancelled() | |
| && needs.ci.result == 'success' | |
| runs-on: windows-latest | |
| env: | |
| NANVIX_MACHINE: microvm | |
| NANVIX_DEPLOYMENT_MODE: standalone | |
| NANVIX_MEMORY_SIZE: 256mb | |
| NANVIX_VERSION: ${{ needs.ci.outputs.nanvix_tag }} | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - name: Install nanvix-zutil | |
| env: | |
| GH_TOKEN: ${{ secrets.GH_TOKEN || secrets.GITHUB_TOKEN }} | |
| shell: pwsh | |
| run: | | |
| $zutils_version = (Get-Content .zutils-version).Trim() | |
| $jq = '.assets[] | select(.name | endswith(".whl")) | .browser_download_url' | |
| $wheel = gh api "repos/nanvix/zutils/releases/tags/$zutils_version" --jq $jq | |
| python -m pip install $wheel | |
| - name: Setup | |
| env: | |
| GH_TOKEN: ${{ secrets.GH_TOKEN || secrets.GITHUB_TOKEN }} | |
| shell: pwsh | |
| run: >- | |
| nanvix-zutil setup --with-docker | |
| ghcr.io/nanvix/toolchain-python@sha256:2ef7213bfff85e927f18d8f0fc53063b9c6ffed236a6b30c92f6b4e148c2dec4 | |
| - name: Download Linux build artifact | |
| uses: actions/download-artifact@v5 | |
| with: | |
| name: >- | |
| nanvix-python-${{ needs.ci.outputs.package_version }}-x86-microvm-standalone-256mb | |
| path: linux-build | |
| - name: Extract ramfs from Linux tarball | |
| shell: pwsh | |
| run: | | |
| $tarball = Get-ChildItem linux-build -Filter "*.tar.gz" | Select-Object -First 1 | |
| if (-not $tarball) { Write-Error "No .tar.gz tarball found in linux-build"; exit 1 } | |
| Write-Host "Extracting ramfs from $($tarball.Name)" | |
| tar -xzf $tarball.FullName -C linux-build | |
| $ramfs = Get-ChildItem linux-build -Recurse -Filter "nanvix_rootfs.img" | Select-Object -First 1 | |
| if (-not $ramfs) { Write-Error "nanvix_rootfs.img not found in tarball"; exit 1 } | |
| $dest = Join-Path $env:GITHUB_WORKSPACE ".nanvix" "nanvix_rootfs.img" | |
| New-Item -ItemType Directory -Force -Path (Split-Path $dest) | Out-Null | |
| Copy-Item $ramfs.FullName $dest | |
| Write-Host "Placed ramfs at $dest ($('{0:N0}' -f (Get-Item $dest).Length) bytes)" | |
| echo "NANVIX_PREBUILT_RAMFS=$dest" >> $env:GITHUB_ENV | |
| - name: Test | |
| env: | |
| GH_TOKEN: ${{ secrets.GH_TOKEN || secrets.GITHUB_TOKEN }} | |
| shell: pwsh | |
| run: nanvix-zutil test | |
| release: | |
| needs: ci | |
| if: github.event_name != 'pull_request' | |
| runs-on: ubuntu-latest | |
| outputs: | |
| release_tag: ${{ steps.tag.outputs.release_tag }} | |
| env: | |
| PACKAGE_NAME: ${{ needs.ci.outputs.package_name }} | |
| PACKAGE_VERSION: ${{ needs.ci.outputs.package_version }} | |
| NANVIX_VERSION: ${{ needs.ci.outputs.nanvix_version }} | |
| steps: | |
| - name: Compute release tag | |
| id: tag | |
| run: | | |
| SHORT_SHA="${GITHUB_SHA::7}" | |
| RELEASE_TAG="${PACKAGE_VERSION}-nanvix-${NANVIX_VERSION}-${SHORT_SHA}" | |
| echo "release_tag=$RELEASE_TAG" >> "$GITHUB_OUTPUT" | |
| echo "Release tag: $RELEASE_TAG" | |
| - name: Download build artifacts | |
| uses: actions/download-artifact@v5 | |
| with: | |
| path: release-artifacts | |
| pattern: ${{ env.PACKAGE_NAME }}-${{ env.PACKAGE_VERSION }}-* | |
| - name: Create release | |
| env: | |
| GH_TOKEN: ${{ secrets.GH_TOKEN || secrets.GITHUB_TOKEN }} | |
| RELEASE_TAG: ${{ steps.tag.outputs.release_tag }} | |
| run: | | |
| set -euo pipefail | |
| mkdir -p release-assets | |
| find release-artifacts \( -name "*.tar.gz" -o -name "*.zip" \) \ | |
| -exec cp {} release-assets/ \; 2>/dev/null || true | |
| NOTES=$(printf 'Automated build from branch %s.\n\n' "${{ github.ref_name }}") | |
| NOTES+=$(printf '**Build:** [#%s](%s)\n' \ | |
| "${{ github.run_number }}" \ | |
| "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}") | |
| NOTES+=$(printf '**Commit:** [`%s`](%s)\n\n' \ | |
| "${GITHUB_SHA::7}" \ | |
| "${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }}") | |
| NOTES+="CPython 3.12.3 distribution for Nanvix with pure Python pip packages." | |
| # Collect assets safely (nullglob prevents literal glob on empty dir) | |
| shopt -s nullglob | |
| assets=(release-assets/*) | |
| shopt -u nullglob | |
| # Fail if no assets were produced — all matrix builds must have failed | |
| if [ "${#assets[@]}" -eq 0 ]; then | |
| echo "::error::No release assets found — all builds may have failed" | |
| exit 1 | |
| fi | |
| # Idempotent: if the release already exists (e.g., rerun or scheduled | |
| # trigger for the same commit), update it instead of failing. | |
| if gh release view "$RELEASE_TAG" --repo "${{ github.repository }}" >/dev/null 2>&1; then | |
| gh release edit "$RELEASE_TAG" \ | |
| --repo "${{ github.repository }}" \ | |
| --title "Build ${GITHUB_SHA::7}" \ | |
| --notes "$NOTES" \ | |
| --latest | |
| gh release upload "$RELEASE_TAG" \ | |
| --repo "${{ github.repository }}" \ | |
| --clobber \ | |
| "${assets[@]}" | |
| else | |
| gh release create "$RELEASE_TAG" \ | |
| --repo "${{ github.repository }}" \ | |
| --title "Build ${GITHUB_SHA::7}" \ | |
| --notes "$NOTES" \ | |
| --latest \ | |
| "${assets[@]}" | |
| fi | |
| windows-release: | |
| needs: [ci, windows-test, release] | |
| if: github.event_name != 'pull_request' | |
| runs-on: ubuntu-latest | |
| env: | |
| RELEASE_TAG: ${{ needs.release.outputs.release_tag }} | |
| NANVIX_TAG: ${{ needs.ci.outputs.nanvix_tag }} | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - name: Download standalone tarball from release | |
| env: | |
| GH_TOKEN: ${{ secrets.GH_TOKEN || secrets.GITHUB_TOKEN }} | |
| run: | | |
| set -euo pipefail | |
| gh release download "$RELEASE_TAG" \ | |
| --repo "${{ github.repository }}" \ | |
| --pattern "*standalone*.tar.gz" \ | |
| --dir release-download | |
| ls -la release-download/ | |
| - name: Download Nanvix Windows binaries | |
| env: | |
| GH_TOKEN: ${{ secrets.GH_TOKEN || secrets.GITHUB_TOKEN }} | |
| run: | | |
| set -euo pipefail | |
| gh release download "$NANVIX_TAG" \ | |
| --repo nanvix/nanvix \ | |
| --pattern "nanvix-windows-x86-microvm-standalone*256mb*.zip" \ | |
| --dir nanvix-windows | |
| ls -la nanvix-windows/ | |
| - name: Create Windows zip | |
| run: | | |
| set -euo pipefail | |
| # Extract the standalone tarball | |
| TARBALL=$(find release-download -name "*standalone*.tar.gz" | head -1) | |
| if [ -z "$TARBALL" ] || [ ! -f "$TARBALL" ]; then | |
| echo "::error::No standalone .tar.gz tarball found in release-download" | |
| exit 1 | |
| fi | |
| echo "Using tarball: $TARBALL" | |
| mkdir -p extract | |
| tar -xzf "$TARBALL" -C extract | |
| # Find the extracted directory name | |
| DIST_DIR=$(find extract -mindepth 1 -maxdepth 1 -type d | head -1) | |
| DIR_NAME=$(basename "$DIST_DIR") | |
| echo "Distribution directory: $DIR_NAME" | |
| # Extract Nanvix Windows zip to get host binaries | |
| WINZIP=$(find nanvix-windows -name "*.zip" | head -1) | |
| echo "Using Nanvix Windows zip: $WINZIP" | |
| mkdir -p nanvix-win-extract | |
| unzip -q "$WINZIP" -d nanvix-win-extract | |
| # Replace Linux host binaries with Windows host binaries | |
| rm -f "$DIST_DIR/bin/nanvixd.elf" | |
| cp nanvix-win-extract/nanvixd.exe "$DIST_DIR/bin/nanvixd.exe" | |
| cp nanvix-win-extract/kernel.elf "$DIST_DIR/bin/kernel.elf" | |
| # Replace the Linux-built README with the top-level | |
| # README.md (which documents the Windows workflow, | |
| # including snapshot generation and warm restore). | |
| cp README.md "$DIST_DIR/README.md" | |
| # Create the Windows zip | |
| ZIP_NAME="${DIR_NAME}.zip" | |
| (cd extract && zip -r "../${ZIP_NAME}" "$DIR_NAME") | |
| echo "zip_name=$ZIP_NAME" >> "$GITHUB_ENV" | |
| echo "Created: $ZIP_NAME ($(stat -c%s "$ZIP_NAME") bytes)" | |
| - name: Upload zip to release | |
| env: | |
| GH_TOKEN: ${{ secrets.GH_TOKEN || secrets.GITHUB_TOKEN }} | |
| run: | | |
| gh release upload "$RELEASE_TAG" \ | |
| --repo "${{ github.repository }}" \ | |
| "${{ env.zip_name }}" --clobber | |
| echo "Uploaded ${{ env.zip_name }} to release $RELEASE_TAG" |