Files
Toastconcern--FrameStudio/.github/workflows/build.yml
T
KevinandCopilot 8d7fdfd305 ci: Build Frame Home in CI
Build Frame Home payload in CI

Build the ARM64 headset payload against the glibc 2.39 baseline, validate its ABI,
and pass the generated artifact to Studio packaging.

Add provenance attestations and document CI and local build modes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-24 18:51:06 +02:00

204 lines
8.3 KiB
YAML

# Builds Frame Studio for every computer it runs on, plus a same-commit Frame Home payload, on branch pushes,
# pull requests, and by hand (Actions > build > Run workflow). A push to main replaces the Studio "latest" pre-release
# and includes the Frame Home installer. A tag "v*" publishes a versioned release containing both products.
name: build
on:
push:
branches: ['**']
tags: ['v*']
pull_request:
workflow_dispatch:
permissions:
contents: read
# A newer push to the same branch replaces a run still going (its builds would be out of date anyway).
concurrency:
group: build-${{ github.ref }}
cancel-in-progress: true
# Every Studio build knows the commit it's from (studio/updater.h compares it with the latest release's).
env:
FRAME_BUILD: ${{ github.sha }}
FRAME_HOME_PAYLOAD_DIR: ${{ github.workspace }}/build/frame-home/payload
jobs:
linux:
name: Linux ${{ matrix.arch }}
needs: [frame-home]
strategy:
fail-fast: false
matrix:
include:
- { arch: x64, runner: ubuntu-24.04, extra: "" }
- { arch: x86, runner: ubuntu-24.04, i386: true,
extra: "gcc-multilib g++-multilib libx11-dev:i386 libxrandr-dev:i386 libxinerama-dev:i386 libxcursor-dev:i386 libxi-dev:i386" }
- { arch: arm64, runner: ubuntu-24.04-arm, extra: "" }
runs-on: ${{ matrix.runner }}
timeout-minutes: 45
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: FrameHome-payload
path: build/frame-home/payload
if-no-files-found: error
- name: Tools and X11 headers
run: |
# x86 links against 32-bit X11, so apt needs the i386 packages too
if [ "${{ matrix.i386 }}" = "true" ]; then sudo dpkg --add-architecture i386; fi
sudo apt-get update
sudo apt-get install -y ninja-build cmake libx11-dev libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libxkbcommon-dev ${{ matrix.extra }}
- name: Build
run: sh scripts/build-studio.sh linux-${{ matrix.arch }}
- uses: actions/upload-artifact@v4
with:
name: FrameStudio-linux-${{ matrix.arch }}
path: dist/FrameStudio-linux-${{ matrix.arch }}.tar.gz
windows:
name: Windows ${{ matrix.arch }}
needs: [frame-home]
strategy:
fail-fast: false
matrix:
arch: [x64, x86, arm64]
runs-on: windows-latest
timeout-minutes: 45
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: FrameHome-payload
path: build/frame-home/payload
- name: Build (MSVC)
shell: cmd
run: studio\build_windows.bat ${{ matrix.arch }} "%GITHUB_WORKSPACE%\build\windows-${{ matrix.arch }}"
- name: Package
shell: pwsh
run: |
$name = "FrameStudio-windows-${{ matrix.arch }}"
$stage = "build/stage/$name"
New-Item -ItemType Directory -Force $stage | Out-Null
Copy-Item "build/windows-${{ matrix.arch }}/Frame Studio.exe" $stage
Copy-Item -Recurse "build/windows-${{ matrix.arch }}/frame" "$stage/frame"
Copy-Item LICENSE "$stage/LICENSE.txt"
New-Item -ItemType Directory -Force dist | Out-Null
Compress-Archive -Path $stage -DestinationPath "dist/$name.zip" -Force
- uses: actions/upload-artifact@v4
with:
name: FrameStudio-windows-${{ matrix.arch }}
path: dist/FrameStudio-windows-${{ matrix.arch }}.zip
macos:
name: macOS ${{ matrix.arch }}
needs: [frame-home]
strategy:
fail-fast: false
matrix:
include:
- { arch: arm64, runner: macos-14 }
# Intel: macos-13 was retired (Dec 2025); macos-15-intel is GitHub's last Intel Mac runner (until Aug 2027).
# Not cross-built on an ARM Mac because Homebrew's MoltenVK, bundled into the app, is the runner's own arch.
- { arch: x64, runner: macos-15-intel }
runs-on: ${{ matrix.runner }}
timeout-minutes: 45
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: FrameHome-payload
path: build/frame-home/payload
- name: Tools and MoltenVK (Vulkan on Metal, bundled into the app)
run: brew install ninja molten-vk
- name: Build
run: sh scripts/build-studio.sh macos-${{ matrix.arch }}
- uses: actions/upload-artifact@v4
with:
name: FrameStudio-macos-${{ matrix.arch }}
path: dist/FrameStudio-macos-${{ matrix.arch }}.zip
frame-home:
name: Build Frame Home payload
runs-on: ubuntu-24.04-arm
timeout-minutes: 45
steps:
- uses: actions/checkout@v4.2.2
- name: Build payload and installer
run: |
sudo apt-get update
sudo apt-get install -y cmake ninja-build gcc-13 g++-13 glslc zip
sh scripts/build-frame-home.sh --ci
- uses: actions/upload-artifact@v4
with:
name: FrameHome-payload
path: build/frame-home/payload
- uses: actions/upload-artifact@v4
with:
name: FrameHome-installer
path: build/frame-home/FrameHome-installer.zip
if-no-files-found: error
- uses: actions/upload-artifact@v4
with:
name: FrameHome-build-info
path: build/frame-home/build-info.txt
if-no-files-found: error
release:
name: Publish
# "latest" gets every build that finished (a failed or still-slow platform just isn't in it); a version release
# needs them all. We don't wait on macOS: its runners can sit in GitHub's queue for hours, and since a push
# cancels the previous run, waiting meant no run ever reached this job. Both Mac builds still upload and are
# attached whenever they finish in time.
if: ${{ !cancelled() && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main') }}
needs: [linux, windows, frame-home]
runs-on: ubuntu-24.04
timeout-minutes: 20
permissions:
contents: write
id-token: write
attestations: write
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
steps:
- uses: actions/download-artifact@v4
with:
pattern: FrameStudio-*
path: dist
merge-multiple: true
- uses: actions/download-artifact@v4
with:
name: FrameHome-payload
path: ${{ runner.temp }}/frame-home-payload
- uses: actions/download-artifact@v4
with:
name: FrameHome-installer
path: dist
# What Frame Studio (studio/updater.h) and the headset's Environments panel check for updates: this commit,
# Frame Home's identity (a hash of the headset programs, the same one the panel works out for what's installed)
# and the files in this release.
- name: latest.json
run: |
home=$(cd "$RUNNER_TEMP/frame-home-payload" && sha256sum bin/frame-home bin/frame-switcher bin/fenv-tool layer/libframe_home_layer.so layer/env_ps.spv | cut -c1-64 | sha256sum | cut -c1-64)
files=$(ls dist | python3 -c 'import json, sys; print(json.dumps([l.strip() for l in sys.stdin if l.strip()]))')
printf '{"build": "%s", "home": "%s", "date": "%s", "files": %s}\n' "$GITHUB_SHA" "$home" "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$files" > dist/latest.json
cat dist/latest.json
- uses: actions/attest-build-provenance@v2
with:
subject-path: dist/FrameStudio-*
- uses: actions/attest-build-provenance@v2
with:
subject-path: dist/FrameHome-installer.zip
- name: Version release (tag v*)
if: ${{ startsWith(github.ref, 'refs/tags/v') && !contains(needs.*.result, 'failure') }}
run: gh release create "${{ github.ref_name }}" dist/* --repo "$REPO" --title "Frame Studio ${{ github.ref_name }}" --generate-notes
- name: Latest build (push to main)
if: github.ref == 'refs/heads/main'
run: |
gh release delete latest --repo "$REPO" --yes --cleanup-tag 2>/dev/null || true
gh release create latest dist/* --repo "$REPO" --prerelease --target "${{ github.sha }}" \
--title "Latest build (${GITHUB_SHA::7})" \
--notes "Built automatically from ${{ github.sha }} on $(date -u '+%Y-%m-%d %H:%M UTC'). Replaced by every push to main."