Run SyncYomi from the local fork build and stop clobbering the Caddy edge
Some checks are pending
md-naming / markdown naming convention (push) Waiting to run
Pre-commit checks / pre-commit (push) Waiting to run
Shell tests / shell-tests (push) Waiting to run
Shell tests / arch-tests (push) Waiting to run

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Krzysztof Rudnicki 2026-08-28 14:44:40 +02:00
parent 2554a45c17
commit 924630c029
4 changed files with 120 additions and 11 deletions

View File

@ -0,0 +1,47 @@
{
"intent": "Run the SyncYomi server from kuhy's own fork instead of the published image, so server-side changes reach the live service, and stop the setup script from breaking the shared Caddy edge on every re-run.",
"scope": [
"linux_configuration/features/setup_syncyomi.sh",
"linux_configuration/features/lib/syncyomi_stack.sh",
"linux_configuration/features/lib/syncyomi_expose.sh",
"Non-goal: changing SyncYomi's schema, credentials or the phone's sync settings"
],
"changes": [
"SYNCYOMI_IMAGE defaults to the local build syncyomi-kuhy:local, overridable back to a registry tag; adds SYNCYOMI_SRC and the fork URL.",
"ensure_local_image() builds that image from ~/syncyomi-src (cloning the fork if absent) before the container starts, under DOCKER_BUILDKIT=1 because the Dockerfile reads $BUILDPLATFORM; SYNCYOMI_REBUILD=1 forces a rebuild.",
"configure_caddy() now owns gitea/sites/<subdomain>.caddy instead of appending to the shared Caddyfile, deletes a legacy inline block, and validates the config before reloading.",
"Preflight installs git and docker-buildx alongside the existing packages."
],
"verification": [
{
"command": "./linux_configuration/features/setup_syncyomi.sh setup",
"result": "pass",
"evidence": "Second (idempotent) run: 'Local image syncyomi-kuhy:local already built', 'Caddy snippet already current', caddy validate+reload OK, report printed. The first run caught the bug this fixes: 'ambiguous site definition: kuhy-sync.duckdns.org'."
},
{
"command": "docker ps --filter name=^syncyomi$ + docker logs syncyomi",
"result": "pass",
"evidence": "Image syncyomi-kuhy:local, 'Version: v1.1.14-24-g21c6acd-kuhy', commit 21c6acd."
},
{
"command": "systemctl --user start syncyomi-guard.service",
"result": "pass",
"evidence": "ok: 2163 manga, 69776 chapters, 1 categories, 23 sources (13346096 bytes) - unchanged across the image swap."
},
{
"command": "TachiyomiSY 'Sync library' on the phone, then read docker logs + sync_data row",
"result": "pass",
"evidence": "'Sync data replaced' at 14:42:34; payload length 13346096 before and after; https://kuhy-sync.duckdns.org/api/auth/onboard returns 403 (user exists)."
}
],
"risks": [
"The deployed build is develop (v1.1.14 + dependency bumps), not the v1.1.11 image it replaced; schema stays at version 8 and internal/database is unchanged between those points, so no migration runs.",
"A machine without ~/syncyomi-src now clones the fork on first setup, which needs network and a Docker build (~4 min) instead of an image pull.",
"The Caddy snippet is only rewritten when its content differs, so hand edits to that file are overwritten by design."
],
"rollback": [
"SYNCYOMI_IMAGE=ghcr.io/syncyomi/syncyomi:v1.1.11 ./linux_configuration/features/setup_syncyomi.sh setup",
"Cold database copy: ~/syncyomi_safe_2026-08-28/pre-local-build/cold/syncyomi.db (schema version 8).",
"After rollback, re-run the guard and confirm 2163 manga, then a phone sync round-trip."
]
}

View File

