muse is now the front door of a public project, and it was the only image in that stack without an arm64 build. Raspberry Pis, arm64 NASes and Ampere VPSes are a large share of self-hosters, so amd64-only made the whole stack un-runnable for them — gluetun, slskd, soularr, lidarr, navidrome and the socket proxy all already publish both. Three parts, because changing `platforms:` alone would have failed: - setup-qemu-action registers the binfmt handlers buildx needs to produce arm64 on an amd64 runner. Without it the build errors with "exec format error"; it does not quietly fall back to amd64. - The web stage is pinned to --platform=$BUILDPLATFORM so the Next.js build runs natively once instead of being emulated per platform. It is the expensive step, and its output is static HTML/JS/CSS with no native code, so the bytes are identical either way. This is what makes the old comment's "3-5x slower for nothing" no longer true. - platforms: linux/amd64,linux/arm64. Verified locally before pushing: both platforms build in ~52s, and the loaded arm64 image reports aarch64 with uvloop, watchfiles, httptools and pydantic_core all importing, `import app` succeeding, and the static UI present. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
112 lines
3.8 KiB
YAML
112 lines
3.8 KiB
YAML
name: release
|
|
|
|
# Publishes ghcr.io/<owner>/muse. The package inherits this repo's visibility
|
|
# on first publish, so it lands PRIVATE — pulling it needs a token with
|
|
# read:packages (see README, "Pulling on a server").
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
tags: ["v*"]
|
|
# Docs-only edits shouldn't burn a build or move :latest.
|
|
paths-ignore:
|
|
- "**.md"
|
|
- ".gitignore"
|
|
workflow_dispatch:
|
|
|
|
concurrency:
|
|
# A newer push makes an in-flight build pointless.
|
|
group: release-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
jobs:
|
|
build:
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: read
|
|
packages: write # required to push to GHCR
|
|
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: Sanity-check the API before publishing
|
|
run: |
|
|
python3 -m pip install --quiet -r requirements.txt
|
|
python3 -c "import app; print('app imports OK')"
|
|
|
|
# Typechecks and compiles the UI. The Docker build runs this too, but
|
|
# doing it up front fails in ~40s instead of after the whole image is
|
|
# assembled, and surfaces TS errors as their own step in the log.
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version: "22"
|
|
cache: npm
|
|
cache-dependency-path: web/package-lock.json
|
|
|
|
- name: Build the UI
|
|
working-directory: web
|
|
run: |
|
|
npm ci
|
|
npm run build
|
|
|
|
# Registers binfmt handlers so buildx can produce a linux/arm64 image on
|
|
# an amd64 runner. REQUIRED by the multi-arch build below — without it
|
|
# buildx fails outright ("exec format error"), it does not silently fall
|
|
# back to amd64 only.
|
|
- uses: docker/setup-qemu-action@v3
|
|
with:
|
|
platforms: arm64
|
|
|
|
- uses: docker/setup-buildx-action@v3
|
|
|
|
- name: Log in to GHCR
|
|
uses: docker/login-action@v3
|
|
with:
|
|
registry: ghcr.io
|
|
username: ${{ github.actor }}
|
|
# Automatically provided; no PAT needed for pushing from CI.
|
|
password: ${{ secrets.GITHUB_TOKEN }}
|
|
|
|
- name: Tags and labels
|
|
id: meta
|
|
uses: docker/metadata-action@v5
|
|
with:
|
|
images: ghcr.io/${{ github.repository }}
|
|
tags: |
|
|
type=raw,value=latest,enable={{is_default_branch}}
|
|
type=sha,format=short
|
|
type=semver,pattern={{version}}
|
|
type=semver,pattern={{major}}.{{minor}}
|
|
|
|
- name: Build and push
|
|
uses: docker/build-push-action@v6
|
|
with:
|
|
context: .
|
|
# amd64 AND arm64.
|
|
#
|
|
# This was amd64-only, and that was the right call while the only
|
|
# host running muse was one x86_64 box — arm64 meant paying QEMU for
|
|
# an image nobody would pull. That changed when muse became the
|
|
# front door of a public project: Raspberry Pis, arm64 NASes and
|
|
# Ampere VPSes are a large share of self-hosters, and every other
|
|
# image in that stack already publishes both. muse would have been
|
|
# the single thing making it un-runnable on a Pi.
|
|
#
|
|
# The old comment's cost estimate no longer applies either: the
|
|
# expensive stage (the Next.js build) is pinned to $BUILDPLATFORM in
|
|
# the Dockerfile, so it runs natively once instead of being emulated
|
|
# per platform. What is emulated is a pip install of prebuilt
|
|
# wheels.
|
|
platforms: linux/amd64,linux/arm64
|
|
push: true
|
|
tags: ${{ steps.meta.outputs.tags }}
|
|
labels: ${{ steps.meta.outputs.labels }}
|
|
cache-from: type=gha
|
|
cache-to: type=gha,mode=max
|
|
|
|
- name: Summary
|
|
run: |
|
|
echo "### Published" >> $GITHUB_STEP_SUMMARY
|
|
echo '```' >> $GITHUB_STEP_SUMMARY
|
|
echo "${{ steps.meta.outputs.tags }}" >> $GITHUB_STEP_SUMMARY
|
|
echo '```' >> $GITHUB_STEP_SUMMARY
|