Compare commits
4 Commits
v0.1.0-alp
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 52c8614a3c | |||
| 5c814a64a7 | |||
| 0716d602e2 | |||
| 42bc05c968 |
@@ -25,13 +25,51 @@ jobs:
|
|||||||
IS_PRE="true"
|
IS_PRE="true"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Build registry path for Docker instructions
|
||||||
|
SERVER_HOST=$(echo "${{ gitea.server_url }}" | sed -E 's|https?://||')
|
||||||
|
REPO=$(echo "${{ gitea.repository }}" | tr '[:upper:]' '[:lower:]')
|
||||||
|
DOCKER_IMAGE="${SERVER_HOST}/${REPO}"
|
||||||
|
|
||||||
|
# Build release body via Python to avoid YAML escaping issues
|
||||||
|
BODY_JSON=$(python3 -c "
|
||||||
|
import json, sys
|
||||||
|
tag = '$TAG'
|
||||||
|
image = '$DOCKER_IMAGE'
|
||||||
|
body = f'''## Downloads
|
||||||
|
|
||||||
|
| Platform | File | Description |
|
||||||
|
|----------|------|-------------|
|
||||||
|
| Windows (installer) | \`LedGrab-{tag}-setup.exe\` | Install with Start Menu shortcut, optional autostart, uninstaller |
|
||||||
|
| Windows (portable) | \`LedGrab-{tag}-win-x64.zip\` | Unzip anywhere, run LedGrab.bat |
|
||||||
|
| Linux | \`LedGrab-{tag}-linux-x64.tar.gz\` | Extract, run ./run.sh |
|
||||||
|
| Docker | See below | docker pull + docker run |
|
||||||
|
|
||||||
|
After starting, open **http://localhost:8080** in your browser.
|
||||||
|
|
||||||
|
### Docker
|
||||||
|
|
||||||
|
\`\`\`bash
|
||||||
|
docker pull {image}:{tag}
|
||||||
|
docker run -d --name ledgrab -p 8080:8080 -v ledgrab-data:/app/data {image}:{tag}
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### First-time setup
|
||||||
|
|
||||||
|
1. Change the default API key in config/default_config.yaml
|
||||||
|
2. Open http://localhost:8080 and discover your WLED devices
|
||||||
|
3. See INSTALLATION.md for detailed configuration
|
||||||
|
'''
|
||||||
|
import textwrap
|
||||||
|
print(json.dumps(textwrap.dedent(body).strip()))
|
||||||
|
")
|
||||||
|
|
||||||
RELEASE=$(curl -s -X POST "$BASE_URL/releases" \
|
RELEASE=$(curl -s -X POST "$BASE_URL/releases" \
|
||||||
-H "Authorization: token $GITEA_TOKEN" \
|
-H "Authorization: token $GITEA_TOKEN" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d "{
|
-d "{
|
||||||
\"tag_name\": \"$TAG\",
|
\"tag_name\": \"$TAG\",
|
||||||
\"name\": \"LedGrab $TAG\",
|
\"name\": \"LedGrab $TAG\",
|
||||||
\"body\": \"## Downloads\\n\\n| Platform | File | How to run |\\n|----------|------|------------|\\n| Windows | \`LedGrab-${TAG}-win-x64.zip\` | Unzip → run \`LedGrab.bat\` → open http://localhost:8080 |\\n| Linux | \`LedGrab-${TAG}-linux-x64.tar.gz\` | Extract → run \`./run.sh\` → open http://localhost:8080 |\\n| Docker | See below | \`docker pull\` → \`docker run\` |\\n\\n### Docker\\n\\n\`\`\`bash\\ndocker pull ${{ gitea.server_url }}/${{ gitea.repository }}:${TAG}\\ndocker run -d -p 8080:8080 ${{ gitea.server_url }}/${{ gitea.repository }}:${TAG}\\n\`\`\`\",
|
\"body\": $BODY_JSON,
|
||||||
\"draft\": false,
|
\"draft\": false,
|
||||||
\"prerelease\": $IS_PRE
|
\"prerelease\": $IS_PRE
|
||||||
}")
|
}")
|
||||||
|
|||||||
12
CLAUDE.md
12
CLAUDE.md
@@ -38,6 +38,7 @@ ast-index changed --base master # Show symbols changed in current bran
|
|||||||
| [contexts/graph-editor.md](contexts/graph-editor.md) | Visual graph editor changes |
|
| [contexts/graph-editor.md](contexts/graph-editor.md) | Visual graph editor changes |
|
||||||
| [contexts/server-operations.md](contexts/server-operations.md) | Server restart, startup modes, demo mode |
|
| [contexts/server-operations.md](contexts/server-operations.md) | Server restart, startup modes, demo mode |
|
||||||
| [contexts/chrome-tools.md](contexts/chrome-tools.md) | Chrome MCP tool usage for testing |
|
| [contexts/chrome-tools.md](contexts/chrome-tools.md) | Chrome MCP tool usage for testing |
|
||||||
|
| [contexts/ci-cd.md](contexts/ci-cd.md) | CI/CD pipelines, release workflow, build scripts |
|
||||||
| [server/CLAUDE.md](server/CLAUDE.md) | Backend architecture, API patterns, common tasks |
|
| [server/CLAUDE.md](server/CLAUDE.md) | Backend architecture, API patterns, common tasks |
|
||||||
|
|
||||||
## Task Tracking via TODO.md
|
## Task Tracking via TODO.md
|
||||||
@@ -53,6 +54,7 @@ Use `TODO.md` in the project root as the primary task tracker. **Do NOT use the
|
|||||||
**NEVER rename a storage file path, store key, entity ID prefix, or JSON field name without writing a migration.** User data lives in JSON files under `data/`. If the code starts reading from a new filename while the old file still has user data, THAT DATA IS SILENTLY LOST.
|
**NEVER rename a storage file path, store key, entity ID prefix, or JSON field name without writing a migration.** User data lives in JSON files under `data/`. If the code starts reading from a new filename while the old file still has user data, THAT DATA IS SILENTLY LOST.
|
||||||
|
|
||||||
When renaming any storage-related identifier:
|
When renaming any storage-related identifier:
|
||||||
|
|
||||||
1. **Add migration logic in `BaseJsonStore.__init__`** (or the specific store) that detects the old file/key and migrates data to the new name automatically on startup
|
1. **Add migration logic in `BaseJsonStore.__init__`** (or the specific store) that detects the old file/key and migrates data to the new name automatically on startup
|
||||||
2. **Log a clear warning** when migration happens so the user knows
|
2. **Log a clear warning** when migration happens so the user knows
|
||||||
3. **Keep the old file as a backup** after migration (rename to `.migrated` or similar)
|
3. **Keep the old file as a backup** after migration (rename to `.migrated` or similar)
|
||||||
@@ -63,6 +65,16 @@ This applies to: file paths in `StorageConfig`, JSON root keys (e.g. `picture_ta
|
|||||||
|
|
||||||
**Incident context:** A past rename of `picture_targets.json` → `output_targets.json` was done without migration. The app created a new empty `output_targets.json` while the user's 7 targets sat unread in the old file. Data was silently lost.
|
**Incident context:** A past rename of `picture_targets.json` → `output_targets.json` was done without migration. The app created a new empty `output_targets.json` while the user's 7 targets sat unread in the old file. Data was silently lost.
|
||||||
|
|
||||||
|
## Pre-Commit Checks (MANDATORY)
|
||||||
|
|
||||||
|
Before every commit, run the relevant linters and fix any issues:
|
||||||
|
|
||||||
|
- **Python changes**: `cd server && ruff check src/ tests/ --fix`
|
||||||
|
- **TypeScript changes**: `cd server && npx tsc --noEmit && npm run build`
|
||||||
|
- **Both**: Run both checks
|
||||||
|
|
||||||
|
Do NOT commit code that fails linting. Fix the issues first.
|
||||||
|
|
||||||
## General Guidelines
|
## General Guidelines
|
||||||
|
|
||||||
- Always test changes before marking as complete
|
- Always test changes before marking as complete
|
||||||
|
|||||||
80
contexts/ci-cd.md
Normal file
80
contexts/ci-cd.md
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
# CI/CD & Release Workflow
|
||||||
|
|
||||||
|
## 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)
|
||||||
|
|
||||||
|
## 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
|
||||||
@@ -98,10 +98,10 @@ async def lifespan(app: FastAPI):
|
|||||||
logger.info(f"Starting LED Grab v{__version__}")
|
logger.info(f"Starting LED Grab v{__version__}")
|
||||||
logger.info(f"Python version: {sys.version}")
|
logger.info(f"Python version: {sys.version}")
|
||||||
logger.info(f"Server listening on {config.server.host}:{config.server.port}")
|
logger.info(f"Server listening on {config.server.host}:{config.server.port}")
|
||||||
print(f"\n =============================================")
|
print("\n =============================================")
|
||||||
print(f" LED Grab v{__version__}")
|
print(f" LED Grab v{__version__}")
|
||||||
print(f" Open http://localhost:{config.server.port} in your browser")
|
print(f" Open http://localhost:{config.server.port} in your browser")
|
||||||
print(f" =============================================\n")
|
print(" =============================================\n")
|
||||||
|
|
||||||
# Validate authentication configuration
|
# Validate authentication configuration
|
||||||
if not config.auth.api_keys:
|
if not config.auth.api_keys:
|
||||||
|
|||||||
Reference in New Issue
Block a user