Compare commits

..

2 Commits

Author SHA1 Message Date
hsiegeln
60813e44d9 Merge branch 'feat/initial-build' into main
Some checks failed
ci / build-test (push) Successful in 4m1s
deploy / build (push) Successful in 31s
deploy / deploy (push) Failing after 27s
Switch deploy from rsync-over-SSH to lftp mirror over SFTP —
Hetzner Webhosting is SFTP-only and refuses remote exec.
2026-04-24 19:49:54 +02:00
hsiegeln
64aa8f426b ci(deploy): switch from rsync to lftp mirror (SFTP-only hosting)
All checks were successful
ci / build-test (push) Successful in 3m55s
Hetzner Webhosting accepts SSH for file transfer but refuses remote
command exec, failing rsync with:

  exec request failed on channel 0
  rsync error: error in rsync protocol data stream (code 12)

rsync over SSH requires spawning a remote rsync binary, which isn't
possible on SFTP-only tiers. Switch the mirror to lftp, which speaks
SFTP end-to-end. Same semantics (upload + delete removed files), same
key + known_hosts pinning via sftp:connect-program.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 19:49:42 +02:00

View File

@@ -96,27 +96,35 @@ jobs:
chmod 600 ~/.ssh/id_ed25519
printf '%s\n' "$SFTP_KNOWN_HOSTS" > ~/.ssh/known_hosts
chmod 644 ~/.ssh/known_hosts
# Ensure rsync + openssh are present even on a minimal runner image.
if ! command -v rsync >/dev/null 2>&1 || ! command -v ssh >/dev/null 2>&1; then
# Hetzner Webhosting accounts are SFTP-only — they accept SSH for file
# transfer but refuse remote command exec ("exec request failed on
# channel 0"). rsync over SSH needs to spawn a remote rsync binary,
# so it cannot work here. Use lftp's mirror instead, which speaks
# SFTP end-to-end with the same key + known_hosts pinning.
if ! command -v lftp >/dev/null 2>&1 || ! command -v ssh >/dev/null 2>&1; then
if command -v sudo >/dev/null 2>&1; then SUDO=sudo; else SUDO=; fi
$SUDO apt-get update -qq
$SUDO apt-get install -y --no-install-recommends rsync openssh-client
$SUDO apt-get install -y --no-install-recommends lftp openssh-client
fi
- name: Deploy via rsync
- name: Deploy via lftp (mirror over SFTP)
env:
SFTP_USER: ${{ secrets.SFTP_USER }}
SFTP_HOST: ${{ secrets.SFTP_HOST }}
SFTP_PATH: ${{ secrets.SFTP_PATH }}
run: |
# Fail loudly if any secret is missing — otherwise rsync --delete
# Fail loudly if any secret is missing — otherwise mirror --delete
# could be directed at the SSH user's home root.
: "${SFTP_USER:?SFTP_USER secret must be set}"
: "${SFTP_HOST:?SFTP_HOST secret must be set}"
: "${SFTP_PATH:?SFTP_PATH secret must be set}"
rsync -avz --delete \
-e "ssh -i ~/.ssh/id_ed25519 -o StrictHostKeyChecking=yes -o UserKnownHostsFile=~/.ssh/known_hosts" \
dist/ "$SFTP_USER@$SFTP_HOST:$SFTP_PATH/"
lftp <<LFTP
set cmd:fail-exit yes
set sftp:connect-program 'ssh -a -x -i $HOME/.ssh/id_ed25519 -o StrictHostKeyChecking=yes -o UserKnownHostsFile=$HOME/.ssh/known_hosts'
open sftp://$SFTP_USER@$SFTP_HOST
mirror --reverse --delete --verbose --parallel=4 dist/ $SFTP_PATH/
bye
LFTP
- name: Post-deploy smoke test
run: |