@ -7,16 +7,34 @@
# --- Step 6: Caddy reverse-proxy block (append + reload) ---------------------
configure_caddy() {
if grep -qE "^${SYNCYOMI_SUBDOMAIN} \{" "${CADDYFILE}"; then
log_ok "Caddyfile already has a block for ${SYNCYOMI_SUBDOMAIN}"
local snippet="${CADDY_SITES_DIR}/${SYNCYOMI_SUBDOMAIN}.caddy"
local desired
desired="$(printf '# Managed by setup_syncyomi.sh.\n%s {\n\treverse_proxy 127.0.0.1:%s\n}' \
"${SYNCYOMI_SUBDOMAIN}" "${SYNCYOMI_PORT}")"
grep -qE '^[[:space:]]*import[[:space:]]+/etc/caddy/sites' "${CADDYFILE}" ||
die "Root Caddyfile does not import sites/ — run setup_personal_website.sh first."
# Every service on this edge owns one snippet under sites/. Writing into the
# shared Caddyfile instead is what produced "ambiguous site definition" on
# 2026-08-28: the site was already defined in its snippet, the block was
# appended a second time, and the reload failed while the edge kept serving
# the last config it had accepted — so the damage only showed up later.
ensure_dir "${CADDY_SITES_DIR}"
if [[ -f ${snippet} && "$(cat "${snippet}")" == "${desired}" ]]; then
log_ok "Caddy snippet already current: ${snippet}"
else
log_info "Adding Caddy block for ${SYNCYOMI_SUBDOMAIN}"
# NOTE: appended to the shared Gitea Caddyfile. Re-running setup_gitea.sh
# regenerates that file and drops this block — re-run setup_syncyomi.sh
# to restore it. Caddyfile uses tab indentation.
printf '\n%s {\n\treverse_proxy 127.0.0.1:%s\n}\n' "${SYNCYOMI_SUBDOMAIN}" "${SYNCYOMI_PORT}" \
>>"${CADDYFILE}"
log_info "Writing Caddy snippet ${snippet}"
printf '%s\n' "${desired}" >"${snippet}"
fi
if grep -qE "^${SYNCYOMI_SUBDOMAIN} \{" "${CADDYFILE}"; then
log_info "Removing the legacy inline ${SYNCYOMI_SUBDOMAIN} block from ${CADDYFILE}"
sed -i -E "/^${SYNCYOMI_SUBDOMAIN} \{/,/^\}/d" "${CADDYFILE}"
fi
docker exec "${CADDY_CONTAINER}" caddy validate --config "${CADDYFILE_IN_CONTAINER}" >/dev/null 2>&1 ||
die "Caddy config invalid — not reloading. Check ${CADDY_SITES_DIR}/*.caddy."
docker exec "${CADDY_CONTAINER}" caddy reload --config "${CADDYFILE_IN_CONTAINER}" ||
die "caddy reload failed — check 'docker logs ${CADDY_CONTAINER}'."
log_ok "Caddy reloaded (auto-HTTPS will be issued once the subdomain resolves)"

View File

@ -28,7 +28,41 @@ EOF
log_ok "Wrote ${COMPOSE_FILE}"
}
# Build the local image the compose file asks for. A local tag exists in no
# registry, so `docker compose up` would fail with a pull error rather than an
# explanation -- and a fork whose changes are never rebuilt is worse than no
# fork at all. Builds whatever is checked out in ${SYNCYOMI_SRC}; never moves
# that working tree. Set SYNCYOMI_REBUILD=1 to rebuild after editing it.
ensure_local_image() {
if [[ ${SYNCYOMI_IMAGE} == */* ]]; then
log_info "Using registry image ${SYNCYOMI_IMAGE}"
return 0
fi
if [[ ${SYNCYOMI_REBUILD:-0} != "1" ]] && docker image inspect "${SYNCYOMI_IMAGE}" >/dev/null 2>&1; then
log_ok "Local image ${SYNCYOMI_IMAGE} already built"
return 0
fi
if [[ ! -d ${SYNCYOMI_SRC}/.git ]]; then
log_info "Cloning ${SYNCYOMI_FORK_URL} into ${SYNCYOMI_SRC}"
git clone "${SYNCYOMI_FORK_URL}" "${SYNCYOMI_SRC}"
fi
local version revision
version="$(git -C "${SYNCYOMI_SRC}" describe --tags --always)"
revision="$(git -C "${SYNCYOMI_SRC}" rev-parse HEAD)"
log_info "Building ${SYNCYOMI_IMAGE} from ${SYNCYOMI_SRC} (${version})"
# BuildKit is required: the Dockerfile reads $BUILDPLATFORM, which the
# legacy builder leaves empty and then rejects as an invalid platform.
DOCKER_BUILDKIT=1 docker build \
-t "${SYNCYOMI_IMAGE}" \
--build-arg VERSION="${version}-kuhy" \
--build-arg REVISION="${revision}" \
--build-arg BUILDTIME="$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
"${SYNCYOMI_SRC}"
log_ok "Built ${SYNCYOMI_IMAGE} from ${version}"
}
start_containers() {
ensure_local_image
log_info "Starting SyncYomi container"
docker compose -f "${COMPOSE_FILE}" up -d
}

View File

@ -22,8 +22,15 @@
set -euo pipefail
# --- Pinned SyncYomi image ---------------------------------------------------
readonly SYNCYOMI_IMAGE="ghcr.io/syncyomi/syncyomi:v1.1.11"
# --- SyncYomi image ----------------------------------------------------------
# Runs a local build of the personal fork by default, so server-side changes
# actually reach the service instead of sitting unbuilt in a clone. The
# published image stays one variable away:
# SYNCYOMI_IMAGE=ghcr.io/syncyomi/syncyomi:v1.1.11 ./setup_syncyomi.sh
# Any value containing a "/" is treated as a registry image and pulled as-is.
readonly SYNCYOMI_IMAGE="${SYNCYOMI_IMAGE:-syncyomi-kuhy:local}"
readonly SYNCYOMI_SRC="${SYNCYOMI_SRC:-${HOME}/syncyomi-src}"
readonly SYNCYOMI_FORK_URL="https://github.com/kuhyx/syncyomi.git"
readonly SYNCYOMI_PORT=8282
readonly SYNCYOMI_LOCAL="http://127.0.0.1:${SYNCYOMI_PORT}"
@ -37,6 +44,7 @@ readonly TOKEN_FILE="${DATA_DIR}/.api_token"
# --- Existing host infrastructure (from setup_gitea.sh / install_joplin.sh) --
readonly CADDY_CONTAINER="gitea-caddy"
readonly CADDYFILE="${HOME}/gitea/Caddyfile"
readonly CADDY_SITES_DIR="${HOME}/gitea/sites"
readonly CADDYFILE_IN_CONTAINER="/etc/caddy/Caddyfile"
readonly DUCKDNS_UPDATER="${HOME}/.joplin-server/duckdns-update.sh"
@ -73,7 +81,9 @@ preflight() {
[[ ${EUID} -ne 0 ]] || die "Run as your normal user, not root (it will sudo where needed)."
# Runtime deps; install any that are missing from the official repos.
install_missing_pacman_packages docker docker-compose jq curl openssl
# docker-buildx: the fork's Dockerfile uses $BUILDPLATFORM, which only the
# BuildKit builder defines; the legacy builder rejects the empty value.
install_missing_pacman_packages docker docker-compose jq curl openssl git docker-buildx
# Ensure the docker daemon is enabled+running (auto-start after reboot). Only
# touch systemd via sudo when it is not already active, to stay a no-op on the