The release tarball previously omitted dashcaddy-api/src/, which meant the
in-container self-updater had to apply post-deploy patches (dashcaddy-post-
deploy-patches.sh) to work around missing files. That script generates 37
flat copies of src/ files at the dashcaddy-api/ root level to satisfy
broken require() paths. With proper src/ shipping, those files become
obsolete, but they were still being shown as untracked in git.
Changes:
- BUILD-PIPELINE-FIX.md documents the build pipeline fix (in /opt/dashcaddy-release/
build-release.sh — sibling repo, not tracked here)
- .gitignore now ignores the 37 generated post-deploy artifacts plus the
backups/ and updates/ runtime directories, so 'git status' stays clean
- scripts/dashcaddy-post-deploy-patches.sh is now tracked so it's preserved
across rebuilds (still useful as a safety net for transitional installs)
- Updated root VERSION file from 1.13.0 → 1.13.4 to match package.json.
- scripts/release.sh now writes both files on every release bump, and
stages VERSION alongside package.json in the release commit.
- This prevents the drift that caused the stale VERSION in the first place.
build.js rewrites three things during `node build.js`:
- status/dist/*.js (bundle output)
- status/index.html (CSP hash for inline bootstrap)
- status/sw.js (cache name derived from bundle content)
release.sh was only staging status/dist/. Result: when a release didn't
touch index.html or sw.js source, the post-build modifications to those
two files were left unstaged, the commit included only dist/, and the
tarball shipped the stale sw.js. Clients then kept the previous SW
cache name -> activate handler never wiped the cache -> precached old
bundles served forever even after they were "updated" on disk.
Now stage index.html and sw.js too. They're tracked (not gitignored)
so a plain `git add` is enough; the commit is a no-op when nothing
actually changed.
- csrf-protection: skip CSRF validation on /api/system/update-notify. The
endpoint has its own X-DashCaddy-Notify-Secret auth and is only ever called
machine-to-machine; browsers never reach it. Without this, the CSRF cookie
check rejects the notify POST before the secret comparison runs.
- release.sh: the verify step piped curl into `node -p ".../dev/stdin"` which
works on Linux but blows up on Windows/git-bash. Replaced with portable
grep+sed extraction so the same script works on both publisher OSes.
- self-updater: per-instance notify secret (auto-generated), notifyAndApply()
triggers an immediate check+apply for the publishing host
- routes: POST /api/system/update-notify (X-DashCaddy-Notify-Secret gated,
added to public-routes allowlist so TOTP doesn't block machine-to-machine)
- dashcaddy-update.sh: include VERSION in backup/deploy/rollback copy lists;
belt-and-suspenders write trigger.json commit to VERSION post-deploy.
Fixes drift where /app/VERSION stayed at the old commit after self-update.
- release.sh: mirror failures are non-fatal+loud; HTTP-verify get2 after
rsync; auto-notify co-located instance via /opt/dashcaddy/updates/notify-secret
(or honour DASHCADDY_NOTIFY_TARGETS for multi-instance setups).
Dockerfile never received DASHCADDY_COMMIT at build, so /app/VERSION held
'unknown'. _isNewer then treated same-version-different-commit as newer,
making the auto-updater rebuild the container indefinitely (each rebuild
still produced commit='unknown').
- self-updater._isNewer: normalize commits; treat unknown/null/empty as no
commit info and fall back to pure version comparison
- self-updater._autoCheckAndApply + routes/updates: refuse to apply when
local version >= remote version (belt-and-suspenders)
- update-management.js: hide '(unknown)' from version label
- Dockerfile: COPY VERSION instead of writing from build arg
- VERSION: committed placeholder ('dev'); scripts/release.sh now writes
the real short SHA into the tarball's VERSION before tar-ing, so every
published release ships with an accurate commit
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Automates what was previously a six-step manual process that, twice
in this codebase's history, has produced version skew between git and
the released tarball (v1.2.0 was published with package.json 1.2.0 in
the tarball but the bump was never committed back to gitea — making
"what code is in v1.2.0?" answerable only by extracting the tarball).
The script:
- Refuses to run with a dirty tree, off main, or already at the
target version.
- Bumps dashcaddy-api/package.json, rebuilds status/dist/, commits
+ pushes to gitea — so the released artifact and gitea HEAD are
always in lockstep.
- Clones gitea HEAD on the release host, verifies the cloned commit
matches what we just pushed (catches a stale clone or a missed
push), tars it, computes sha256, writes version.json.
- Refreshes install.sh on the release host alongside the tarball
(fresh installs use the install.sh from the latest release).
- Mirrors the release dir to the get2 backup via rsync.
- Verifies live by curling version.json and re-hashing the served
tarball.
Hosts overridable via DASHCADDY_RELEASE_HOST / DASHCADDY_MIRROR_HOST
/ DASHCADDY_GITEA_URL env vars.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>