Auto Update#366
Conversation
WalkthroughThe update page adds an "Auto update" button that automatically retrieves the latest firmware from the configured GitHub repository, matches it to the device build configuration, downloads the binary, and submits it through the existing multipart form—eliminating the manual download step. ChangesAuto-update Feature
Sequence DiagramsequenceDiagram
participant User
participant Device as Local Device
participant GitHub as GitHub API
participant Form as Update Form
User->>Form: Click "Auto update" button
Form->>Device: GET /json/info
Device-->>Form: Device build metadata
Form->>GitHub: GET latest release (configured repo)
GitHub-->>Form: Release data with assets
Form->>Form: Derive expected asset name
Form->>GitHub: Download matching .bin asset
GitHub-->>Form: Binary content
Form->>Form: Inject binary into file input
Form->>Form: Submit multipart form
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
wled00/data/update.htm (1)
10-54: ⚡ Quick winMark WLED-MM-specific additions with
// WLEDMMcomments.Please annotate the new auto-update JS/UI additions with
// WLEDMMcomments to match project web UI conventions.As per coding guidelines, “add
// WLEDMMcomments on WLED-MM specific additions.”Also applies to: 68-70
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@wled00/data/update.htm` around lines 10 - 54, The autoUpdate function contains WLED-MM specific logic (GitHub lookup, asset selection, download replacement of host, building File/DataTransfer and form submit) and needs inline markers; add // WLEDMM comments on each WLED-MM-specific addition inside autoUpdate—specifically annotate the GitHub fetch block (fetch to api.github.com and parsing ghRelease), the assetSuffix/asset selection logic, the host-rewrite line (fwUrl = ... replace...), the blob->File/DataTransfer handling and the form submit/U() call—so each of those unique sections (autoUpdate, infoResp/ghResp/ghRelease handling, asset selection, fwUrl replacement, File/DataTransfer creation, and gId('uf').submit()) is followed or prefixed with a // WLEDMM comment to match project UI conventions.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@wled00/data/update.htm`:
- Around line 29-33: In autoUpdate(), guard use of ghRelease.tag_name,
ghRelease.assets and info.release before calling .replace, .find or .map: check
typeof ghRelease.tag_name === 'string' before computing releaseVer, ensure
Array.isArray(ghRelease.assets) before searching/mapping assets, and verify
info.release is present (non-empty string) before building assetSuffix; for each
missing/invalid condition set a clear status.textContent (e.g., "Missing release
tag", "No assets array in release", "Missing device info.release") and
early-return to avoid the generic error path.
- Around line 17-18: autoUpdate() currently performs three fetch() calls (info
fetch via getURL('/json/info'), GitHub latest release fetch, and firmware
download) with no timeout; add a small AbortController-based helper (e.g.,
fetchWithTimeout or timeoutFetch) that accepts a URL, options, and a timeout ms,
uses AbortController to cancel the request after the timeout, and throws on
timeout, then replace the direct fetch(...) calls inside autoUpdate() with this
helper; mark your additions with the required // WLEDMM comment(s) in the inline
<script> (place a // WLEDMM above the helper and another where you call the
helper in autoUpdate() so WLED-MM-specific changes are clearly identifiable).
---
Nitpick comments:
In `@wled00/data/update.htm`:
- Around line 10-54: The autoUpdate function contains WLED-MM specific logic
(GitHub lookup, asset selection, download replacement of host, building
File/DataTransfer and form submit) and needs inline markers; add // WLEDMM
comments on each WLED-MM-specific addition inside autoUpdate—specifically
annotate the GitHub fetch block (fetch to api.github.com and parsing ghRelease),
the assetSuffix/asset selection logic, the host-rewrite line (fwUrl = ...
replace...), the blob->File/DataTransfer handling and the form submit/U()
call—so each of those unique sections (autoUpdate, infoResp/ghResp/ghRelease
handling, asset selection, fwUrl replacement, File/DataTransfer creation, and
gId('uf').submit()) is followed or prefixed with a // WLEDMM comment to match
project UI conventions.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: a7a47339-1ddb-4377-b45f-bf93bea19513
📒 Files selected for processing (1)
wled00/data/update.htm
| const infoResp = await fetch(getURL('/json/info')); | ||
| if (!infoResp.ok) throw new Error('Failed to fetch device info'); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="wled00/data/update.htm"
echo "== File header (first 60 lines) =="
sed -n '1,60p' "$FILE" | nl -ba | sed -e 's/^/ /'
echo
echo "== Lines 1-120 with line numbers (to find autoUpdate and fetches) =="
sed -n '1,140p' "$FILE" | nl -ba | sed -e 's/^/ /'
echo
echo "== Search for fetch usage =="
rg -n "fetch\(" "$FILE" || true
echo
echo "== Show autoUpdate function block (best-effort) =="
# Print around the first occurrence of autoUpdate
LINE_AUTO=$(rg -n "autoUpdate" "$FILE" | head -n 1 | cut -d: -f1 || true)
if [ -n "${LINE_AUTO:-}" ]; then
START=$((LINE_AUTO-40))
END=$((LINE_AUTO+180))
START=$((START<1?1:START))
sed -n "${START},${END}p" "$FILE" | nl -ba | sed -e 's/^/ /'
fi
echo
echo "== Check for any AbortController/timeout wrapper already present =="
rg -n "AbortController|signal:|timeoutMs|fetchWithTimeout" "$FILE" || true
echo
echo "== Check for WLEDMM markers near additions =="
rg -n "//\s*WLEDMM" "$FILE" || true
echo
echo "== Show nearby lines where // WLEDMM might be missing (lines 1-140) for context =="
sed -n '1,140p' "$FILE" | nl -ba | sed -e 's/^/ /'Repository: MoonModules/WLED-MM
Length of output: 159
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="wled00/data/update.htm"
echo "== File header (first 80 lines) =="
sed -n '1,80p' "$FILE" | cat -n
echo
echo "== Lines 1-220 with line numbers (find autoUpdate/fetches) =="
sed -n '1,220p' "$FILE" | cat -n
echo
echo "== Search for fetch usage =="
rg -n "fetch\(" "$FILE" || true
echo
echo "== Search for AbortController/timeout wrapper =="
rg -n "AbortController|fetchWithTimeout|timeoutMs|signal:" "$FILE" || true
echo
echo "== Search for WLEDMM markers =="
rg -n "//\s*WLEDMM" "$FILE" || true
echo
echo "== Show full autoUpdate block vicinity (around first autoUpdate) =="
LINE_AUTO=$(rg -n "autoUpdate" "$FILE" | head -n 1 | cut -d: -f1 || true)
if [ -n "${LINE_AUTO:-}" ]; then
START=$((LINE_AUTO-60))
END=$((LINE_AUTO+260))
START=$((START<1?1:START))
sed -n "${START},${END}p" "$FILE" | cat -n
fiRepository: MoonModules/WLED-MM
Length of output: 12732
Add fetch timeouts (AbortController) to prevent the Auto update flow from hanging
autoUpdate() has three fetch() calls with no timeout, so the button can remain disabled indefinitely on a stalled network request (device info, GitHub latest release, and firmware download).
- Add an
AbortController-based timeout wrapper and use it for allfetch()calls inautoUpdate(). - Add the required
// WLEDMMmarker(s) for the WLED-MM-specific JS additions in this file (there are no// WLEDMMcomments in the inline<script>).
Suggested patch
async function autoUpdate() {
const btn = gId('autoUpdBtn');
const status = gId('autoUpdStatus');
+ const fetchWithTimeout = async (url, options = {}, timeoutMs = 15000) => {
+ const controller = new AbortController();
+ const t = setTimeout(() => controller.abort(), timeoutMs);
+ try {
+ return await fetch(url, {...options, signal: controller.signal});
+ } finally {
+ clearTimeout(t);
+ }
+ };
btn.disabled = true;
status.textContent = '';
try {
status.textContent = 'Fetching device info...';
- const infoResp = await fetch(getURL('/json/info'));
+ const infoResp = await fetchWithTimeout(getURL('/json/info'));
if (!infoResp.ok) throw new Error('Failed to fetch device info');
const info = await infoResp.json();
@@
- const ghResp = await fetch(`https://api.github.com/repos/${info.repo}/releases/latest`);
+ const ghResp = await fetchWithTimeout(`https://api.github.com/repos/${info.repo}/releases/latest`);
@@
- const fwResp = await fetch(fwUrl);
+ const fwResp = await fetchWithTimeout(fwUrl);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@wled00/data/update.htm` around lines 17 - 18, autoUpdate() currently performs
three fetch() calls (info fetch via getURL('/json/info'), GitHub latest release
fetch, and firmware download) with no timeout; add a small AbortController-based
helper (e.g., fetchWithTimeout or timeoutFetch) that accepts a URL, options, and
a timeout ms, uses AbortController to cancel the request after the timeout, and
throws on timeout, then replace the direct fetch(...) calls inside autoUpdate()
with this helper; mark your additions with the required // WLEDMM comment(s) in
the inline <script> (place a // WLEDMM above the helper and another where you
call the helper in autoUpdate() so WLED-MM-specific changes are clearly
identifiable).
| const releaseVer = ghRelease.tag_name.replace(/^v/, ''); | ||
| const assetSuffix = `_${releaseVer}_${info.release}.bin`; | ||
| const asset = ghRelease.assets.find(a => a.name.endsWith(assetSuffix)); | ||
| if (!asset) { | ||
| const available = ghRelease.assets.map(a => a.name).join(', '); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Show referenced section with line numbers
nl -ba wled00/data/update.htm | sed -n '1,120p'
# Show around the cited lines (29-70 in file are likely different due to offsets; also search exact snippet)
rg -n "ghRelease\.tag_name|assetSuffix|ghRelease\.assets\.find|info\.release" wled00/data/update.htm
# Extract larger surrounding context near matches
rg -n "ghRelease\.tag_name" wled00/data/update.htm | cut -d: -f1 | head -n 5 | while read -r ln; do
start=$((ln-30)); end=$((ln+80))
echo "---- context around line $ln ($start-$end) ----"
nl -ba wled00/data/update.htm | sed -n "${start},${end}p"
done
# Find where ghRelease is fetched/parsed and whether there is any schema guard
rg -n "fetch\\(|api\\.github|releases|json\\(|tag_name|assets" wled00/data/update.htm
# Show any catch blocks / error messaging
rg -n "catch\\(|show.*status|status.*msg|alert\\(|toast\\(|error" wled00/data/update.htmRepository: MoonModules/WLED-MM
Length of output: 106
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Basic sanity: list file exists
ls -la wled00/data/update.htm
# Print first ~160 lines with line numbers via awk
awk '{printf "%6d\t%s\n", NR, $0}' wled00/data/update.htm | sed -n '1,160p'
# Print around the referenced snippet area by searching for the exact line fragments
# (no nl needed)
awk '{if ($0 ~ /ghRelease\.tag_name/ || $0 ~ /assetSuffix/ || $0 ~ /ghRelease\.assets\.find/ || $0 ~ /info\.release/) print NR "\t" $0}' wled00/data/update.htm
# Get broader context around where ghRelease.tag_name appears
ln=$(awk '/ghRelease\.tag_name/ {print NR; exit}' wled00/data/update.htm)
start=$((ln-60)); end=$((ln+120))
awk -v s="$start" -v e="$end" '{if(NR>=s && NR<=e) printf "%6d\t%s\n", NR, $0}' wled00/data/update.htm
# Find where ghRelease is constructed/assigned
rg -n --context 3 "ghRelease\s*=" wled00/data/update.htm || true
rg -n --context 3 "tag_name|assets" wled00/data/update.htm || true
# Find where info.release is created/assigned
rg -n --context 3 "info\.release" wled00/data/update.htm || true
# Find relevant error handling paths
rg -n --context 3 "catch\\(|show.*status|status.*msg|alert\\(|toast\\(|error" wled00/data/update.htm || trueRepository: MoonModules/WLED-MM
Length of output: 10817
Harden GitHub release parsing before using release fields
In autoUpdate() the code assumes ghRelease.tag_name is a string and ghRelease.assets is an array, and it uses info.release without checking it exists. If the GitHub/device payload shape differs, tag_name.replace(...) / assets.find(...) / assets.map(...) can throw and fall into the generic Auto-update failed: ... message; add guards for tag_name, assets (array), and info.release and set a targeted status.textContent when any are missing/invalid.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@wled00/data/update.htm` around lines 29 - 33, In autoUpdate(), guard use of
ghRelease.tag_name, ghRelease.assets and info.release before calling .replace,
.find or .map: check typeof ghRelease.tag_name === 'string' before computing
releaseVer, ensure Array.isArray(ghRelease.assets) before searching/mapping
assets, and verify info.release is present (non-empty string) before building
assetSuffix; for each missing/invalid condition set a clear status.textContent
(e.g., "Missing release tag", "No assets array in release", "Missing device
info.release") and early-return to avoid the generic error path.
There was a problem hiding this comment.
Please suggest some minimal code that would ensure that the code catches unexpected response content.
There was a problem hiding this comment.
@softhack007, here are minimal inline guards to insert right after the two await *.json() calls:
// After: const info = await infoResp.json();
if (!info || typeof info.release !== 'string' || !info.release) {
status.textContent = 'Missing device release info (info.release).';
btn.disabled = false;
return;
}// After: const ghRelease = await ghResp.json();
if (typeof ghRelease.tag_name !== 'string' || !ghRelease.tag_name) {
status.textContent = 'Missing release tag from GitHub response.';
btn.disabled = false;
return;
}
if (!Array.isArray(ghRelease.assets)) {
status.textContent = 'No assets array in GitHub release response.';
btn.disabled = false;
return;
}That gives a targeted, readable error for each unexpected shape instead of falling through to the generic Auto-update failed: TypeError message. The existing !info.repo guard already covers the repo field, so no change is needed there.
🧠 Learnings used
Learnt from: CR
Repo: MoonModules/WLED-MM PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-04-14T23:39:33.591Z
Learning: Applies to wled00/**/*.{h,hpp,cpp,js},wled00/data/**/*.{html,css,js} : Repository language is English — suggest translations for non-English content
Learnt from: CR
Repo: MoonModules/WLED-MM PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-04-14T23:39:33.590Z
Learning: Applies to wled00/html_*.h,wled00/js_*.h : Never edit or commit auto-generated header files `wled00/html_*.h` and `wled00/js_*.h` — these are generated from `wled00/data/` during the build process
Learnt from: CR
Repo: MoonModules/WLED-MM PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-14T23:40:02.031Z
Learning: Applies to wled00/html_*.h wled00/js_*.h : Never edit or commit auto-generated `wled00/html_*.h` or `wled00/js_*.h` files
Learnt from: CR
Repo: MoonModules/WLED-MM PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-14T23:40:02.031Z
Learning: Applies to wled00/data/**/*.{html,js,css} : Run `npm run build` after editing web UI files to regenerate C++ headers
Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 353
File: .github/web.instructions.md:8-9
Timestamp: 2026-04-02T14:17:35.133Z
Learning: In the WLED web UI files under wled00/data (HTML and JavaScript: wled00/data/**/*.{htm,html,js}), indent with tabs. Do not enforce spaces in these files.
|
@coderabbitai please double-check that the logic for constructing the asset filename is compabile with the WLED-MM release naming sheme. For example, See https://github.com/MoonModules/WLED-MM/blob/mdev/.github/workflows/release.yml and https://github.com/MoonModules/WLED-MM/blob/mdev/pio-scripts/output_bins.py |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
echo "=== update.htm (PR branch - auto-ota-mm) ==="
cat wled00/data/update.htm
echo ""
echo "=== .github/workflows/release.yml ==="
cat .github/workflows/release.yml
echo ""
echo "=== pio-scripts/output_bins.py ==="
cat pio-scripts/output_bins.pyLength of output: 11027 🏁 Script executed: #!/bin/bash
echo "=== Search for 'release' in json info construction ==="
rg -n '"release"' wled00/ --type cpp -C3
echo ""
echo "=== Search for 'repo' in json info construction ==="
rg -n '"repo"' wled00/ --type cpp -C3
echo ""
echo "=== WLED_RELEASE_NAME usage in json ==="
rg -n 'WLED_RELEASE_NAME' wled00/ -C2
echo ""
echo "=== Look for json/info handler ==="
rg -n 'serializeInfo\|json.*info\|info.*json' wled00/ --type cpp -C2 | head -80Length of output: 5409 🏁 Script executed: #!/bin/bash
echo "=== WLED_REPO / repoString definition ==="
rg -n 'WLED_REPO\|repoString' wled00/ -C3 | head -80
echo ""
echo "=== Check download.wled.me references in codebase ==="
rg -rn 'download\.wled\.me' --type html --type cpp --type js -C2
echo ""
echo "=== Check WLED_VERSION define format (to verify tag format) ==="
rg -n 'WLED_VERSION' platformio.ini | head -20Length of output: 997
✅ Filename construction logic: Compatible with WLED-MM naming schemeThe suffix-match approach works correctly:
The
|
@netmindz @lost-hope is this finding from the rabbit correct? Should we better redirect to wladi's installer, or does install.wled.me also offer WLED-MM bins? |
|
download.wled.me is the cors proxy of @netmindz iirc. install.wled.me does not offer MM. |
|
We've swapped around between different release names and which are included in the release, which makes testing a little tricky, but my proxy should handle both WLED and MoonModules owner paths |
|
@coderabbitai have you actually tried to fetch any release assets via the CDN or have you just assumed based on the hostname? It's also nothing to do with embedded SSL as the browser fetches the file and you are wrong about the allowed cors hosts when fetching directly from GitHub, especially once you take into account their redirects |
|
Tip For best results, initiate chat on the files or code changes.
The use of (╯°□°)╯︵ 🐰 🧠 Learnings used |
|
Tip For best results, initiate chat on the files or code changes.
The use of (╯°□°)╯︵ 🐰 ✏️ Learnings added
🧠 Learnings used |
This pull request adds an automatic firmware update feature to the WLED update page, allowing users to update their device firmware directly from the web interface with a single click. The update process fetches the latest release from GitHub, downloads the appropriate firmware binary, and uploads it to the device, providing real-time status updates to the user.
New auto-update functionality:
Added a new
autoUpdate()JavaScript function inupdate.htmthat:Introduced a new "Auto update" button and status display to the update page UI, enabling users to trigger the auto-update process and view progress or error messages.
Summary by CodeRabbit