Skip to content

1.0.0

1.0.0 #26

Workflow file for this run

name: Build firmware
on:
workflow_dispatch:
inputs:
release_tag:
description: "Optional tag name to cut a release (example 'v1.2.3'). Leave empty for build-only."
required: false
push:
branches: [main]
paths:
- 'main/**'
- 'web/**'
- 'sdkconfig*'
- 'partitions_*.csv'
- 'CMakeLists.txt'
- 'tools/build_all.py'
- 'tools/gen_web_flasher.py'
- '.github/workflows/build.yml'
tags:
- 'v*'
# A new push to the same ref supersedes in-flight builds.
concurrency:
group: build-${{ github.ref }}
cancel-in-progress: true
jobs:
# ---------------------------------------------------------------------------
# Single source of truth: derive the board matrix from tools/build_all.py so
# adding a board there automatically adds it to CI.
# ---------------------------------------------------------------------------
prepare:
name: Prepare matrix
runs-on: ubuntu-24.04
outputs:
matrix: ${{ steps.set.outputs.matrix }}
wc_version: ${{ steps.version.outputs.wc_version }}
steps:
- uses: actions/checkout@v4
- name: Build board matrix
id: set
run: echo "matrix=$(python3 tools/build_all.py --print-matrix)" >> "$GITHUB_OUTPUT"
- name: Read firmware version
id: version
run: |
ver=$(grep -oP '(?<=JANOS_WC_VERSION ")[^"]+' main/main.c)
echo "wc_version=$ver" >> "$GITHUB_OUTPUT"
# ---------------------------------------------------------------------------
# Build every board in parallel. fail-fast:false => one broken board does not
# cancel the others, so you see every failure in a single run.
# ---------------------------------------------------------------------------
build:
name: ${{ matrix.board }}
needs: prepare
permissions:
contents: read
runs-on: ubuntu-24.04
container:
image: espressif/idf:release-v6.0
strategy:
fail-fast: false
matrix:
include: ${{ fromJSON(needs.prepare.outputs.matrix) }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Cache ESP-IDF artifacts
uses: actions/cache@v4
with:
path: /root/.espressif
key: espidf-release-v6.0-${{ matrix.board }}-${{ hashFiles('sdkconfig.defaults', 'dependencies.lock') }}
restore-keys: |
espidf-release-v6.0-${{ matrix.board }}-
espidf-release-v6.0-
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
cache-dependency-path: web/package-lock.json
- name: Install web dependencies
run: npm ci
working-directory: web
- name: Build web UI
run: npm run build
working-directory: web
# build_all.py runs the firmware build, generates the per-board
# flash_board.py, and verifies the generated sdkconfig matches the
# board's defaults (catches config leakage). --no-docker because we are
# already inside the esp-idf container. --retry 2 re-attempts the board
# up to 2 extra times so a flaky failure (e.g. transient component-manager
# network error) does not fail the job and block the release gate.
- name: Build firmware (${{ matrix.board }})
run: |
python3 tools/build_all.py \
--no-docker \
--no-web \
--retry 2 \
--targets ${{ matrix.board }} \
--output-dir artifacts
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: firmware-${{ matrix.board }}
path: artifacts/${{ matrix.board }}/
if-no-files-found: error
retention-days: 30
# ---------------------------------------------------------------------------
# Release gate: needs ALL build jobs => runs only after every board is green.
# Fires on a pushed v* tag, a published release, or a manual run with a tag.
# ---------------------------------------------------------------------------
release:
name: Release
needs: [prepare, build]
runs-on: ubuntu-24.04
permissions:
contents: write
outputs:
tag: ${{ steps.meta.outputs.tag }}
if: >-
startsWith(github.ref, 'refs/tags/v') ||
(github.event_name == 'workflow_dispatch' && inputs.release_tag != '')
steps:
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: release_artifacts/
- name: Resolve release tag
id: meta
env:
INPUT_TAG: ${{ inputs.release_tag }}
WC_VERSION: ${{ needs.prepare.outputs.wc_version }}
run: |
TAG="${GITHUB_REF#refs/tags/}"
if [ "$TAG" = "$GITHUB_REF" ]; then TAG="${INPUT_TAG:-}"; fi
if [ -z "$TAG" ]; then TAG="v${WC_VERSION}"; fi
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
echo "Using tag: $TAG"
# Bundle each board into its own zip (bins + flash_board.py). Release
# assets share a flat namespace, so the per-board flash_board.py files
# would otherwise collide — one zip per board keeps them separate and
# gives users a single self-contained download.
- name: Package per-board zips
run: |
set -euo pipefail
mkdir -p release_zips
for d in release_artifacts/firmware-*; do
[ -d "$d" ] || continue
board="${d#release_artifacts/firmware-}"
(cd "$d" && zip -r -q "${GITHUB_WORKSPACE}/release_zips/mate-${board}.zip" .)
done
- name: Build release notes
run: |
{
printf 'Auto-build from commit %s\n\nBoards:\n' "${GITHUB_SHA}"
for z in release_zips/mate-*.zip; do
board="$(basename "$z" .zip)"
printf -- '- %s.zip\n' "$board"
done
} > release_notes.md
- name: Create or update GitHub Release
env:
GH_TOKEN: ${{ secrets.PAT_TOKEN }}
TAG: ${{ steps.meta.outputs.tag }}
run: |
set -euo pipefail
gh release delete "$TAG" -R "$GITHUB_REPOSITORY" --yes 2>/dev/null || true
gh release create "$TAG" -R "$GITHUB_REPOSITORY" \
--target "$GITHUB_SHA" \
--title "Mate $TAG" \
--notes-file release_notes.md \
release_zips/*.zip
# ---------------------------------------------------------------------------
# Web flasher: deploy the browser-based esptool-js flasher (docs/) to GitHub
# Pages, arranging every board's bins under docs/firmware/<board>/ and
# generating a per-board manifest + boards.json (the dropdown). needs release
# => only publishes once every board is green AND a release was cut, so the
# flasher never points at a half-built matrix.
# ---------------------------------------------------------------------------
pages:
name: Web Flasher (Pages)
needs: [prepare, build, release]
runs-on: ubuntu-24.04
permissions:
contents: read
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deploy.outputs.page_url }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Download all firmware artifacts
uses: actions/download-artifact@v4
with:
path: _artifacts
pattern: firmware-*
# Each artifact "firmware-<board>" holds that board's bins at its root.
# Fan them out into docs/firmware/<board>/ so the manifests (and Pages)
# can serve firmware/<board>/<file>.bin by relative URL.
- name: Arrange firmware under docs/firmware/
run: |
set -euo pipefail
rm -rf docs/firmware
mkdir -p docs/firmware
for d in _artifacts/firmware-*; do
[ -d "$d" ] || continue
board="${d#_artifacts/firmware-}"
mkdir -p "docs/firmware/${board}"
cp "$d"/*.bin "docs/firmware/${board}/"
done
ls -R docs/firmware
- name: Generate manifests + boards.json
run: |
python3 tools/gen_web_flasher.py \
--firmware-dir docs/firmware \
--out-dir docs \
--version "${{ needs.prepare.outputs.wc_version }}" \
--build "${{ needs.release.outputs.tag }}"
- name: Setup Pages
uses: actions/configure-pages@v4
with:
enablement: true
- name: Upload Pages artifact
uses: actions/upload-pages-artifact@v3
with:
path: docs
- name: Deploy to GitHub Pages
id: deploy
uses: actions/deploy-pages@v4