fix(release): ship prebuilt assets and bump fallback version
Two release-blocking bugs traced to the same root cause: the unanchored
`data/` rule in .gitignore matched server/src/ledgrab/data/, which is
where shipped package assets live (prebuilt sounds, game adapters).
The files were never `git add`-able without -f, so they never reached
the v0.4.2 tag and CI builds couldn't include them.
- .gitignore: anchor /data/ and /server/data/ so nested package data
dirs are not ignored.
- Track previously-excluded shipped assets:
- server/src/ledgrab/data/prebuilt_sounds/{alert,bell,chime,ping,pop}.wav
- server/src/ledgrab/data/game_adapters/{minecraft,rocket_league,valorant}.yaml
- Bump _FALLBACK_VERSION 0.3.0 -> 0.4.2 to match pyproject.toml.
The Windows installer strips ledgrab-*.dist-info, so
importlib.metadata falls back to this literal — which is why
v0.4.2 reports v0.3.0 in the WebUI.
- Patch _FALLBACK_VERSION at bundle time in build-common.sh and
build-dist.ps1 so future drift is auto-corrected by the build.
This commit is contained in:
+6
-2
@@ -62,8 +62,12 @@ htmlcov/
|
||||
logs/
|
||||
*.log.*
|
||||
|
||||
# Runtime data
|
||||
data/
|
||||
# Runtime data — anchor to repo root so nested package data dirs
|
||||
# (server/src/ledgrab/data/prebuilt_sounds, game_adapters) are NOT ignored.
|
||||
# An unanchored `data/` rule silently broke the v0.4.2 release by keeping
|
||||
# shipped sound assets out of the CI tag checkout.
|
||||
/data/
|
||||
/server/data/
|
||||
*.db
|
||||
*.sqlite
|
||||
*.json.bak
|
||||
|
||||
@@ -69,6 +69,16 @@ copy_app_files() {
|
||||
# Clean up source maps and __pycache__
|
||||
find "$APP_DIR" -name "*.map" -delete 2>/dev/null || true
|
||||
find "$APP_DIR" -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true
|
||||
|
||||
# Patch the fallback version in the bundled __init__.py. Bundled installs
|
||||
# strip ledgrab-*.dist-info from site-packages, so importlib.metadata
|
||||
# falls back to this literal at runtime — and a stale literal is what
|
||||
# silently shipped v0.4.2 reporting "0.3.0" in the WebUI.
|
||||
local bundled_init="$APP_DIR/src/ledgrab/__init__.py"
|
||||
if [ -f "$bundled_init" ] && [ -n "${VERSION_CLEAN:-}" ]; then
|
||||
sed -i "s/_FALLBACK_VERSION = \"[^\"]*\"/_FALLBACK_VERSION = \"${VERSION_CLEAN}\"/" "$bundled_init"
|
||||
echo " Patched _FALLBACK_VERSION -> ${VERSION_CLEAN}"
|
||||
fi
|
||||
}
|
||||
|
||||
# ── Site-packages cleanup ────────────────────────────────────
|
||||
|
||||
@@ -196,6 +196,17 @@ New-Item -ItemType Directory -Path (Join-Path $DistDir "logs") -Force | Out-Null
|
||||
Get-ChildItem -Path $srcDest -Recurse -Filter "*.map" | Remove-Item -Force -ErrorAction SilentlyContinue
|
||||
Get-ChildItem -Path $srcDest -Recurse -Directory -Filter "__pycache__" | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue
|
||||
|
||||
# Patch the fallback version in the bundled __init__.py so the WebUI always
|
||||
# reports the release version — the installer strips ledgrab-*.dist-info from
|
||||
# site-packages (above), so importlib.metadata falls back to this literal.
|
||||
$bundledInit = Join-Path $srcDest "ledgrab\__init__.py"
|
||||
if (Test-Path $bundledInit) {
|
||||
$initContent = Get-Content $bundledInit -Raw
|
||||
$patched = [regex]::Replace($initContent, '_FALLBACK_VERSION\s*=\s*"[^"]*"', "_FALLBACK_VERSION = `"$VersionClean`"")
|
||||
Set-Content -Path $bundledInit -Value $patched -NoNewline
|
||||
Write-Host " Patched _FALLBACK_VERSION -> $VersionClean"
|
||||
}
|
||||
|
||||
# ── Create launcher ────────────────────────────────────────────
|
||||
|
||||
Write-Host "[8/8] Creating launcher..."
|
||||
|
||||
@@ -2,10 +2,14 @@
|
||||
|
||||
from importlib.metadata import version, PackageNotFoundError
|
||||
|
||||
# Fallback version — kept in sync with pyproject.toml.
|
||||
# Fallback version — kept in sync with pyproject.toml. MUST match the
|
||||
# version declared there on every release. The Windows installer build
|
||||
# (build/build-dist.ps1) also patches this literal to the resolved build
|
||||
# version, so any drift here is corrected for bundled distributions.
|
||||
# Used when the package isn't pip-installed (e.g. embedded via Chaquopy
|
||||
# on Android, where the source is included directly via source sets).
|
||||
_FALLBACK_VERSION = "0.3.0"
|
||||
# on Android, where the source is included directly via source sets, or
|
||||
# in the Windows bundle where the installed dist-info is stripped).
|
||||
_FALLBACK_VERSION = "0.4.2"
|
||||
|
||||
try:
|
||||
__version__ = version("ledgrab")
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
# Minecraft community adapter
|
||||
# Requires a server-side mod that sends game state via webhook
|
||||
# (e.g., GameStateIntegration mod or custom Fabric/Forge mod)
|
||||
#
|
||||
# Configure your mod to POST JSON to:
|
||||
# http://<WLED_IP>:8080/api/v1/game-integrations/<ID>/event
|
||||
|
||||
name: minecraft
|
||||
game: Minecraft
|
||||
protocol: webhook
|
||||
|
||||
mappings:
|
||||
- source_path: player.health
|
||||
event: health
|
||||
min: 0
|
||||
max: 20
|
||||
trigger: on_change
|
||||
|
||||
- source_path: player.armor
|
||||
event: armor
|
||||
min: 0
|
||||
max: 20
|
||||
trigger: on_change
|
||||
|
||||
- source_path: player.food_level
|
||||
event: energy
|
||||
min: 0
|
||||
max: 20
|
||||
trigger: on_change
|
||||
|
||||
- source_path: player.experience_level
|
||||
event: speed
|
||||
min: 0
|
||||
max: 100
|
||||
trigger: on_change
|
||||
|
||||
- source_path: player.deaths
|
||||
event: death
|
||||
trigger: on_increase
|
||||
min: 0
|
||||
max: 100
|
||||
|
||||
- source_path: stats.kills
|
||||
event: kill
|
||||
trigger: on_increase
|
||||
min: 0
|
||||
max: 100
|
||||
|
||||
auth:
|
||||
type: header
|
||||
header: X-Minecraft-Auth
|
||||
|
||||
setup_instructions: |
|
||||
## Minecraft Integration Setup
|
||||
|
||||
This adapter requires a server-side mod that sends game state data as JSON.
|
||||
|
||||
**Recommended mods:**
|
||||
- [GameStateIntegration](https://github.com/example/gsi-mod) (Fabric)
|
||||
- Custom Forge mod using `PlayerTickEvent`
|
||||
|
||||
**Expected JSON format:**
|
||||
```json
|
||||
{
|
||||
"player": {
|
||||
"health": 20.0,
|
||||
"armor": 10,
|
||||
"food_level": 18,
|
||||
"experience_level": 30
|
||||
},
|
||||
"stats": {
|
||||
"kills": 5
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Configure the mod to POST to the event endpoint with the auth token
|
||||
in the `X-Minecraft-Auth` header.
|
||||
@@ -0,0 +1,99 @@
|
||||
# Rocket League community adapter
|
||||
# Uses the SOS (Rocket League Overlay System) plugin
|
||||
# https://gitlab.com/bakkesplugins/sos/sos-plugin
|
||||
#
|
||||
# SOS sends game state via WebSocket, but you can use a bridge
|
||||
# to forward events as HTTP POST to:
|
||||
# http://<WLED_IP>:8080/api/v1/game-integrations/<ID>/event
|
||||
|
||||
name: rocket_league
|
||||
game: Rocket League
|
||||
protocol: webhook
|
||||
|
||||
mappings:
|
||||
- source_path: player.boost
|
||||
event: energy
|
||||
min: 0
|
||||
max: 100
|
||||
trigger: on_change
|
||||
|
||||
- source_path: player.speed
|
||||
event: speed
|
||||
min: 0
|
||||
max: 2300
|
||||
trigger: on_value
|
||||
|
||||
- source_path: match.goals_scored
|
||||
event: kill
|
||||
trigger: on_increase
|
||||
min: 0
|
||||
max: 20
|
||||
|
||||
- source_path: match.goals_conceded
|
||||
event: death
|
||||
trigger: on_increase
|
||||
min: 0
|
||||
max: 20
|
||||
|
||||
- source_path: match.time_remaining
|
||||
event: objective_progress
|
||||
min: 0
|
||||
max: 300
|
||||
trigger: on_value
|
||||
|
||||
- source_path: game.started
|
||||
event: match_start
|
||||
trigger: on_change
|
||||
min: 0
|
||||
max: 1
|
||||
|
||||
- source_path: game.ended
|
||||
event: match_end
|
||||
trigger: on_change
|
||||
min: 0
|
||||
max: 1
|
||||
|
||||
- source_path: team.score_blue
|
||||
event: team_a
|
||||
min: 0
|
||||
max: 10
|
||||
trigger: on_change
|
||||
|
||||
- source_path: team.score_orange
|
||||
event: team_b
|
||||
min: 0
|
||||
max: 10
|
||||
trigger: on_change
|
||||
|
||||
setup_instructions: |
|
||||
## Rocket League Integration Setup
|
||||
|
||||
This adapter works with the SOS (Rocket League Overlay System) plugin.
|
||||
|
||||
**Setup:**
|
||||
1. Install BakkesMod: https://bakkesmod.com
|
||||
2. Install the SOS plugin from the BakkesMod plugin manager
|
||||
3. Use a WebSocket-to-HTTP bridge to forward SOS events
|
||||
|
||||
**Bridge tool:**
|
||||
A small script that connects to SOS WebSocket (ws://localhost:49122)
|
||||
and forwards events as HTTP POST to the WLED event endpoint.
|
||||
|
||||
**Expected JSON format:**
|
||||
```json
|
||||
{
|
||||
"player": {
|
||||
"boost": 75,
|
||||
"speed": 1500
|
||||
},
|
||||
"match": {
|
||||
"goals_scored": 2,
|
||||
"goals_conceded": 1,
|
||||
"time_remaining": 180
|
||||
},
|
||||
"team": {
|
||||
"score_blue": 2,
|
||||
"score_orange": 1
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,85 @@
|
||||
# Valorant community adapter
|
||||
# Uses Overwolf/Insights API or third-party overlay tool
|
||||
# that exposes game state via webhook
|
||||
#
|
||||
# Configure your overlay to POST JSON to:
|
||||
# http://<WLED_IP>:8080/api/v1/game-integrations/<ID>/event
|
||||
|
||||
name: valorant
|
||||
game: Valorant
|
||||
protocol: webhook
|
||||
|
||||
mappings:
|
||||
- source_path: player.health
|
||||
event: health
|
||||
min: 0
|
||||
max: 100
|
||||
trigger: on_change
|
||||
|
||||
- source_path: player.shield
|
||||
event: shield
|
||||
min: 0
|
||||
max: 50
|
||||
trigger: on_change
|
||||
|
||||
- source_path: player.money
|
||||
event: gold
|
||||
min: 0
|
||||
max: 9000
|
||||
trigger: on_change
|
||||
|
||||
- source_path: match.kills
|
||||
event: kill
|
||||
trigger: on_increase
|
||||
min: 0
|
||||
max: 50
|
||||
|
||||
- source_path: match.deaths
|
||||
event: death
|
||||
trigger: on_increase
|
||||
min: 0
|
||||
max: 50
|
||||
|
||||
- source_path: match.round_phase
|
||||
event: round_start
|
||||
trigger: on_change
|
||||
min: 0
|
||||
max: 1
|
||||
|
||||
- source_path: match.spike_planted
|
||||
event: objective_captured
|
||||
trigger: on_change
|
||||
min: 0
|
||||
max: 1
|
||||
|
||||
auth:
|
||||
type: header
|
||||
header: X-Valorant-Auth
|
||||
|
||||
setup_instructions: |
|
||||
## Valorant Integration Setup
|
||||
|
||||
Valorant does not have a native Game State Integration API.
|
||||
You need a third-party tool to capture and forward game data.
|
||||
|
||||
**Options:**
|
||||
- Overwolf with a game events plugin
|
||||
- Insights.gg capture API
|
||||
- Custom screen-reading overlay
|
||||
|
||||
**Expected JSON format:**
|
||||
```json
|
||||
{
|
||||
"player": {
|
||||
"health": 100,
|
||||
"shield": 50,
|
||||
"money": 3900
|
||||
},
|
||||
"match": {
|
||||
"kills": 12,
|
||||
"deaths": 5,
|
||||
"round_phase": 1,
|
||||
"spike_planted": 0
|
||||
}
|
||||
}
|
||||
```
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user