Files
ledgrab/contexts/ci-cd.md
alexei.dolgolyov 151cea3ecb
Build Android APK / build-android (push) Failing after 2m31s
Lint & Test / test (push) Successful in 6m15s
ci: Android multi-ABI APK pipeline + pydantic-core wheel rebuild
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).
2026-04-14 12:36:13 +03:00

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.sh on 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) and LedGrab-{tag}-setup.exe (NSIS installer)

3. build-linux

  • Runs build/build-dist.sh on 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, and latest (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: true on the Gitea release
  • Docker latest tag only applied to stable releases
  • Version in server/pyproject.toml should match the tag (without v prefix)

CI Runners

  • Two TrueNAS Gitea runners with ubuntu tags
  • 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-headless and libportaudio2 for CI compatibility
  • Display-dependent tests are skipped via @requires_display marker
  • Uses python not python3 (Git Bash on Windows resolves python3 to 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 (not MUI_FINISHPAGE_RUN_PARAMETERS — NSIS Exec chokes on quoting). Still requires MUI_FINISHPAGE_RUN "" defined for checkbox visibility
  • Detect running instance: .onInit checks file lock on python.exe, offers to kill process before install
  • Uninstall preserves user data: Remove python/, app/, logs/ but NOT data/
  • CI build: sudo apt-get install -y nsis msitools zip then makensis -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

  1. Update the build script to produce the new file
  2. Add upload step in the relevant build-* job
  3. Update the release description in create-release job body template
  4. 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