From 2b538b668ec6f345cdf81811d545e2bee0e1e063 Mon Sep 17 00:00:00 2001 From: mhoennig Date: Tue, 1 Sep 2026 17:49:41 +0200 Subject: [PATCH] Step 21 session D: the managed-webspace install path replaces the self-build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit tools/remote reworked to role-named commands (the builder-vs-built tangle noted 2026-09-01): instance-install/instance-update/ instance-start manage the installed Werkator + werkdock, repo-init the watched repository. The self-build is retired — no repo clone for building, no GitHub-key step; the instance installs from locally built artifacts (ADR 0006), the watched repo clones anonymously via https. instance-update refuses to swap the runtime under a running build (FORCE=1 overrides) and keeps the previous runtime as the rollback asset. repo-init is idempotent: checksum-skipped rootfs upload, and the machine-config guard is fixed — its indentation mismatch (grep for two spaces, block written with four) appended the bwrap block on every run, the source of the nine duplicates found on mih34. The retired install/build/start commands fail loudly naming their successors. docs/deployment.md gains the Hostsharing Managed Webspace as the third deployment variant, written from the live-verified setup: both new commands ran against mih34 (full update cycle; fully skipping re-init). Co-Authored-By: Claude Fable 5 --- docs/deployment.md | 28 ++ ...erkdock-extraction-and-webspace-install.md | 4 +- tools/remote | 285 +++++++++++------- 3 files changed, 208 insertions(+), 109 deletions(-) diff --git a/docs/deployment.md b/docs/deployment.md index 20df304..0e824f9 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -261,3 +261,31 @@ All nginx and certificate failures are non-fatal warnings — the plain HTTP ser The nginx container cannot reach `localhost` of the host, so the proxy upstream defaults to `serverName`; set `server.nginx.upstreamHost` if the host is reachable under a different name from inside containers. With the managed nginx, set `server.bindAddress: 0.0.0.0` explicitly (or an address reachable from the Docker network) — the default `127.0.0.1` makes Werkator unreachable for the proxy container. See [configuration.md](configuration.md) for all `server.nginx.*` keys. + +## Hostsharing Managed Webspace + +The third deployment variant (plan step 21, verified live on a real webspace): no root, no Docker daemon, no own reverse proxy. +Werkator runs as a systemd *user* service on the assigned localhost port ("eigener Serverdienst"), the platform's managed Apache terminates TLS and proxies via `.htaccess`, and builds run in the bubblewrap sandbox executed by the [werkdock](../werkdock/README.md) CLI (ADR 0008, step 21 session C). + +Werkator is never built on the webspace: the runtime bundle and the werkdock binary are built locally and uploaded (ADR 0006). +All steps are driven by `tools/remote`, configured through the `.env` file in the repository root; commands name their role — `instance-*` manages the installed Werkator, `repo-*` the repository it watches. + +```bash +tools/remote werkator check-prerequisites # bwrap capability, disk and quota headroom +tools/remote werkator instance-install # upload + unpack the runtime bundle and werkdock +tools/remote werkator repo-init # clone the watched repo, init, rootfs archive, bwrap config +tools/remote werkator instance-start # server config, Apache proxy, systemd user unit +tools/remote port-forward start # browser tunnel while no public domain is set up +``` + +Layout on the host: the watched repository at `$WERKATOR_PATH/werkator/`, the unpacked runtime at `$WERKATOR_PATH/.werkator/werkator/`, the werkdock binary at `$WERKATOR_PATH/.werkator/bin/werkdock`. +The rootfs archive is loaded once per source into werkdock's image store (`~/.werkdock`), shared by every repository of the user. +Fill `git.account`/`git.token` in the machine config when the origin is private, and make the user's services survive logout with `loginctl enable-linger`. + +Updates are one command, refused while a build runs (`FORCE=1` overrides): + +```bash +tools/remote werkator instance-update +``` + +The previous runtime stays as `.werkator/werkator.prev` for one deployment as the rollback asset. diff --git a/docs/plan/21-werkdock-extraction-and-webspace-install.md b/docs/plan/21-werkdock-extraction-and-webspace-install.md index 3ad66b5..b6fae40 100644 --- a/docs/plan/21-werkdock-extraction-and-webspace-install.md +++ b/docs/plan/21-werkdock-extraction-and-webspace-install.md @@ -73,7 +73,7 @@ A docker-like CLI over `bwrap`, filesystem isolation only. - Decided: the rootfs archive becomes a werkdock *image* (`werkator-buildenv-`, checked via `werkdock images`, loaded via `werkdock load`) in werkdock's own store — shared across every repository of the OS user, which resolves step 22's buildenv-sharing question; only the URL download cache and the persistent `/root` toolchain home stay under `.git/werkator/buildenv/`. - Consequence of werkdock's `--clearenv`: the server environment no longer leaks into builds, and the runner's TMPDIR workaround is gone. -### D — The Managed-Webspace install path (this repo, independent of B/C) +### D — The Managed-Webspace install path (this repo, independent of B/C; implemented 2026-09-01 on branch `werkator-consumes-werkdock`) Bring intent 1 to the webspace: build locally, install the bundle — Werkator never builds itself on the target. @@ -94,6 +94,8 @@ Bring intent 1 to the webspace: build locally, install the bundle — Werkator n The image was then trimmed (headless JDK, en/de locales only, no man/doc/apt-lists): 351 MB compressed — smaller than the original JDK-only archive despite carrying Go and Node. All rollback assets on mih34 are removed; the PR for this branch is prepared (PR-doc with `PR#000` placeholder) and will be opened later. - 2026-09-01, session C deployed to mih34: the werkdock binary sits at `.werkator/bin/werkdock`, the machine config names it in `bwrap.werkdock`, the runtime bundle carries the delegating runner, and the TMPDIR workaround left the machine config (obsolete under werkdock's clearenv). +- 2026-09-01, session D done and live-verified on mih34: `tools/remote` reworked to role-named commands (`instance-install`/`instance-update`/`instance-start` for the builder, `repo-init` for the built; the retired `install`/`build`/`start` fail loudly naming their successors); the self-build, the repo clone for it, and the GitHub-key step are gone — the instance installs from locally built artifacts (bundle + werkdock), the watched repo clones anonymously via https. + `instance-update` refuses to swap under a running build, `repo-init` is idempotent (checksum-skipped rootfs upload; the machine-config guard whose indentation mismatch once appended nine duplicate bwrap blocks is fixed); `docs/deployment.md` gained the Managed Webspace as the third variant, written from the verified setup. ## Acceptance Criteria diff --git a/tools/remote b/tools/remote index 016214c..7ce3faf 100755 --- a/tools/remote +++ b/tools/remote @@ -5,63 +5,65 @@ # the second the command. All connection and deployment values come from the # `.env` file in the repository root — never as command line parameters. # -# NOTE: `install` (its clone step) and `build` are a prototype of the webspace -# self-build workflow. They proved the bwrap sandbox, but as a deployment path -# they invert ADR 0006 (build locally, install the bundle) and will be replaced -# by session D of docs/plan/21-werkdock-extraction-and-webspace-install.md. +# Commands name their role (step 21 session D): `instance-*` manages the +# BUILDER — the installed Werkator instance and its werkdock sandbox tool — +# while `repo-*` acts on the BUILT, the repository the instance watches. +# Werkator is never built on the target: the instance is installed from the +# locally built runtime bundle (ADR 0006), and builds of the watched +# repository are the running instance's job (or `bin/werkator build` on the +# host — the werkator CLI, not this script). # # Usage: -# tools/remote werkator check-prerequisites -# tools/remote werkator install -# tools/remote werkator build # WERKATOR_BRANCH to override, default main -# tools/remote werkator start -# tools/remote port-forward start # background tunnel to the Werkator UI -# tools/remote port-forward stop +# tools/remote werkator check-prerequisites bwrap capability + disk/quota on the host +# tools/remote werkator instance-install first-time: upload + unpack bundle and werkdock +# tools/remote werkator instance-update redeploy bundle + werkdock, restart the service +# tools/remote werkator instance-start server config, Apache proxy, systemd unit +# tools/remote werkator repo-init clone the watched repo, init, rootfs, bwrap config # tools/remote werkator control-token +# tools/remote port-forward start background tunnel to the Werkator UI +# tools/remote port-forward stop # # Required in .env: # WERKATOR_REMOTE user@host to operate on, e.g. mih34-werkator@mih34.hostsharing.net # WERKATOR_PATH target directory on that host, e.g. /home/storage/mih34/users/werkator # -# Required for `start`: +# Required for `instance-start`: # WERKATOR_PORT the localhost port assigned by Hostsharing (eigener Serverdienst) # WERKATOR_DOMAIN the domain served by the managed Apache, e.g. ci.example.de # # Required for `port-forward`: # WERKATOR_LOCAL_PORT the local port the browser uses # Optional in .env: -# WERKATOR_BRANCH branch for `build` (default: main) -# WERKATOR_MEMORY_MAX systemd MemoryMax for the unit, e.g. 1G (start) -# WERKATOR_TASKS_MAX systemd TasksMax for the unit, e.g. 512 (start) -# WERKATOR_ROOTFS rootfs archive path -# (default: /build/werkator-buildenv-trixie.tar.zst) +# WERKATOR_REPO_URL https clone URL of the watched repository +# (default: https://github.com/mhoennig/werkator.git) +# WERKATOR_MEMORY_MAX systemd MemoryMax for the unit, e.g. 1G (instance-start) +# WERKATOR_TASKS_MAX systemd TasksMax for the unit, e.g. 512 (instance-start) +# WERKATOR_ROOTFS rootfs archive path for repo-init +# (default: /build/werkator-buildenv-trixie-java-go-node.tar.zst) # # Install layout on the host: -# $WERKATOR_PATH/werkator/ the repository clone -# $WERKATOR_PATH/.werkator/ runtime bundle + rootfs archive -# -# `install` performs, in order: -# 1. check-prerequisites (bwrap capability + disk/quota, aborts on FAIL) -# 2. ensure SSH access (ssh-copy-id on first use; asks for the password) -# 3. upload artifacts (runtime bundle, built locally if missing, + rootfs) -# 4. clone the repository (needs the host SSH key registered at GitHub once — -# the script prints the key and waits) -# 5. `werkator init` + machine-local bwrap configuration +# $WERKATOR_PATH/werkator/ the watched repository (clone) +# $WERKATOR_PATH/.werkator/werkator/ the unpacked runtime bundle +# $WERKATOR_PATH/.werkator/bin/ the werkdock binary +# $WERKATOR_PATH/.werkator/*.tar.* uploaded bundle and rootfs archives # set -euo pipefail +die() { echo "ERROR: $*" >&2; exit 1; } + REPO="${1:-}" COMMAND="${2:-}" REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" PREREQ_SCRIPT="$REPO_ROOT/tools/werkator-build-prerequisites.sh" RUNTIME_BUNDLE="$REPO_ROOT/build/distributions/werkator-runtime-linux-x64.tar.gz" +WERKDOCK_BINARY="$REPO_ROOT/werkdock/dist/werkdock" PID_FILE="/tmp/werkator-port-forward-$(id -u).pid" LOG_FILE="/tmp/werkator-port-forward-$(id -u).log" usage() { - sed -n '3,38p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//' + awk 'NR > 2 && !/^#/ { exit } NR > 2 { sub(/^# ?/, ""); print }' "${BASH_SOURCE[0]}" exit 2 } @@ -87,7 +89,11 @@ set +a require_env WERKATOR_REMOTE WERKATOR_PATH HOST="$WERKATOR_REMOTE" TARGET_DIR="$WERKATOR_PATH" -ROOTFS="${WERKATOR_ROOTFS:-$REPO_ROOT/build/werkator-buildenv-trixie.tar.zst}" +ROOTFS="${WERKATOR_ROOTFS:-$REPO_ROOT/build/werkator-buildenv-trixie-java-go-node.tar.zst}" +REPO_URL="${WERKATOR_REPO_URL:-https://github.com/mhoennig/werkator.git}" +MACHINE_CONFIG="$TARGET_DIR/werkator/.git/werkator/.werkator.yml" +WERKATOR_BIN="$TARGET_DIR/.werkator/werkator/bin/werkator" +UNIT="werkator-werkator.service" ssh_present() { ssh -o BatchMode=yes -o ConnectTimeout=10 "$HOST" true 2>/dev/null @@ -99,7 +105,7 @@ ensure_ssh() { else echo "==> No key-based SSH access yet; running ssh-copy-id (password prompt expected)" ssh-copy-id "$HOST" - ssh_present || { echo "ERROR: SSH access still not working after ssh-copy-id" >&2; exit 1; } + ssh_present || die "SSH access still not working after ssh-copy-id" fi } @@ -109,118 +115,167 @@ check_prerequisites() { echo "==> Checking prerequisites on $HOST (target dir: $TARGET_DIR)" local rootfs_remote="$TARGET_DIR/.werkator/$(basename "$ROOTFS")" if ! ssh "$HOST" "WERKATOR_SSH_TARGET='$HOST' bash -s -- '$TARGET_DIR' '$rootfs_remote'" < "$PREREQ_SCRIPT"; then - echo "ERROR: prerequisites failed on $HOST — install aborted" >&2 - exit 1 + die "prerequisites failed on $HOST — install aborted" fi } -ensure_local_artifacts() { +# The instance artifacts are built locally (ADR 0006): the runtime bundle via +# Gradle, the werkdock binary via the Go toolchain. Both are rebuilt when +# missing, never on the target. +ensure_instance_artifacts() { if [ ! -f "$RUNTIME_BUNDLE" ]; then echo "==> Runtime bundle not found; building it locally (./gradlew runtimeBundle)" (cd "$REPO_ROOT" && ./gradlew runtimeBundle --console=plain -q) fi - [ -f "$RUNTIME_BUNDLE" ] || { echo "ERROR: runtime bundle missing: $RUNTIME_BUNDLE" >&2; exit 1; } - [ -f "$ROOTFS" ] || { - echo "ERROR: rootfs archive missing: $ROOTFS" >&2 - echo " build it with tools/build-bwrap-rootfs.sh or set WERKATOR_ROOTFS" >&2 - exit 1 - } -} - -ensure_github_access() { - # `ssh -T git@github.com` exits 1 even on success ("does not provide shell - # access") — neutralize remotely, then match on the greeting text. - if ssh "$HOST" 'ssh -o BatchMode=yes -o ConnectTimeout=10 -T git@github.com 2>&1 || true' | grep -q "successfully authenticated"; then - echo "==> GitHub SSH access from $HOST: ok" - return 0 + [ -f "$RUNTIME_BUNDLE" ] || die "runtime bundle missing: $RUNTIME_BUNDLE" + if [ ! -f "$WERKDOCK_BINARY" ]; then + echo "==> werkdock binary not found; building it locally (go build)" + (cd "$REPO_ROOT/werkdock" && CGO_ENABLED=0 go build -o dist/werkdock .) fi - echo - echo "==> The host cannot reach GitHub via SSH yet." - echo " Add THIS public key to GitHub (Settings > SSH and GPG keys > New SSH key):" - ssh "$HOST" 'cat ~/.ssh/id_*.pub 2>/dev/null' || { - echo "ERROR: no public key on the host; create one with ssh-keygen -t ed25519" >&2 - exit 1 - } - read -r -p " Press Enter once the key is registered at GitHub... " - ssh "$HOST" 'ssh -o BatchMode=yes -T git@github.com 2>&1 || true' | grep -q "successfully authenticated" || { - echo "ERROR: GitHub authentication from $HOST still failing" >&2 - exit 1 - } - echo "==> GitHub SSH access from $HOST: ok" + [ -f "$WERKDOCK_BINARY" ] || die "werkdock binary missing: $WERKDOCK_BINARY" } -install() { +# Uploads and unpacks the instance artifacts. The previous runtime stays as +# werkator.prev for one deployment as the rollback asset. +deploy_instance() { + echo "==> Uploading runtime bundle and werkdock binary" + ssh "$HOST" "mkdir -p '$TARGET_DIR/.werkator/bin'" + scp -q "$RUNTIME_BUNDLE" "$HOST:$TARGET_DIR/.werkator/" + scp -q "$WERKDOCK_BINARY" "$HOST:$TARGET_DIR/.werkator/bin/werkdock.new" + echo "==> Unpacking" + ssh "$HOST" "set -e + cd '$TARGET_DIR/.werkator' + mv bin/werkdock.new bin/werkdock && chmod 755 bin/werkdock + rm -rf werkator.prev + [ ! -d werkator ] || mv werkator werkator.prev + tar xzf '$(basename "$RUNTIME_BUNDLE")' + './werkator/bin/werkator' --version + './bin/werkdock' version" +} + +instance_install() { ensure_ssh check_prerequisites - ensure_local_artifacts + ensure_instance_artifacts + deploy_instance + echo + echo "==> Instance installed." + echo " Runtime: $WERKATOR_BIN" + echo " werkdock: $TARGET_DIR/.werkator/bin/werkdock" + echo " Next: tools/remote werkator repo-init, then instance-start" +} - echo "==> Uploading runtime bundle and rootfs archive" - ssh "$HOST" "mkdir -p '$TARGET_DIR/.werkator'" - scp -q "$RUNTIME_BUNDLE" "$HOST:$TARGET_DIR/.werkator/" - scp -q "$ROOTFS" "$HOST:$TARGET_DIR/.werkator/" +# Refuse to swap the runtime under a running build; FORCE=1 overrides. +require_idle() { + local port + port="$(ssh "$HOST" "awk '/^server:/{f=1;next} f && /^ port:/{print \$2; exit}' '$MACHINE_CONFIG' 2>/dev/null" || true)" + [ -n "$port" ] || return 0 + local current + current="$(ssh "$HOST" "curl -s --max-time 5 http://127.0.0.1:$port/api/builds/current" || true)" + if [ -n "$current" ] && [ "$current" != "[]" ]; then + [ "${FORCE:-}" = "1" ] || die "a build is running on $HOST — retry when idle, or FORCE=1 to override" + echo "==> WARNING: deploying although a build is running (FORCE=1)" + fi +} - echo "==> Unpacking runtime bundle" - ssh "$HOST" "tar xzf '$TARGET_DIR/.werkator/$(basename "$RUNTIME_BUNDLE")' -C '$TARGET_DIR/.werkator'" - ssh "$HOST" "'$TARGET_DIR/.werkator/werkator/bin/werkator' --version" +instance_update() { + ensure_ssh + ensure_instance_artifacts + require_idle + local was_active=0 + if ssh "$HOST" "XDG_RUNTIME_DIR=/run/user/\$(id -u) systemctl --user is-active --quiet '$UNIT'"; then + was_active=1 + fi + if [ "$was_active" = "1" ]; then + echo "==> Stopping $UNIT" + ssh "$HOST" "XDG_RUNTIME_DIR=/run/user/\$(id -u) systemctl --user stop '$UNIT'" + fi + deploy_instance + if [ "$was_active" = "1" ]; then + echo "==> Starting $UNIT" + ssh "$HOST" "XDG_RUNTIME_DIR=/run/user/\$(id -u) systemctl --user start '$UNIT' && sleep 3 && systemctl --user is-active '$UNIT'" + else + echo "==> Service was not running; not started (use instance-start for the first start)" + fi + echo "==> Instance updated." +} - ensure_github_access +# Sets up the WATCHED repository: an anonymous https clone (a private origin +# gets its credentials via git.account/git.token in the machine config that +# `werkator init` creates), the werkator init, the rootfs archive for the +# sandbox builds, and the machine-local bwrap configuration. +repo_init() { + ensure_ssh + [ -f "$ROOTFS" ] || die "rootfs archive missing: $ROOTFS — build it with tools/build-bwrap-rootfs.sh or set WERKATOR_ROOTFS" + ssh "$HOST" "test -x '$WERKATOR_BIN'" || die "no instance on $HOST — run instance-install first" - echo "==> Cloning the repository" + echo "==> Cloning the watched repository" if ssh "$HOST" "test -d '$TARGET_DIR/werkator/.git'"; then echo " (already cloned, skipping)" else - ssh "$HOST" "git clone git@github.com:mhoennig/werkator.git '$TARGET_DIR/werkator'" + ssh "$HOST" "git clone '$REPO_URL' '$TARGET_DIR/werkator'" fi echo "==> Running werkator init" - ssh "$HOST" "cd '$TARGET_DIR/werkator' && '$TARGET_DIR/.werkator/werkator/bin/werkator' init" + ssh "$HOST" "cd '$TARGET_DIR/werkator' && '$WERKATOR_BIN' init" - echo "==> Writing machine-local bwrap configuration" - ssh "$HOST" "grep -q '^ bwrap:' '$TARGET_DIR/werkator/.git/werkator/.werkator.yml' 2>/dev/null" || ssh "$HOST" "cat >> '$TARGET_DIR/werkator/.git/werkator/.werkator.yml' <<'CFG' + echo "==> Uploading the rootfs archive (skipped when unchanged)" + local rootfs_remote="$TARGET_DIR/.werkator/$(basename "$ROOTFS")" + local local_sha remote_sha + local_sha="$(sha256sum "$ROOTFS" | cut -d' ' -f1)" + remote_sha="$(ssh "$HOST" "sha256sum '$rootfs_remote' 2>/dev/null | cut -d' ' -f1" || true)" + if [ "$local_sha" = "$remote_sha" ]; then + echo " (already on the host, skipping)" + else + scp -q "$ROOTFS" "$HOST:$rootfs_remote" + remote_sha="$(ssh "$HOST" "sha256sum '$rootfs_remote' | cut -d' ' -f1")" + [ "$local_sha" = "$remote_sha" ] || die "rootfs upload checksum mismatch" + fi -# Build in the bubblewrap sandbox instead of natively (Step 17 / ADR 0007). -# Both keys are pinned: read from this machine config even if a branch sets -# its own values in a committed .werkator.yml. + echo "==> Writing the machine-local bwrap configuration" + # NOTE: the guard must match the block's real indentation — a mismatch here + # once appended the block on every run. + if ssh "$HOST" "grep -q '^ bwrap:' '$MACHINE_CONFIG' 2>/dev/null"; then + echo " (bwrap block present, skipping)" + else + ssh "$HOST" "cat >> '$MACHINE_CONFIG' <<'CFG' + +# Build in the bubblewrap sandbox instead of natively (ADR 0008), executed by +# the werkdock CLI (step 21 session C). All three keys are pinned: read from +# this machine config even if a branch sets its own values. builds: default: bwrap: enabled: true - rootfs: $TARGET_DIR/.werkator/$(basename "$ROOTFS") + rootfs: $rootfs_remote + werkdock: $TARGET_DIR/.werkator/bin/werkdock CFG" + fi echo "==> Verifying the effective configuration" - ssh "$HOST" "cd '$TARGET_DIR/werkator' && '$TARGET_DIR/.werkator/werkator/bin/werkator' config:print 2>/dev/null | grep -A3 'bwrap:' | head -4" + ssh "$HOST" "cd '$TARGET_DIR/werkator' && '$WERKATOR_BIN' config:print 2>/dev/null | grep -A4 'bwrap:' | head -5" echo - echo "==> Install complete." - echo " Repo: $TARGET_DIR/werkator" - echo " Runtime: $TARGET_DIR/.werkator/werkator/bin/werkator" - echo " Next: tools/remote werkator build" -} - -build() { - ensure_ssh - local branch="${WERKATOR_BRANCH:-main}" - echo "==> Running one initial build of branch '$branch' on $HOST (in the bwrap sandbox)" - ssh -t "$HOST" "cd '$TARGET_DIR/werkator' && '$TARGET_DIR/.werkator/werkator/bin/werkator' build '$branch'" + echo "==> Repository ready." + echo " Repo: $TARGET_DIR/werkator" + echo " Next: fill git.account/git.token in $MACHINE_CONFIG if the origin is private," + echo " then tools/remote werkator instance-start" } # Start the server as a systemd user unit behind the managed Apache. # WERKATOR_MEMORY_MAX / WERKATOR_TASKS_MAX (optional) are written into the # machine config so `init --systemd` bakes them into the unit. -start() { +instance_start() { ensure_ssh require_env WERKATOR_PORT WERKATOR_DOMAIN - local machine="$TARGET_DIR/werkator/.git/werkator/.werkator.yml" - local unit="werkator-$(basename "$TARGET_DIR/werkator").service" local htaccess="$TARGET_DIR/doms/$WERKATOR_DOMAIN/subs/www/.htaccess" echo "==> Writing server settings to the machine config" - if ssh "$HOST" "grep -q '^server:' '$machine' 2>/dev/null"; then + if ssh "$HOST" "grep -q '^server:' '$MACHINE_CONFIG' 2>/dev/null"; then # re-run: update port and publicBaseUrl in place (systemd limits stay as written) - ssh "$HOST" "sed -i 's/^ port: .*/ port: $WERKATOR_PORT/; s|^ publicBaseUrl: .*| publicBaseUrl: \"https://$WERKATOR_DOMAIN/\"|' '$machine'" + ssh "$HOST" "sed -i 's/^ port: .*/ port: $WERKATOR_PORT/; s|^ publicBaseUrl: .*| publicBaseUrl: \"https://$WERKATOR_DOMAIN/\"|' '$MACHINE_CONFIG'" else - ssh "$HOST" "cat >> '$machine' <<'CFG' + ssh "$HOST" "cat >> '$MACHINE_CONFIG' <<'CFG' # Web access: the managed Apache terminates TLS and proxies to the localhost # port assigned by Hostsharing (eigener Serverdienst); TLS is the domain's @@ -246,18 +301,20 @@ RewriteRule .* http://127.0.0.1:$WERKATOR_PORT%{REQUEST_URI} [proxy] HT" echo "==> Generating the systemd user unit (init --systemd)" - ssh "$HOST" "cd '$TARGET_DIR/werkator' && '$TARGET_DIR/.werkator/werkator/bin/werkator' init --systemd" + ssh "$HOST" "cd '$TARGET_DIR/werkator' && '$WERKATOR_BIN' init --systemd" echo "==> Linking the units into ~/.config/systemd/user and enabling the service" ssh "$HOST" "mkdir -p ~/.config/systemd/user && \ - ln -sf '$TARGET_DIR/werkator/.git/werkator/$unit' ~/.config/systemd/user/ && \ + ln -sf '$TARGET_DIR/werkator/.git/werkator/$UNIT' ~/.config/systemd/user/ && \ ln -sf '$TARGET_DIR/werkator/.git/werkator/werkator-docker-prune.service' ~/.config/systemd/user/ && \ ln -sf '$TARGET_DIR/werkator/.git/werkator/werkator-docker-prune.timer' ~/.config/systemd/user/ && \ - systemctl --user daemon-reload && systemctl --user restart '$unit' && systemctl --user status '$unit' --no-pager -l | head -12" + XDG_RUNTIME_DIR=/run/user/\$(id -u) systemctl --user daemon-reload && \ + XDG_RUNTIME_DIR=/run/user/\$(id -u) systemctl --user restart '$UNIT' && \ + XDG_RUNTIME_DIR=/run/user/\$(id -u) systemctl --user status '$UNIT' --no-pager -l | head -12" echo echo "==> Server started. Verify: https://$WERKATOR_DOMAIN/" - echo " Logs: ssh $HOST -- systemctl --user status '$unit'" + echo " Logs: ssh $HOST -- systemctl --user status '$UNIT'" } # Background SSH tunnel to the Werkator server, so the browser reaches the UI @@ -266,8 +323,8 @@ HT" port_forward() { require_env WERKATOR_LOCAL_PORT local remote_port - remote_port="$(ssh "$HOST" "awk '/^server:/{f=1;next} f && /^ port:/{print \$2; exit}' '$TARGET_DIR/werkator/.git/werkator/.werkator.yml'")" - [ -n "$remote_port" ] || { echo "ERROR: no server.port in the machine config — run 'tools/remote werkator start' first" >&2; exit 1; } + remote_port="$(ssh "$HOST" "awk '/^server:/{f=1;next} f && /^ port:/{print \$2; exit}' '$MACHINE_CONFIG'")" + [ -n "$remote_port" ] || die "no server.port in the machine config — run 'tools/remote werkator instance-start' first" case "$COMMAND" in start) @@ -324,18 +381,30 @@ case "$REPO" in ensure_ssh check_prerequisites ;; - install) - install + instance-install) + instance_install ;; - build) - build + instance-update) + instance_update ;; - start) - start + instance-start) + instance_start + ;; + repo-init) + repo_init ;; control-token) control_token ;; + install) + die "'install' was the self-build prototype; use instance-install + repo-init (step 21 session D)" + ;; + build) + die "'build' (the self-build) is retired; the instance builds pushes itself, or run '$WERKATOR_BIN build ' on the host" + ;; + start) + die "'start' is now 'instance-start' — commands name their role (builder vs built)" + ;; *) echo "ERROR: unknown command: $COMMAND" >&2 usage