Some checks failed
Lint & Test / test (push) Failing after 30s
release.yml: add fallback for existing releases on tag re-push. installer.nsi: add .onInit file lock check, use LaunchApp function instead of RUN_PARAMETERS to fix NSIS quoting bug. build-dist.ps1: copy start-hidden.vbs to dist scripts/. start-hidden.vbs: embedded Python fallback for installed/dev envs. Update ci-cd.md with version detection, NSIS best practices, local build testing, Gitea vs GitHub differences, troubleshooting. Update frontend.md with full entity type checklist and common pitfalls.
155 lines
6.6 KiB
Markdown
155 lines
6.6 KiB
Markdown
# CI/CD & Release Workflow
|
|
|
|
> **Reference guide:** [Gitea Python CI/CD Guide](https://git.dolgolyov-family.by/alexei.dolgolyov/claude-code-facts/src/branch/main/gitea-python-ci-cd.md) — 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 artifacts + create Gitea release |
|
|
|
|
## 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-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-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-dist-windows.sh` | Linux → Windows cross-build | ZIP + NSIS installer |
|
|
| `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}" 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
|
|
```bash
|
|
git tag v0.2.0
|
|
git push origin v0.2.0
|
|
```
|
|
|
|
### Creating a pre-release
|
|
```bash
|
|
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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
npm ci && npm run build # frontend
|
|
bash build-dist-windows.sh v1.0.0 # Windows dist
|
|
"/c/Program Files (x86)/NSIS/makensis.exe" -DVERSION="1.0.0" 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-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 |
|