Skip to content

Commit 4c26a47

Browse files
allen0125claude
andcommitted
[CI] Add firmware build + tagged release workflow with version-tag guard
GitHub Actions: build the firmware in the ESP-IDF v5.3.3 container on push/PR; on a v* tag, first enforce that the tag matches QUOTAPUTER_VERSION in main/main.cpp via tools/check_version.sh (single source of truth) and fail the release otherwise, then publish a GitHub Release with quotaputer.bin, bootloader, partition-table, a merged quotaputer-full.bin, and flasher_args.json. Documents the release flow in AGENT.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 6edb9b6 commit 4c26a47

3 files changed

Lines changed: 127 additions & 0 deletions

File tree

.github/workflows/firmware.yml

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
name: firmware
2+
3+
# Build on every push to main and on PRs (CI), and additionally publish a
4+
# GitHub Release with the firmware binaries when a version tag (v*) is pushed.
5+
on:
6+
push:
7+
branches: [main]
8+
tags: ["v*"]
9+
pull_request:
10+
11+
permissions:
12+
contents: write # needed to create the Release on a tag
13+
14+
jobs:
15+
build:
16+
runs-on: ubuntu-latest
17+
steps:
18+
- uses: actions/checkout@v4
19+
20+
# On a tag push, fail fast unless the tag matches QUOTAPUTER_VERSION in
21+
# main/main.cpp (single source of truth). Never release a mismatched tag.
22+
- name: Check tag matches firmware version
23+
if: startsWith(github.ref, 'refs/tags/v')
24+
run: ./tools/check_version.sh "${GITHUB_REF_NAME}"
25+
26+
# Build inside the official ESP-IDF v5.3.3 container (pulls m5unified/m5gfx
27+
# from the component registry). Also produce a single merged image.
28+
- name: Build firmware
29+
uses: espressif/esp-idf-ci-action@v1
30+
with:
31+
esp_idf_version: v5.3.3
32+
target: esp32s3
33+
command: idf.py build && (idf.py merge-bin -o build/quotaputer-full.bin || echo "merge-bin unavailable, skipping")
34+
35+
- name: Collect artifacts
36+
run: |
37+
set -e
38+
mkdir -p dist
39+
cp build/quotaputer.bin dist/
40+
cp build/bootloader/bootloader.bin dist/
41+
cp build/partition_table/partition-table.bin dist/
42+
cp build/flasher_args.json dist/ || true
43+
cp build/quotaputer-full.bin dist/ || true
44+
ls -l dist
45+
46+
- name: Upload build artifacts
47+
uses: actions/upload-artifact@v4
48+
with:
49+
name: quotaputer-firmware
50+
path: dist/
51+
52+
- name: Publish release
53+
if: startsWith(github.ref, 'refs/tags/v')
54+
uses: softprops/action-gh-release@v2
55+
with:
56+
files: dist/*
57+
generate_release_notes: true
58+
fail_on_unmatched_files: true
59+
body: |
60+
QuotaPuter firmware ${{ github.ref_name }} for M5Stack Cardputer (ESP32-S3).
61+
62+
## Flash (single merged image, simplest)
63+
```bash
64+
esptool.py --chip esp32s3 -p /dev/cu.usbmodemXXXX write_flash 0x0 quotaputer-full.bin
65+
```
66+
67+
## Flash (individual images)
68+
```bash
69+
esptool.py --chip esp32s3 -p /dev/cu.usbmodemXXXX \
70+
--before default_reset --after hard_reset write_flash \
71+
--flash_mode dio --flash_freq 80m --flash_size 8MB \
72+
0x0 bootloader.bin \
73+
0x8000 partition-table.bin \
74+
0x20000 quotaputer.bin
75+
```
76+
77+
Wi-Fi and provider credentials are provisioned separately over USB with
78+
`tools/quota_config.py` — see docs/PROVISIONING.md. Nothing sensitive is
79+
baked into these images.

AGENT.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,20 @@ M5Stack Cardputer (ESP32-S3) 上的 LLM 额度查看固件,纯 **ESP-IDF** 开
104104
[Step 6.2] Implement kimi balance provider with real moonshot API
105105
```
106106

107+
## 发布 (Releasing)
108+
109+
固件版本的**唯一事实来源**`main/main.cpp` 里的 `QUOTAPUTER_VERSION` 宏。发布流程:
110+
111+
1.`main/main.cpp``QUOTAPUTER_VERSION` 为新版本(如 `0.2.0`),提交。
112+
2. 打 tag:`git tag v0.2.0 && git push origin v0.2.0`(tag 必须等于宏值,**`v` 前缀**)。
113+
3. `.github/workflows/firmware.yml` 在 tag 推送时先跑 `tools/check_version.sh "$GITHUB_REF_NAME"`
114+
tag 与固件内版本不一致则**直接失败**,不会发布。一致则用 ESP-IDF v5.3.3 容器构建,
115+
并发 GitHub Release,附 `quotaputer.bin` / `bootloader.bin` / `partition-table.bin` /
116+
合并镜像 `quotaputer-full.bin` / `flasher_args.json`
117+
4. 本地可随时 `tools/check_version.sh` 查看当前固件版本、`tools/check_version.sh v0.2.0` 预校验。
118+
119+
非 tag 的 push/PR 只构建做 CI,不发布。
120+
107121
## 参考
108122

109123
- M5Stack 文档 — https://docs.m5stack.com | M5Unified — https://github.com/m5stack/M5Unified

tools/check_version.sh

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#!/usr/bin/env bash
2+
# Single source of truth for the firmware version is the QUOTAPUTER_VERSION macro
3+
# in main/main.cpp.
4+
#
5+
# tools/check_version.sh -> prints the firmware version (e.g. 0.1.0)
6+
# tools/check_version.sh v0.1.0 -> exit 0 if the tag matches, non-zero if not
7+
#
8+
# CI runs the second form on a tag push so a release can never ship with a tag
9+
# that disagrees with the in-firmware version.
10+
set -euo pipefail
11+
12+
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
13+
SRC="$ROOT/main/main.cpp"
14+
15+
VERSION="$(grep -oE '#define[[:space:]]+QUOTAPUTER_VERSION[[:space:]]+"[^"]+"' "$SRC" \
16+
| sed -E 's/.*"([^"]+)".*/\1/')"
17+
18+
if [ -z "${VERSION:-}" ]; then
19+
echo "ERROR: QUOTAPUTER_VERSION not found in $SRC" >&2
20+
exit 2
21+
fi
22+
23+
if [ "$#" -lt 1 ]; then
24+
echo "$VERSION"
25+
exit 0
26+
fi
27+
28+
TAG="${1#v}" # strip an optional leading 'v'
29+
if [ "$TAG" != "$VERSION" ]; then
30+
echo "ERROR: tag '$1' (-> $TAG) does not match firmware QUOTAPUTER_VERSION '$VERSION'." >&2
31+
echo "Bump QUOTAPUTER_VERSION in main/main.cpp to '$TAG', or retag as 'v$VERSION'." >&2
32+
exit 1
33+
fi
34+
echo "OK: tag '$1' matches firmware version '$VERSION'."

0 commit comments

Comments
 (0)