Skip to content

Merge pull request #311 from nanvix/fix/304/release-to-standard-location #893

Merge pull request #311 from nanvix/fix/304/release-to-standard-location

Merge pull request #311 from nanvix/fix/304/release-to-standard-location #893

Workflow file for this run

# 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"