Adds .gitea/workflows/build-android.yml — Linux runner installs JDK 17, Python 3.11, Android SDK/NDK, symlinks server/src/ledgrab into the Chaquopy python source dir, and runs assembleDebug on master pushes / assembleRelease on v* tags. APK is uploaded as an artifact and attached to the Gitea release on tag push. Conditional signing config in build.gradle.kts reads keystore from env vars (CI secrets) and falls back to debug signing locally. Gradle wrapper (gradlew/gradlew.bat/ gradle-wrapper.jar) committed so CI can drive the build. Rebuilds pydantic-core wheels for arm64-v8a and x86_64 — both were missing libpython3.11.so in NEEDED, which would have crashed at import on real devices. build-pydantic-core.sh rewritten as a multi-ABI builder: selects targets via args, sets RUSTFLAGS=-C link-arg=-Wl,--no-as-needed -C link-arg=-lpython3.11 to force the symbol-resolution dependency, uses the per-ABI sysconfigdata + libpython staged in android/.build-cache/, prefers `py -3.11` on Windows (Git Bash's python3.11 is an MSStore stub), uses the .cmd clang wrapper on Windows (fixes os error 193), and verifies NEEDED via llvm-readelf after each build. abiFilters restored to the full triple in build.gradle.kts; multi-ABI debug APK builds cleanly (~99 MB).
6.8 KiB
CI/CD & Release Workflow
Reference guide: Gitea Python CI/CD Guide — reusable patterns for Gitea Actions, cross-build, NSIS, Docker. When modifying workflows or build scripts, consult this guide to stay in sync with established patterns.
Workflows
| File | Trigger | Purpose |
|---|---|---|
.gitea/workflows/test.yml |
Push/PR to master | Lint (ruff) + pytest |
.gitea/workflows/release.yml |
Tag v* |
Build Windows/Linux/Docker artifacts + create Gitea release |
.gitea/workflows/build-android.yml |
Push master (android/server paths), tag v*, manual |
Build Android APK; attach to release on tag |
Release Pipeline (release.yml)
Four parallel jobs triggered by pushing a v* tag:
1. create-release
Creates the Gitea release with a description table listing all artifacts. The description must stay in sync with actual build outputs — if you add/remove/rename an artifact, update the body template here.
2. build-windows (cross-built from Linux)
- Runs
build/build-dist-windows.shon Ubuntu with NSIS + msitools - Downloads Windows embedded Python 3.11 + pip wheels cross-platform
- Bundles tkinter from Python MSI via msiextract
- Builds frontend (
npm run build) - Pre-compiles Python bytecode (
compileall) - Produces:
LedGrab-{tag}-win-x64.zip(portable) andLedGrab-{tag}-setup.exe(NSIS installer)
3. build-linux
- Runs
build/build-dist.shon Ubuntu - Creates a venv, installs deps, builds frontend
- Produces:
LedGrab-{tag}-linux-x64.tar.gz
4. build-docker
- Plain
docker build+docker push(no Buildx — TrueNAS runners lack nested networking) - Registry:
{gitea_host}/{repo}:{tag} - Tags:
v0.x.x,0.x.x, andlatest(stable only, not alpha/beta/rc)
Build Scripts
| Script | Platform | Output |
|---|---|---|
build/build-dist-windows.sh |
Linux → Windows cross-build | ZIP + NSIS installer |
build/build-dist.sh |
Linux native | tarball |
server/Dockerfile |
Docker | Container image |
Release Versioning
- Tags:
v{major}.{minor}.{patch}for stable,v{major}.{minor}.{patch}-alpha.{n}for pre-release - Pre-release tags set
prerelease: trueon the Gitea release - Docker
latesttag only applied to stable releases - Version in
server/pyproject.tomlshould match the tag (withoutvprefix)
CI Runners
- Two TrueNAS Gitea runners with
ubuntutags - No Windows runner available — Windows builds are cross-compiled from Linux
- Docker Buildx not available (networking limitations) — use plain
docker build
Test Pipeline (test.yml)
- Installs
opencv-python-headlessandlibportaudio2for CI compatibility - Display-dependent tests are skipped via
@requires_displaymarker - Uses
pythonnotpython3(Git Bash on Windows resolvespython3to MS Store stub)
Version Detection Pattern
Build scripts use a fallback chain: CLI argument → exact git tag → CI env var (GITEA_REF_NAME / GITHUB_REF_NAME) → hardcoded in source. Always strip leading v for clean version strings.
NSIS Installer Best Practices
- User-scoped install (
$LOCALAPPDATA,RequestExecutionLevel user) — no admin required - Launch after install: Use
MUI_FINISHPAGE_RUN_FUNCTION(notMUI_FINISHPAGE_RUN_PARAMETERS— NSISExecchokes on quoting). Still requiresMUI_FINISHPAGE_RUN ""defined for checkbox visibility - Detect running instance:
.onInitchecks file lock onpython.exe, offers to kill process before install - Uninstall preserves user data: Remove
python/,app/,logs/but NOTdata/ - CI build:
sudo apt-get install -y nsis msitools zipthenmakensis -DVERSION="${VERSION}" build/installer.nsi
Hidden Launcher (VBS)
All shortcuts and the installer finish page use scripts/start-hidden.vbs instead of .bat to avoid console window flash. The VBS launcher must include an embedded Python fallback — installed distributions don't have Python on PATH, dev environment uses system Python.
Gitea vs GitHub Actions Differences
| Feature | GitHub Actions | Gitea Actions |
|---|---|---|
| Context prefix | github.* |
gitea.* |
| Ref name | ${{ github.ref_name }} |
${{ gitea.ref_name }} |
| Server URL | ${{ github.server_url }} |
${{ gitea.server_url }} |
| Output vars | $GITHUB_OUTPUT |
$GITHUB_OUTPUT (same) |
| Secrets | ${{ secrets.NAME }} |
${{ secrets.NAME }} (same) |
| Docker Buildx | Available | May not work (runner networking) |
Common Tasks
Creating a release
git tag v0.2.0
git push origin v0.2.0
Creating a pre-release
git tag v0.2.0-alpha.1
git push origin v0.2.0-alpha.1
Adding a new build artifact
- Update the build script to produce the new file
- Add upload step in the relevant
build-*job - Update the release description in
create-releasejob body template - Test with a pre-release tag first
Re-triggering a failed release workflow
# Option A: Delete and re-push the same tag
git push origin :refs/tags/v0.1.0-alpha.2
# Delete the release in Gitea UI or via API
git tag -f v0.1.0-alpha.2
git push origin v0.1.0-alpha.2
# Option B: Just bump the version (simpler)
git tag v0.1.0-alpha.3
git push origin v0.1.0-alpha.3
The create-release job has fallback logic — if the release already exists for a tag, it fetches and reuses the existing release ID.
Local Build Testing (Windows)
Prerequisites
- NSIS:
& "$env:LOCALAPPDATA\Microsoft\WindowsApps\winget.exe" install NSIS.NSIS - Installs to
C:\Program Files (x86)\NSIS\makensis.exe
Build steps
npm ci && npm run build # frontend
bash build/build-dist-windows.sh v1.0.0 # Windows dist
"/c/Program Files (x86)/NSIS/makensis.exe" -DVERSION="1.0.0" build/installer.nsi # installer
Iterating on installer only
If only installer.nsi changed (not app code), skip the full rebuild — just re-run makensis. If app code changed, re-run build/build-dist-windows.sh first since dist/ is a snapshot.
Common issues
| Issue | Fix |
|---|---|
zip: command not found |
Git Bash doesn't include zip — harmless for installer builds |
Exec expects 1 parameters, got 2 |
Use MUI_FINISHPAGE_RUN_FUNCTION instead of MUI_FINISHPAGE_RUN_PARAMETERS |
Error opening file for writing: python\_asyncio.pyd |
Server is running — stop it before installing |
| App doesn't start after install | VBS must use embedded Python fallback, not bare python |
winget not recognized |
Use full path: $env:LOCALAPPDATA\Microsoft\WindowsApps\winget.exe |
dist/ has stale files |
Re-run full build script — dist/ doesn't auto-update |