fix(P04): deploy.sh — UAT branch lookup strips patch segment; SSH-key auth preferred
---ci--- phase: 4 milestone: v0.1 status: execute persona: ci-lead-developer task: T-4.2 requirements: covered: [REQ-52] partial: [] ---/ci--- UAT variant now resolves the milestone branch by milestone number (v0.1) not full tag version (v0.1.4), matching branch-strategy labeling. DEPLOY_KEY (path in .env.secrets) preferred over passwords — survives sshd PermitRootLogin prohibit-password (Ubuntu 24.04 default that rejects root password auth; observed on the LXC host). Deployed v0.1.4-uat for manual UAT: http://10.0.0.114:3000.
This commit is contained in:
+40
-22
@@ -44,31 +44,41 @@ command -v ssh >/dev/null || die "ssh not found" 2
|
||||
DEPLOY_HOST=$(grep -E "^DEPLOY_HOST=" "$SECRETS_FILE" | cut -d= -f2- | tr -d '\r')
|
||||
DEPLOY_USER=$(grep -E "^DEPLOY_USER=" "$SECRETS_FILE" | cut -d= -f2- | tr -d '\r')
|
||||
DEPLOY_PASSWORD=$(grep -E "^DEPLOY_PASSWORD=" "$SECRETS_FILE" | cut -d= -f2- | tr -d '\r')
|
||||
[ -n "$DEPLOY_HOST" ] && [ -n "$DEPLOY_USER" ] && [ -n "$DEPLOY_PASSWORD" ] \
|
||||
|| die "DEPLOY_HOST/DEPLOY_USER/DEPLOY_PASSWORD not set in $SECRETS_FILE" 2
|
||||
DEPLOY_KEY=$(grep -E "^DEPLOY_KEY=" "$SECRETS_FILE" | cut -d= -f2- | tr -d '\r')
|
||||
[ -n "$DEPLOY_HOST" ] && [ -n "$DEPLOY_USER" ] \
|
||||
|| die "DEPLOY_HOST/DEPLOY_USER not set in $SECRETS_FILE" 2
|
||||
|
||||
# Password auth transport + file-sync transport.
|
||||
# sshpass when available; otherwise OpenSSH's native SSH_ASKPASS
|
||||
# (no extra binary). rsync when available; otherwise tar-over-ssh.
|
||||
# Auth: an SSH key (DEPLOY_KEY=path in .env.secrets) is preferred —
|
||||
# it survives sshd 'PermitRootLogin prohibit-password' policies that
|
||||
# reject root passwords. Password auth (DEPLOY_PASSWORD) is fallback.
|
||||
SSH_AUTH_DIR=$(mktemp -d)
|
||||
trap 'rm -rf "$SSH_AUTH_DIR"' EXIT
|
||||
if command -v sshpass >/dev/null; then
|
||||
SSH_TRANSPORT="sshpass"
|
||||
SSH() { sshpass -p "$DEPLOY_PASSWORD" ssh -o StrictHostKeyChecking=accept-new "$DEPLOY_USER@$DEPLOY_HOST" "$@"; }
|
||||
|
||||
if [ -n "$DEPLOY_KEY" ] && [ -f "$DEPLOY_KEY" ]; then
|
||||
SSH_TRANSPORT="ssh-key:${DEPLOY_KEY##*/}"
|
||||
SSH() { ssh -i "$DEPLOY_KEY" -o IdentitiesOnly=yes \
|
||||
-o StrictHostKeyChecking=accept-new "$DEPLOY_USER@$DEPLOY_HOST" "$@"; }
|
||||
else
|
||||
SSH_TRANSPORT="ssh-askpass"
|
||||
cat > "$SSH_AUTH_DIR/askpass.sh" <<ASKPASS
|
||||
[ -n "$DEPLOY_PASSWORD" ] \
|
||||
|| die "neither DEPLOY_KEY (existing file) nor DEPLOY_PASSWORD set in $SECRETS_FILE" 2
|
||||
if command -v sshpass >/dev/null; then
|
||||
SSH_TRANSPORT="sshpass"
|
||||
SSH() { sshpass -p "$DEPLOY_PASSWORD" ssh -o StrictHostKeyChecking=accept-new "$DEPLOY_USER@$DEPLOY_HOST" "$@"; }
|
||||
else
|
||||
SSH_TRANSPORT="ssh-askpass"
|
||||
cat > "$SSH_AUTH_DIR/askpass.sh" <<ASKPASS
|
||||
#!/usr/bin/env bash
|
||||
printf '%s\n' "\$DEPLOY_PASSWORD"
|
||||
ASKPASS
|
||||
chmod 700 "$SSH_AUTH_DIR/askpass.sh"
|
||||
SSH() {
|
||||
DEPLOY_PASSWORD="$DEPLOY_PASSWORD" SSH_ASKPASS="$SSH_AUTH_DIR/askpass.sh" \
|
||||
SSH_ASKPASS_REQUIRE=force DISPLAY=:0 \
|
||||
ssh -o StrictHostKeyChecking=accept-new "$DEPLOY_USER@$DEPLOY_HOST" "$@"
|
||||
}
|
||||
chmod 700 "$SSH_AUTH_DIR/askpass.sh"
|
||||
SSH() {
|
||||
DEPLOY_PASSWORD="$DEPLOY_PASSWORD" SSH_ASKPASS="$SSH_AUTH_DIR/askpass.sh" \
|
||||
SSH_ASKPASS_REQUIRE=force DISPLAY=:0 \
|
||||
ssh -o StrictHostKeyChecking=accept-new "$DEPLOY_USER@$DEPLOY_HOST" "$@"
|
||||
}
|
||||
fi
|
||||
fi
|
||||
log "password transport: $SSH_TRANSPORT"
|
||||
log "ssh transport: $SSH_TRANSPORT"
|
||||
|
||||
if command -v rsync >/dev/null; then
|
||||
SYNC_TRANSPORT="rsync"
|
||||
@@ -77,11 +87,16 @@ else
|
||||
fi
|
||||
|
||||
# Sync the build context to the host (excludes live in the Dockerfile's
|
||||
# .dockerignore; we only prune heavy dirs here).
|
||||
# .dockerignore; we only prune heavy dirs here). rsync uses the SSH
|
||||
# function's transport implicitly via -e; tar-over-ssh uses SSH directly.
|
||||
sync_source() {
|
||||
local src="$1" dst="$2"
|
||||
if [ "$SYNC_TRANSPORT" = "rsync" ]; then
|
||||
if [ "$SSH_TRANSPORT" = "sshpass" ]; then
|
||||
if [[ "$SSH_TRANSPORT" == ssh-key:* ]]; then
|
||||
rsync -az --delete --exclude node_modules --exclude .next --exclude .git \
|
||||
-e "ssh -i '$DEPLOY_KEY' -o IdentitiesOnly=yes -o StrictHostKeyChecking=accept-new" \
|
||||
"$src" "$DEPLOY_USER@$DEPLOY_HOST:$dst/"
|
||||
elif [ "$SSH_TRANSPORT" = "sshpass" ]; then
|
||||
sshpass -p "$DEPLOY_PASSWORD" rsync -az --delete \
|
||||
--exclude node_modules --exclude .next --exclude .git \
|
||||
-e "ssh -o StrictHostKeyChecking=accept-new" "$src" "$DEPLOY_USER@$DEPLOY_HOST:$dst/"
|
||||
@@ -117,9 +132,12 @@ git -C "$REPO_ROOT" rev-parse -q --verify "refs/tags/$BASE_VERSION" >/dev/null \
|
||||
|| die "tag $BASE_VERSION not found locally or on origin" 2
|
||||
|
||||
if [ "$UAT_BUILD" = true ]; then
|
||||
# Deploy the milestone branch HEAD carrying this tag (review staging)
|
||||
MILESTONE_BRANCH=$(git -C "$REPO_ROOT" branch -r --list "origin/milestone/${BASE_VERSION#v}*" | head -1 | tr -d ' ')
|
||||
[ -n "$MILESTONE_BRANCH" ] || die "no milestone branch found for $BASE_VERSION" 2
|
||||
# Deploy the milestone branch HEAD carrying this tag (review staging).
|
||||
# Branch labels use the MILESTONE number (v0.1), tags the full patch
|
||||
# version (v0.1.4) — strip the patch segment for the branch lookup.
|
||||
MILESTONE_VER="${BASE_VERSION%.*}" # v0.1.4 -> v0.1
|
||||
MILESTONE_BRANCH=$(git -C "$REPO_ROOT" branch -r --list "origin/milestone/${MILESTONE_VER}-*" | head -1 | tr -d ' ')
|
||||
[ -n "$MILESTONE_BRANCH" ] || die "no milestone branch found for $BASE_VERSION (looked for origin/milestone/${MILESTONE_VER}-*)" 2
|
||||
TAG_COMMIT=$(git -C "$REPO_ROOT" rev-parse "${MILESTONE_BRANCH#origin/}" 2>/dev/null || git -C "$REPO_ROOT" rev-parse "$MILESTONE_BRANCH")
|
||||
log "UAT build: milestone HEAD $TAG_COMMIT (tag $BASE_VERSION + review infra)"
|
||||
else
|
||||
|
||||
Reference in New Issue
Block a user