Skip to content

Merge pull request #1139 from shorepine/claude/amyboard-c-audio-effec… #73

Merge pull request #1139 from shorepine/claude/amyboard-c-audio-effec…

Merge pull request #1139 from shorepine/claude/amyboard-c-audio-effec… #73

name: AMYboard release
# Continuous AMYboard release on push to main. Now that every PR gets its own
# preview site + firmware (amyboard-pr-<N>.vercel.app) and HW CI before merge,
# each main commit is treated as releasable:
#
# - firmware -> the rolling 'amyboard' GitHub release, kept NON-latest (the
# rolling 'tulip' release owns the LATEST mark / releases/latest).
# amyboard.com's web flasher reads releases/download/amyboard;
# on-device amyboard.upgrade() reads releases/tags/amyboard
# (see get_latest_release() in tulip/shared/py/tulip.py).
# The firmware + sys bins are also MIRRORED onto the rolling
# 'tulip' release: legacy AMYboards (monthly-release firmware,
# <= v-jun-2026) OTA from releases/latest and need to find
# amyboard-* assets there.
# - web -> amyboardweb/stage/ deployed --prod to the 'amyboard' Vercel
# project (amyboard.com).
#
# Tulip firmware is NOT built here — it has its own continuous release
# (tulip-release.yml). This replaces the old 'dev' channel (rolling 'dev'
# prerelease + amyboard-dev.vercel.app), which PR previews made redundant.
on:
push:
branches: [ "main" ]
paths:
- 'amy'
- 'tulip/amyboard/**'
# AMYboard firmware also compiles shared ESP32-S3 sources from here
# (e.g. tulip/esp32s3/usb.c), so changes there must trigger a release.
- 'tulip/esp32s3/**'
- 'tulip/amyboardweb/**'
- 'tulip/shared/**'
- '.github/workflows/amyboard-release.yml'
workflow_dispatch:
concurrency:
group: amyboard-release # serialize releases; never cancel a half-done one
cancel-in-progress: false
jobs:
firmware:
runs-on: ubuntu-latest
permissions:
contents: write # create + clobber the 'amyboard' release
steps:
- uses: actions/checkout@v4
with:
# amy + micropython (and their nested submodules) are needed to build.
submodules: recursive
- name: Build AMYboard firmware + assemble images
uses: espressif/esp-idf-ci-action@v1
with:
esp_idf_version: v5.4.1
target: esp32s3
path: tulip/amyboard
command: >-
python -m pip install littlefs-python &&
idf.py -DMICROPY_BOARD=AMYBOARD build &&
cd .. &&
python fs_create.py amyboard
- name: Upload firmware artifacts
uses: actions/upload-artifact@v4
with:
name: amyboard-firmware
if-no-files-found: error
path: |
tulip/amyboard/dist/amyboard-firmware-AMYBOARD.bin
tulip/amyboard/dist/amyboard-full-AMYBOARD.bin
tulip/amyboard/dist/amyboard-sys.bin
- name: Publish to the rolling 'amyboard' release (kept non-latest)
# Gated on main so a workflow_dispatch from a branch is a safe dry-run.
if: github.ref == 'refs/heads/main'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
# Create the rolling release once, explicitly NOT 'latest': the rolling
# 'tulip' release owns the LATEST mark (releases/latest), which legacy
# pre-jun-2026 devices OTA from.
if ! gh release view amyboard >/dev/null 2>&1; then
gh release create amyboard \
--latest=false \
--target "$GITHUB_SHA" \
--title "AMYboard (rolling release)" \
--notes "Rolling AMYboard release built from main. Flashed by amyboard.com and on-device amyboard.upgrade(). Updated on every push to main; not a tagged version."
fi
# Stable asset names — consumed by amyboard.com's flasher and
# amyboard.upgrade(). Do not rename.
gh release upload --clobber amyboard \
tulip/amyboard/dist/amyboard-firmware-AMYBOARD.bin \
tulip/amyboard/dist/amyboard-full-AMYBOARD.bin \
tulip/amyboard/dist/amyboard-sys.bin
# Date-coded copy of the full image for manufacturing ("which build is
# this file?"). One dated copy lives on the release at a time; older
# date codes are pruned. Same-day rebuilds clobber the same name.
# Copy into RUNNER_TEMP: dist/ is root-owned (created inside the
# esp-idf-ci-action container), so this step can't create files there.
DATE_CODE="$(date -u +%Y%m%d)"
cp tulip/amyboard/dist/amyboard-full-AMYBOARD.bin \
"${RUNNER_TEMP}/amyboard-full-AMYBOARD-${DATE_CODE}.bin"
gh api "repos/${GITHUB_REPOSITORY}/releases/tags/amyboard" --jq '.assets[].name' \
| { grep -E '^amyboard-full-AMYBOARD-[0-9]{8}\.bin$' || true; } \
| { grep -v "${DATE_CODE}" || true; } \
| while read -r stale; do gh release delete-asset amyboard "$stale" --yes; done
gh release upload --clobber amyboard \
"${RUNNER_TEMP}/amyboard-full-AMYBOARD-${DATE_CODE}.bin"
# Refresh the release body with the exact released commit for provenance.
gh release edit amyboard \
--notes "Rolling AMYboard release, built from main @ ${GITHUB_SHA}. Flashed by amyboard.com and on-device amyboard.upgrade(). Updated on every push to main; not a tagged version."
# Mirror onto the rolling 'tulip' release (= releases/latest): legacy
# AMYboards running monthly-release firmware (<= v-jun-2026) OTA from
# releases/latest and look for these exact asset names. Once upgraded,
# they track releases/tags/amyboard and no longer need the mirror.
if gh release view tulip >/dev/null 2>&1; then
gh release upload --clobber tulip \
tulip/amyboard/dist/amyboard-firmware-AMYBOARD.bin \
tulip/amyboard/dist/amyboard-full-AMYBOARD.bin \
tulip/amyboard/dist/amyboard-sys.bin
fi
web:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
# Fail fast on a missing/mis-scoped token, before the ~15-min build.
- name: Verify Vercel token can access the team
env:
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
run: |
if [ -z "$VERCEL_TOKEN" ]; then
echo "::error::VERCEL_TOKEN secret is not set — add it (scope bwhitmans-projects)."; exit 1
fi
npm i -g vercel@latest >/dev/null 2>&1
if ! vercel project ls --scope bwhitmans-projects --token "$VERCEL_TOKEN" >/dev/null 2>&1; then
echo "::error::VERCEL_TOKEN cannot access the 'bwhitmans-projects' team — recreate the token with that scope."; exit 1
fi
echo "Vercel token OK for bwhitmans-projects."
- uses: actions/checkout@v4
with:
submodules: recursive
# --- Web stage/ (amy + amyboard WASM + static) ---
- uses: actions/setup-python@v5
with:
python-version: '3.13'
- name: Install web build deps
run: pip install numpy
- uses: mymindstorm/setup-emsdk@v14
with:
version: 4.0.22
- name: Build amyboardweb stage/
run: |
cd tulip/amyboardweb
python3 dev.py --build-only
# --- Deploy stage/ to Vercel production (amyboard.com) ---
# No firmware is bundled: the release flasher reads from the 'amyboard'
# GitHub release, not from the same origin (only PR previews bundle).
- name: Deploy to Vercel production (amyboard.com)
env:
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
run: |
set -euo pipefail
npm i -g vercel@latest >/dev/null 2>&1
cd tulip/amyboardweb
vercel link --yes --token "$VERCEL_TOKEN" --scope bwhitmans-projects --project amyboard --cwd stage
vercel deploy stage --prod --yes --token "$VERCEL_TOKEN" --scope bwhitmans-projects