From edd1493dc6deb439d8f1496e13642a8cd7ec13f4 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 22 Feb 2026 22:46:26 +0000 Subject: [PATCH 1/2] Fix CI/CD deployment failures Three root causes addressed: 1. Service never started: workflow did not ship moon.service, so systemd had no unit to enable/reload. Now moon.service is included in the SCP upload and installed to /etc/systemd/system/ on every deploy, followed by daemon-reload + enable. 2. Missing environment file: /usr/local/bin/moon-env is required by the service EnvironmentFile directive. If absent systemd refuses to start. The server-side script now creates a placeholder on first run with a clear warning to set GOOGLE_MAPS_API_KEY. 3. Brittle inline sudo commands replaced by /usr/local/bin/deploy-moon: a single root-owned script installed by server-setup.sh. The sudoers entry shrinks to one line; the SSH step becomes a single call to sudo /usr/local/bin/deploy-moon. Also added proper failure output (systemctl status --lines=30) so future errors are visible in logs. Also disabled actions/setup-go cache (cache: false) to fix the intermittent tar exit-code-2 cache restore error. https://claude.ai/code/session_01SvqeKxC2hJbDnDAViSJSDk --- .github/workflows/deploy.yml | 38 +++----------- scripts/server-setup.sh | 98 ++++++++++++++++++++++++++++++------ 2 files changed, 89 insertions(+), 47 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 694936c..4f3e916 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -17,7 +17,7 @@ jobs: uses: actions/setup-go@v5 with: go-version: '1.21' - cache: true + cache: false - name: Download dependencies run: go mod download @@ -38,7 +38,7 @@ jobs: uses: actions/setup-go@v5 with: go-version: '1.21' - cache: true + cache: false - name: Download dependencies run: go mod download @@ -52,8 +52,8 @@ jobs: host: ${{ secrets.DEPLOY_HOST }} username: ${{ secrets.DEPLOY_USER }} key: ${{ secrets.DEPLOY_SSH_KEY }} - port: ${{ secrets.DEPLOY_PORT || 22 }} - source: "moon,index.html,about.html,calendar.html,static/" + port: ${{ secrets.DEPLOY_PORT || '22' }} + source: "moon,index.html,about.html,calendar.html,static/,moon.service" target: "/tmp/moon-deploy" overwrite: true @@ -63,31 +63,5 @@ jobs: host: ${{ secrets.DEPLOY_HOST }} username: ${{ secrets.DEPLOY_USER }} key: ${{ secrets.DEPLOY_SSH_KEY }} - port: ${{ secrets.DEPLOY_PORT || 22 }} - script: | - set -e - - DEPLOY_SRC=/tmp/moon-deploy - - # Install binary - sudo cp "$DEPLOY_SRC/moon" /usr/local/bin/moon - sudo chmod +x /usr/local/bin/moon - - # Update web assets - sudo cp "$DEPLOY_SRC/index.html" /var/www/moon/ - sudo cp "$DEPLOY_SRC/about.html" /var/www/moon/ - sudo cp "$DEPLOY_SRC/calendar.html" /var/www/moon/ - sudo cp -r "$DEPLOY_SRC/static/" /var/www/moon/ - sudo chown -R www-data:www-data /var/www/moon - - # Restart service - sudo systemctl restart moon - - # Verify it came back up - sleep 2 - sudo systemctl is-active moon - - # Clean up - rm -rf "$DEPLOY_SRC" - - echo "Deployment complete" + port: ${{ secrets.DEPLOY_PORT || '22' }} + script: sudo /usr/local/bin/deploy-moon /tmp/moon-deploy diff --git a/scripts/server-setup.sh b/scripts/server-setup.sh index 7415ccf..b2cc2ce 100755 --- a/scripts/server-setup.sh +++ b/scripts/server-setup.sh @@ -53,39 +53,107 @@ chmod 600 "$KEY_DIR/authorized_keys" chown -R "$DEPLOY_USER:$DEPLOY_USER" "$KEY_DIR" # --------------------------------------------------------------- -# 3. Create sudoers entry (least privilege) +# 3. Create the server-side deploy script (run as root via sudo) +# --------------------------------------------------------------- +cat > /usr/local/bin/deploy-moon << 'DEPLOY_SCRIPT' +#!/bin/bash +# /usr/local/bin/deploy-moon +# Runs as root (via sudo) during GitHub Actions deployments. + +set -e + +DEPLOY_SRC="${1:-/tmp/moon-deploy}" + +echo "[deploy] Installing binary..." +cp "$DEPLOY_SRC/moon" /usr/local/bin/moon +chmod +x /usr/local/bin/moon + +echo "[deploy] Installing service file..." +cp "$DEPLOY_SRC/moon.service" /etc/systemd/system/moon.service + +echo "[deploy] Updating web assets..." +mkdir -p /var/www/moon +cp "$DEPLOY_SRC/index.html" /var/www/moon/ +cp "$DEPLOY_SRC/about.html" /var/www/moon/ +cp "$DEPLOY_SRC/calendar.html" /var/www/moon/ +cp -r "$DEPLOY_SRC/static/" /var/www/moon/ +chown -R www-data:www-data /var/www/moon + +# Create environment file if missing — edit it to add your API key +if [ ! -f /usr/local/bin/moon-env ]; then + echo "[deploy] Creating empty environment file at /usr/local/bin/moon-env" + cat > /usr/local/bin/moon-env << 'EOF' +GOOGLE_MAPS_API_KEY=REPLACE_WITH_YOUR_KEY +PROD=True +EOF + echo "[deploy] WARNING: Edit /usr/local/bin/moon-env and set GOOGLE_MAPS_API_KEY" +fi + +echo "[deploy] Reloading systemd and enabling service..." +systemctl daemon-reload +systemctl enable moon + +echo "[deploy] Restarting service..." +systemctl restart moon + +echo "[deploy] Verifying service is active..." +sleep 2 +if ! systemctl is-active --quiet moon; then + echo "[deploy] ERROR: Service failed to start. Status:" + systemctl status moon --no-pager --lines=30 + exit 1 +fi + +echo "[deploy] Cleaning up staging directory..." +rm -rf "$DEPLOY_SRC" + +echo "[deploy] Done — moon is running." +DEPLOY_SCRIPT + +chmod +x /usr/local/bin/deploy-moon +echo "[ok] Created /usr/local/bin/deploy-moon" + +# --------------------------------------------------------------- +# 4. Configure sudoers — only allow the one deploy script # --------------------------------------------------------------- SUDOERS_FILE="/etc/sudoers.d/moon-deploy" cat > "$SUDOERS_FILE" << 'EOF' -# Allow the deploy user to install the moon app without a password -deploy ALL=(ALL) NOPASSWD: \ - /bin/cp /tmp/moon-deploy/moon /usr/local/bin/moon, \ - /bin/chmod +x /usr/local/bin/moon, \ - /bin/cp /tmp/moon-deploy/index.html /var/www/moon/, \ - /bin/cp /tmp/moon-deploy/about.html /var/www/moon/, \ - /bin/cp /tmp/moon-deploy/calendar.html /var/www/moon/, \ - /bin/cp -r /tmp/moon-deploy/static/ /var/www/moon/, \ - /bin/chown -R www-data\:www-data /var/www/moon, \ - /usr/bin/systemctl restart moon, \ - /usr/bin/systemctl is-active moon +# Allow the deploy user to run the moon deployment script as root +deploy ALL=(ALL) NOPASSWD: /usr/local/bin/deploy-moon EOF chmod 440 "$SUDOERS_FILE" -# Validate the file visudo -c -f "$SUDOERS_FILE" echo "[ok] sudoers entry created at $SUDOERS_FILE" # --------------------------------------------------------------- -# 4. Ensure /var/www/moon exists and is owned correctly +# 5. Ensure /var/www/moon exists # --------------------------------------------------------------- mkdir -p /var/www/moon chown -R www-data:www-data /var/www/moon echo "[ok] /var/www/moon ready" # --------------------------------------------------------------- -# 5. Print next steps +# 6. Remind about the environment file # --------------------------------------------------------------- +if [ ! -f /usr/local/bin/moon-env ]; then + cat > /usr/local/bin/moon-env << 'EOF' +GOOGLE_MAPS_API_KEY=REPLACE_WITH_YOUR_KEY +PROD=True +EOF + echo "[ok] Created placeholder /usr/local/bin/moon-env" +fi + +# --------------------------------------------------------------- +# 7. Print next steps +# --------------------------------------------------------------- +echo "" +echo "=== IMPORTANT: Edit the environment file before first deploy ===" +echo "" +echo " sudo nano /usr/local/bin/moon-env" +echo "" +echo " Set GOOGLE_MAPS_API_KEY to your real key." echo "" echo "=== Setup complete. Add these secrets to your GitHub repository: ===" echo "" From d47f1a7d105a6e98de616f84e2fbc74915766a40 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 22 Feb 2026 22:59:36 +0000 Subject: [PATCH 2/2] Fix deploy paths to match actual server configuration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The installed moon.service on the server differs from the repo copy: ExecStart=/var/www/moon/moon (not /usr/local/bin/moon) EnvironmentFile=/var/www/moon/.env (not /usr/local/bin/moon-env) User=james (not www-data) Changes: - deploy.yml: remove moon.service from SCP source — the installed service file is already correct and must not be overwritten - deploy-moon script: install binary to /var/www/moon/moon and web assets to /var/www/moon/; read User/Group dynamically from 'systemctl show moon' so no username is hardcoded - deploy-moon script: do not create or modify .env — it already exists at /var/www/moon/.env with the real API key https://claude.ai/code/session_01SvqeKxC2hJbDnDAViSJSDk --- .github/workflows/deploy.yml | 2 +- scripts/server-setup.sh | 75 +++++++++++------------------------- 2 files changed, 24 insertions(+), 53 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 4f3e916..2d8d5c2 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -53,7 +53,7 @@ jobs: username: ${{ secrets.DEPLOY_USER }} key: ${{ secrets.DEPLOY_SSH_KEY }} port: ${{ secrets.DEPLOY_PORT || '22' }} - source: "moon,index.html,about.html,calendar.html,static/,moon.service" + source: "moon,index.html,about.html,calendar.html,static/" target: "/tmp/moon-deploy" overwrite: true diff --git a/scripts/server-setup.sh b/scripts/server-setup.sh index b2cc2ce..80aa34e 100755 --- a/scripts/server-setup.sh +++ b/scripts/server-setup.sh @@ -53,7 +53,10 @@ chmod 600 "$KEY_DIR/authorized_keys" chown -R "$DEPLOY_USER:$DEPLOY_USER" "$KEY_DIR" # --------------------------------------------------------------- -# 3. Create the server-side deploy script (run as root via sudo) +# 3. Create the server-side deploy script (runs as root via sudo) +# +# Reads User/Group directly from the installed service file so +# this script never needs to hardcode a username. # --------------------------------------------------------------- cat > /usr/local/bin/deploy-moon << 'DEPLOY_SCRIPT' #!/bin/bash @@ -63,35 +66,27 @@ cat > /usr/local/bin/deploy-moon << 'DEPLOY_SCRIPT' set -e DEPLOY_SRC="${1:-/tmp/moon-deploy}" +DEPLOY_DIR=/var/www/moon -echo "[deploy] Installing binary..." -cp "$DEPLOY_SRC/moon" /usr/local/bin/moon -chmod +x /usr/local/bin/moon - -echo "[deploy] Installing service file..." -cp "$DEPLOY_SRC/moon.service" /etc/systemd/system/moon.service +# Read the service owner from the installed unit — no hardcoded username +SERVICE_USER=$(systemctl show moon --property=User --value) +SERVICE_GROUP=$(systemctl show moon --property=Group --value) -echo "[deploy] Updating web assets..." -mkdir -p /var/www/moon -cp "$DEPLOY_SRC/index.html" /var/www/moon/ -cp "$DEPLOY_SRC/about.html" /var/www/moon/ -cp "$DEPLOY_SRC/calendar.html" /var/www/moon/ -cp -r "$DEPLOY_SRC/static/" /var/www/moon/ -chown -R www-data:www-data /var/www/moon - -# Create environment file if missing — edit it to add your API key -if [ ! -f /usr/local/bin/moon-env ]; then - echo "[deploy] Creating empty environment file at /usr/local/bin/moon-env" - cat > /usr/local/bin/moon-env << 'EOF' -GOOGLE_MAPS_API_KEY=REPLACE_WITH_YOUR_KEY -PROD=True -EOF - echo "[deploy] WARNING: Edit /usr/local/bin/moon-env and set GOOGLE_MAPS_API_KEY" +if [ -z "$SERVICE_USER" ]; then + echo "[deploy] ERROR: Could not read User from moon.service" + exit 1 fi -echo "[deploy] Reloading systemd and enabling service..." -systemctl daemon-reload -systemctl enable moon +echo "[deploy] Installing binary to $DEPLOY_DIR/moon (owner: $SERVICE_USER:$SERVICE_GROUP)..." +cp "$DEPLOY_SRC/moon" "$DEPLOY_DIR/moon" +chmod +x "$DEPLOY_DIR/moon" + +echo "[deploy] Updating web assets..." +cp "$DEPLOY_SRC/index.html" "$DEPLOY_DIR/" +cp "$DEPLOY_SRC/about.html" "$DEPLOY_DIR/" +cp "$DEPLOY_SRC/calendar.html" "$DEPLOY_DIR/" +cp -r "$DEPLOY_SRC/static/" "$DEPLOY_DIR/" +chown -R "$SERVICE_USER:$SERVICE_GROUP" "$DEPLOY_DIR" echo "[deploy] Restarting service..." systemctl restart moon @@ -104,7 +99,7 @@ if ! systemctl is-active --quiet moon; then exit 1 fi -echo "[deploy] Cleaning up staging directory..." +echo "[deploy] Cleaning up..." rm -rf "$DEPLOY_SRC" echo "[deploy] Done — moon is running." @@ -128,32 +123,8 @@ visudo -c -f "$SUDOERS_FILE" echo "[ok] sudoers entry created at $SUDOERS_FILE" # --------------------------------------------------------------- -# 5. Ensure /var/www/moon exists -# --------------------------------------------------------------- -mkdir -p /var/www/moon -chown -R www-data:www-data /var/www/moon -echo "[ok] /var/www/moon ready" - -# --------------------------------------------------------------- -# 6. Remind about the environment file -# --------------------------------------------------------------- -if [ ! -f /usr/local/bin/moon-env ]; then - cat > /usr/local/bin/moon-env << 'EOF' -GOOGLE_MAPS_API_KEY=REPLACE_WITH_YOUR_KEY -PROD=True -EOF - echo "[ok] Created placeholder /usr/local/bin/moon-env" -fi - +# 5. Print next steps # --------------------------------------------------------------- -# 7. Print next steps -# --------------------------------------------------------------- -echo "" -echo "=== IMPORTANT: Edit the environment file before first deploy ===" -echo "" -echo " sudo nano /usr/local/bin/moon-env" -echo "" -echo " Set GOOGLE_MAPS_API_KEY to your real key." echo "" echo "=== Setup complete. Add these secrets to your GitHub repository: ===" echo ""