From 32240e18f915c6d9febc873f77c1500707b18481 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 10 Aug 2026 03:10:26 +0000 Subject: [PATCH] Fix ensure_coturn_user leaving callers in the wrong directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit install_coturn (services/coturn.sh) cd's into $DOCKER_DIR/coturn and never restores the caller's original working directory. A consumer that chain- installs coturn mid-flow (e.g. asterisk.sh, already cd'd into its own install directory) returned from ensure_coturn_user still sitting in coturn's directory, then went on to write its own docker-compose.yml/.env there instead of its own directory — clobbering coturn's compose file and leaving the consumer's directory without one. The consumer's later `docker compose up --build` then failed with "Dockerfile: no such file or directory", since the Dockerfile was correctly in the consumer's directory but the misplaced compose file (and the build) were not. ensure_coturn_user now saves/restores the caller's cwd around the install_coturn call, fixing this for every consumer (asterisk, mattermost). Also fold cloud-init.sh's contents into a collapsible README section so it's copy-pasteable straight from the repo instead of requiring a separate file download. --- README.md | 101 ++++++++++++++++++++++++++++++++++++++++++++------ lib/common.sh | 15 +++++++- 2 files changed, 104 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 0951f62..3556e22 100644 --- a/README.md +++ b/README.md @@ -33,28 +33,107 @@ the wizard — the USB can be unplugged once setup starts. sudo bash bootstrap.sh --pat ghp_xxxxxxxxxxxxxxxxxxxx ``` Use a fine-grained read-only PAT scoped to just this repo (Contents: Read). +The PAT is stripped from the stored remote URL after cloning. -**Cloud provider install-script / user-data field (IONOS, DigitalOcean, -Hetzner, ...):** these run as root with no terminal attached while the -image is still being provisioned, so `bootstrap.sh`'s interactive hand-off -doesn't apply yet. Use `cloud-init.sh` instead — it's a plain cloud-init -user-data shell script (starts with `#!/bin/bash`, no `#cloud-config` YAML). +
+Cloud provider install-script / user-data field (IONOS, DigitalOcean, Hetzner, ...) + +These run as root with no terminal attached while the image is still being +provisioned, so `bootstrap.sh`'s interactive hand-off doesn't apply yet. +Use `cloud-init.sh` instead — it's a plain cloud-init user-data shell +script (starts with `#!/bin/bash`, no `#cloud-config` YAML). IONOS's server-creation screen has a **User Data** box under "Scripts" with a **Script Type** choice of *Cloud Config* or *Shell Script* — pick **Shell Script**, then either click **Import from file** and select -`cloud-init.sh`, or paste its contents directly. User-data fields run the -script's own content; they don't fetch a URL, so paste/import the file -itself rather than a link to it. (DigitalOcean/Hetzner's plain "User data" -textbox works the same way — paste the script contents in directly.) +`cloud-init.sh`, or paste its contents (below) directly. User-data fields +run the script's own content; they don't fetch a URL, so paste/import the +file itself rather than a link to it. (DigitalOcean/Hetzner's plain "User +data" textbox works the same way — paste the script contents in directly.) It clones the repo in the background during provisioning and installs a one-shot login hook. The provider boots Ubuntu 24.04, this runs unattended, and by the time you SSH in the whiptail service menu is already waiting for you — same experience as `bootstrap.sh`, just already started. Assumes a root login (the default for all three providers above); see the comments in -`cloud-init.sh` if you've provisioned a separate sudo user instead. -The PAT is stripped from the stored remote URL after cloning. +the script if you've provisioned a separate sudo user instead. + +```bash +#!/bin/bash +# cloud-init.sh — payload for a cloud provider's "install script" / user-data +# field (IONOS Cloud Server image deploy, DigitalOcean droplet user-data, +# Hetzner Cloud user-data, etc). The provider runs this as root, unattended, +# with no TTY, while the box is still being provisioned — before you have +# ever logged in. +# +# It deliberately does NOT run the interactive wizard itself (there's no +# terminal for whiptail to talk to yet). Instead it does two things: +# +# 1. Clones this repo to /root/ubuntu-post-install (pulls if already there). +# 2. Installs a one-shot /etc/profile.d hook that launches setup.sh — +# the normal whiptail service menu — the first time you actually log +# in over SSH, then deletes itself so it never fires again. +# +# End result: the provider boots Ubuntu 24.04, this runs in the background, +# and by the time you SSH in the checklist menu is sitting there waiting — +# the same experience as running bootstrap.sh by hand, just already started. +# +# Usage: paste this whole file's contents into the provider's install-script / +# user-data field (or use an "import from file" option if it has one). +# User-data fields run the content you give them directly — they don't fetch +# a URL — so paste the script itself, not a link to it. +# +# Assumes the provider logs you in as root (the default for IONOS Cloud +# Server, DigitalOcean droplets, and Hetzner Cloud server images). If you've +# provisioned a separate non-root sudo user instead, the hook won't reach +# you automatically — SSH in and run: +# sudo bash /root/ubuntu-post-install/setup.sh +set -euo pipefail + +if [ "$(id -u)" -ne 0 ]; then + echo "cloud-init.sh must run as root — that's how provider install-script hooks already run it." >&2 + exit 1 +fi + +REPO_URL="https://github.com/outis1one/ubuntu-post-install.git" +DEST="/root/ubuntu-post-install" +MARKER="/root/.ubuntu-post-install-pending" +HOOK="/etc/profile.d/99-ubuntu-post-install.sh" +export DEBIAN_FRONTEND=noninteractive + +command -v git >/dev/null 2>&1 || { apt-get update -qq && apt-get install -y git; } + +if [ -d "$DEST/.git" ]; then + git -C "$DEST" pull --ff-only || true +else + git clone "$REPO_URL" "$DEST" +fi + +touch "$MARKER" + +# POSIX sh, not bash — /etc/profile.d/*.sh gets sourced by whatever shell +# the login uses, not necessarily bash. +cat > "$HOOK" << 'EOF' +# Installed by cloud-init.sh — launches the ubuntu-post-install wizard on +# the first interactive login, then removes itself so it never fires again. +MARKER="/root/.ubuntu-post-install-pending" +HOOK="/etc/profile.d/99-ubuntu-post-install.sh" +DEST="/root/ubuntu-post-install" + +if [ -f "$MARKER" ] && [ -t 0 ] && [ "$(id -u)" -eq 0 ] && [ -f "$DEST/setup.sh" ]; then + rm -f "$MARKER" "$HOOK" + echo "" + echo "ubuntu-post-install: launching the setup wizard..." + echo "" + bash "$DEST/setup.sh" +fi +EOF +chmod 644 "$HOOK" + +echo "cloud-init.sh: repo cloned to $DEST — the setup wizard will launch on first login." +``` + +
## Usage diff --git a/lib/common.sh b/lib/common.sh index 1d96289..34aa245 100644 --- a/lib/common.sh +++ b/lib/common.sh @@ -810,7 +810,20 @@ ensure_coturn_user() { if [ ! -d "$DOCKER_DIR/coturn" ]; then if declare -F install_coturn >/dev/null 2>&1; then log_info "No shared coturn (TURN/STUN) server yet — setting one up for $_consumer..." - install_coturn || { log_warning "coturn setup failed — $_consumer will run without TURN."; return 1; } + # install_coturn cd's into $DOCKER_DIR/coturn and never cd's back — + # the caller (e.g. asterisk.sh, already cd'd into its own install + # directory) would otherwise return here with the wrong cwd and go + # on to write ITS docker-compose.yml/.env into coturn's directory + # instead of its own. Confirmed live: this clobbered coturn's + # compose file and left the consumer's own directory without one, + # so its later `docker compose up --build` failed with "Dockerfile: + # no such file or directory" (no Dockerfile in coturn's directory). + local _caller_pwd + _caller_pwd="$(pwd)" + install_coturn + local _coturn_rc=$? + cd "$_caller_pwd" || true + [ "$_coturn_rc" -ne 0 ] && { log_warning "coturn setup failed — $_consumer will run without TURN."; return 1; } else log_warning "services/coturn.sh not loaded — $_consumer will run without TURN." log_warning "Run: sudo ./setup.sh coturn"