diff --git a/README.md b/README.md index bceea96..7979d6b 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,29 @@ It can be invoked from any directory: by default it resolves the project root from the script's own location. Override with `--project-dir PATH` or the `PROJECT_DIR` environment variable when needed. +### SSH config aliases + +`--remote` accepts either an explicit `user@host[:port]` or a bare alias that +is resolved from `~/.ssh/config`. The matching `Host` block supplies +`HostName`, `User`, `Port` and `IdentityFile` automatically: + +```text +# ~/.ssh/config +Host prod + HostName prod.example.com + User deploy + Port 22 + IdentityFile ~/.ssh/deploy_ed25519 +``` + +```shell script +./scripts/build-and-push.sh --remote prod +# Resolves to deploy@prod.example.com:22 using ~/.ssh/deploy_ed25519 +``` + +`--ssh-key` always overrides the config `IdentityFile`. Use `--ssh-config PATH` +to point at a non-default config file. + ## Running the application in dev mode You can run your application in dev mode that enables live coding using: diff --git a/scripts/build-and-push.sh b/scripts/build-and-push.sh index af01115..f380407 100755 --- a/scripts/build-and-push.sh +++ b/scripts/build-and-push.sh @@ -59,7 +59,9 @@ Build the native Quarkus image and push it to a remote Docker host using unregistry (docker-pussh). Required: - --remote USER@HOST[:PORT] SSH destination (e.g. deploy@prod.example.com) + --remote TARGET SSH destination. Either 'user@host[:port]' + or a bare alias resolved from ~/.ssh/config + (e.g. 'prod'). See SSH config aliases below. Image identity (defaults read from pom.xml): --name NAME Image repository name @@ -74,8 +76,8 @@ Build options: (platform is fixed to ${DEFAULT_PLATFORM}) SSH options forwarded to docker pussh: - --ssh-key PATH Private key file - --ssh-config PATH SSH config file + --ssh-key PATH Private key file (overrides SSH config) + --ssh-config PATH SSH config file (default: ~/.ssh/config) Post-push: --no-verify Skip the ssh/docker images verification step @@ -83,8 +85,15 @@ Post-push: Other: -h, --help Show this help +SSH config aliases: + When --remote is a bare alias (no '@'), it is resolved from the SSH + config: HostName, User, Port and IdentityFile are pulled from the + matching Host block, with sensible fallbacks. --ssh-key always wins + over the config IdentityFile. + Examples: $(basename "$0") --remote deploy@prod.example.com + $(basename "$0") --remote prod # uses 'Host prod' in ~/.ssh/config $(basename "$0") --remote ubuntu@1.2.3.4 --ssh-key ~/.ssh/deploy_ed25519 $(basename "$0") --remote deploy@prod --skip-build --tag 2.0.0 /absolute/path/to/test-quarkus/scripts/$(basename "$0") --remote deploy@prod @@ -109,6 +118,57 @@ require() { command -v "$1" >/dev/null 2>&1 || die "Required command '$1' not found in PATH." } +resolve_ssh_alias() { + local alias="$1" + local config="${SSH_CONFIG:-$HOME/.ssh/config}" + + if [[ "$alias" == *@* ]]; then + printf '%s|%s\n' "$alias" "" + return 0 + fi + + [[ -f "$config" ]] \ + || die "Cannot resolve '$alias': $config not found and value has no '@'." + + local in_block=0 hostname="" user="" port="" identity="" line key value p + local -a patterns + while IFS= read -r line || [[ -n "$line" ]]; do + line="${line#"${line%%[![:space:]]*}"}" + [[ -z "$line" || "${line:0:1}" == "#" ]] && continue + key="${line%% *}" + if [[ "$key" == "Host" ]]; then + if (( in_block )); then break; fi + read -r -a patterns <<< "${line#Host }" + in_block=0 + for p in "${patterns[@]}"; do + if [[ "$p" == "$alias" ]]; then + in_block=1 + hostname=""; user=""; port=""; identity="" + break + fi + done + continue + fi + if (( in_block )); then + case "$key" in + HostName) hostname="${line#* }" ;; + User) user="${line#* }" ;; + Port) port="${line#* }" ;; + IdentityFile) identity="${line#* }" ;; + esac + fi + done < "$config" + + (( in_block )) \ + || die "No matching Host entry in $config for alias '$alias'. Use user@host[:port] or check ~/.ssh/config." + [[ -z "$hostname" ]] && die "Host '$alias' has no HostName in $config." + [[ -z "$user" ]] && user="$(whoami)" + [[ -z "$port" ]] && port="22" + identity="${identity/#\~/$HOME}" + + printf '%s@%s:%s|%s\n' "$user" "$hostname" "$port" "$identity" +} + # ---------- arg parsing ---------- while [[ $# -gt 0 ]]; do case "$1" in @@ -138,11 +198,23 @@ PROJECT_DIR="$(pwd)" IMAGE_REF="${IMAGE_NAME}:${IMAGE_TAG}" +# ---------- resolve SSH target ---------- +resolved=$(resolve_ssh_alias "$REMOTE") +REMOTE_TARGET="${resolved%|*}" +REMOTE_IDENTITY="${resolved##*|}" +if [[ "$REMOTE" != *@* ]]; then + log "Resolved alias '${REMOTE}' → ${REMOTE_TARGET}" +fi +if [[ -z "$SSH_KEY" && -n "$REMOTE_IDENTITY" ]]; then + SSH_KEY="$REMOTE_IDENTITY" + log "Using IdentityFile from SSH config: ${SSH_KEY}" +fi + # ---------- pre-flight ---------- log "Image: ${IMAGE_REF}" log "Platform: ${DEFAULT_PLATFORM}" log "Dockerfile: ${DOCKERFILE}" -log "Remote: ${REMOTE}" +log "Remote: ${REMOTE_TARGET}" require docker docker info >/dev/null 2>&1 \ @@ -181,9 +253,9 @@ else fi # ---------- pussh ---------- -log "Pusshing ${IMAGE_REF} to ${REMOTE} via unregistry…" +log "Pusshing ${IMAGE_REF} to ${REMOTE_TARGET} via unregistry…" -pussh_args=( "$REMOTE" ) +pussh_args=( "$REMOTE_TARGET" ) [[ -n "$SSH_KEY" ]] && pussh_args+=( -i "$SSH_KEY" ) [[ -n "$SSH_CONFIG" ]] && pussh_args+=( -F "$SSH_CONFIG" ) @@ -194,13 +266,13 @@ ok "Pussh complete." if [[ "$SKIP_VERIFY" -eq 1 ]]; then warn "Skipping post-push verification (--no-verify)." else - log "Verifying ${IMAGE_REF} on ${REMOTE}…" - ssh_args=( "$REMOTE" ) + log "Verifying ${IMAGE_REF} on ${REMOTE_TARGET}…" + ssh_args=( "$REMOTE_TARGET" ) [[ -n "$SSH_KEY" ]] && ssh_args=( -i "$SSH_KEY" "${ssh_args[@]}" ) [[ -n "$SSH_CONFIG" ]] && ssh_args=( -F "$SSH_CONFIG" "${ssh_args[@]}" ) if ! ssh "${ssh_args[@]}" "docker images --format '{{.Repository}}:{{.Tag}} {{.Size}}' | grep -E '^${IMAGE_NAME}:${IMAGE_TAG} '"; then - die "Image ${IMAGE_REF} not found on ${REMOTE} after pussh." + die "Image ${IMAGE_REF} not found on ${REMOTE_TARGET} after pussh." fi ok "Image present on remote." fi @@ -208,4 +280,4 @@ fi # ---------- next step hint ---------- printf '\n%s→ To run on the remote:%s\n' "$C_OK" "$C_RESET" printf ' ssh %s "docker run -d --name %s --restart unless-stopped -p 8080:8080 %s"\n\n' \ - "$REMOTE" "$IMAGE_NAME" "$IMAGE_REF" + "$REMOTE_TARGET" "$IMAGE_NAME" "$IMAGE_REF"