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/
|
logs/
|
||||||
*.log.*
|
*.log.*
|
||||||
|
|
||||||
# Runtime data
|
# Runtime data — anchor to repo root so nested package data dirs
|
||||||
data/
|
# (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
|
*.db
|
||||||
*.sqlite
|
*.sqlite
|
||||||
*.json.bak
|
*.json.bak
|
||||||
|
|||||||
@@ -69,6 +69,16 @@ copy_app_files() {
|
|||||||
# Clean up source maps and __pycache__
|
# Clean up source maps and __pycache__
|
||||||
find "$APP_DIR" -name "*.map" -delete 2>/dev/null || true
|
find "$APP_DIR" -name "*.map" -delete 2>/dev/null || true
|
||||||
find "$APP_DIR" -type d -name __pycache__ -exec rm -rf {} + 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 ────────────────────────────────────
|
# ── 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 -Filter "*.map" | Remove-Item -Force -ErrorAction SilentlyContinue
|
||||||
Get-ChildItem -Path $srcDest -Recurse -Directory -Filter "__pycache__" | Remove-Item -Recurse -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 ────────────────────────────────────────────
|
# ── Create launcher ────────────────────────────────────────────
|
||||||
|
|
||||||
Write-Host "[8/8] Creating launcher..."
|
Write-Host "[8/8] Creating launcher..."
|
||||||
|
|||||||
@@ -2,10 +2,14 @@
|
|||||||
|
|
||||||
from importlib.metadata import version, PackageNotFoundError
|
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
|
# Used when the package isn't pip-installed (e.g. embedded via Chaquopy
|
||||||
# on Android, where the source is included directly via source sets).
|
# on Android, where the source is included directly via source sets, or
|
||||||
_FALLBACK_VERSION = "0.3.0"
|
# in the Windows bundle where the installed dist-info is stripped).
|
||||||
|
_FALLBACK_VERSION = "0.4.2"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
__version__ = version("ledgrab")
|
__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