build: add native build and unregistry push script
scripts/build-and-push.sh builds the native image with the Mandrel Dockerfile and pushes it to a remote Docker host via docker-pussh (unregistry), transferring only missing layers over SSH. - Name and tag default to pom.xml artifactId/version (overridable) - Builds for linux/amd64; --skip-build to pussh only - Verifies image presence on the remote after push and prints a ready-to-use docker run hint README updated with a 'Building and deploying natively' section.
This commit is contained in:
@@ -4,6 +4,20 @@ This project uses Quarkus, the Supersonic Subatomic Java Framework.
|
||||
|
||||
If you want to learn more about Quarkus, please visit its website: https://quarkus.io/ .
|
||||
|
||||
## Building and deploying natively
|
||||
|
||||
Build the native image with the Mandrel-based multi-stage Dockerfile and push it
|
||||
directly to a remote Docker host via [unregistry](https://github.com/psviderski/unregistry)
|
||||
(no external registry required; only missing layers are transferred over SSH):
|
||||
|
||||
```shell script
|
||||
./scripts/build-and-push.sh --remote deploy@prod.example.com
|
||||
```
|
||||
|
||||
The script reads `artifactId` and `version` from `pom.xml` for the image name
|
||||
and tag, builds for `linux/amd64`, and verifies the image is present on the
|
||||
remote after pushing. See `./scripts/build-and-push.sh --help` for all flags.
|
||||
|
||||
## Running the application in dev mode
|
||||
|
||||
You can run your application in dev mode that enables live coding using:
|
||||
|
||||
Executable
+196
@@ -0,0 +1,196 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Build the native Quarkus image and push it to a remote Docker host
|
||||
# via docker-pussh (unregistry): https://github.com/psviderski/unregistry
|
||||
#
|
||||
# - Image name/tag default to pom.xml properties (quarkus.application.*)
|
||||
# - Builds for linux/amd64 by default (cross-compile from arm64 hosts)
|
||||
# - Transfers only missing layers over SSH (no external registry required)
|
||||
|
||||
set -euo pipefail
|
||||
IFS=$'\n\t'
|
||||
|
||||
readonly DOCKERFILE="src/main/docker/Dockerfile"
|
||||
readonly DEFAULT_PLATFORM="linux/amd64"
|
||||
readonly UNREGISTRY_PLUGIN="https://raw.githubusercontent.com/psviderski/unregistry/main/docker-pussh"
|
||||
readonly PLUGIN_DIR="${HOME}/.docker/cli-plugins"
|
||||
readonly PLUGIN_PATH="${PLUGIN_DIR}/docker-pussh"
|
||||
|
||||
REMOTE=""
|
||||
IMAGE_NAME=""
|
||||
IMAGE_TAG=""
|
||||
SSH_KEY=""
|
||||
SSH_CONFIG=""
|
||||
SKIP_BUILD=0
|
||||
SKIP_VERIFY=0
|
||||
|
||||
# ---------- logging ----------
|
||||
if [[ -t 1 ]]; then
|
||||
readonly C_INFO=$'\033[0;36m'
|
||||
readonly C_OK=$'\033[0;32m'
|
||||
readonly C_WARN=$'\033[0;33m'
|
||||
readonly C_ERR=$'\033[0;31m'
|
||||
readonly C_RESET=$'\033[0m'
|
||||
else
|
||||
readonly C_INFO="" C_OK="" C_WARN="" C_ERR="" C_RESET=""
|
||||
fi
|
||||
|
||||
log() { printf '%s[INFO]%s %s\n' "$C_INFO" "$C_RESET" "$*"; }
|
||||
ok() { printf '%s[OK]%s %s\n' "$C_OK" "$C_RESET" "$*"; }
|
||||
warn() { printf '%s[WARN]%s %s\n' "$C_WARN" "$C_RESET" "$*" >&2; }
|
||||
err() { printf '%s[ERROR]%s %s\n' "$C_ERR" "$C_RESET" "$*" >&2; }
|
||||
die() { err "$*"; exit 1; }
|
||||
|
||||
on_error() {
|
||||
local exit_code=$?
|
||||
err "Script aborted on line ${BASH_LINENO[0]} (exit ${exit_code})."
|
||||
exit "$exit_code"
|
||||
}
|
||||
trap on_error ERR
|
||||
|
||||
# ---------- helpers ----------
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Usage: $(basename "$0") --remote USER@HOST[:PORT] [options]
|
||||
|
||||
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)
|
||||
|
||||
Image identity (defaults read from pom.xml):
|
||||
--name NAME Image repository name
|
||||
--tag TAG Image tag
|
||||
|
||||
Build options:
|
||||
--skip-build Skip docker build, only pussh an existing image
|
||||
(platform is fixed to ${DEFAULT_PLATFORM})
|
||||
|
||||
SSH options forwarded to docker pussh:
|
||||
--ssh-key PATH Private key file
|
||||
--ssh-config PATH SSH config file
|
||||
|
||||
Post-push:
|
||||
--no-verify Skip the ssh/docker images verification step
|
||||
|
||||
Other:
|
||||
-h, --help Show this help
|
||||
|
||||
Examples:
|
||||
$(basename "$0") --remote deploy@prod.example.com
|
||||
$(basename "$0") --remote ubuntu@1.2.3.4 --ssh-key ~/.ssh/deploy_ed25519
|
||||
$(basename "$0") --remote deploy@prod --skip-build --tag 2.0.0
|
||||
EOF
|
||||
}
|
||||
|
||||
read_pom() {
|
||||
local xpath="$1" fallback="$2"
|
||||
local value=""
|
||||
if command -v xmllint >/dev/null 2>&1; then
|
||||
value=$(xmllint --xpath "string($xpath)" pom.xml 2>/dev/null || true)
|
||||
fi
|
||||
if [[ -z "$value" ]]; then
|
||||
value=$(grep -E "<artifactId>|<version>" pom.xml | head -2 \
|
||||
| sed -E 's:.*<(artifactId|version)>(.*)</\1>.*:\2:')
|
||||
fi
|
||||
printf '%s' "${value:-$fallback}"
|
||||
}
|
||||
|
||||
require() {
|
||||
command -v "$1" >/dev/null 2>&1 || die "Required command '$1' not found in PATH."
|
||||
}
|
||||
|
||||
# ---------- arg parsing ----------
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--remote) REMOTE="$2"; shift 2 ;;
|
||||
--name) IMAGE_NAME="$2"; shift 2 ;;
|
||||
--tag) IMAGE_TAG="$2"; shift 2 ;;
|
||||
--ssh-key) SSH_KEY="$2"; shift 2 ;;
|
||||
--ssh-config) SSH_CONFIG="$2"; shift 2 ;;
|
||||
--skip-build) SKIP_BUILD=1; shift ;;
|
||||
--no-verify) SKIP_VERIFY=1; shift ;;
|
||||
-h|--help) usage; exit 0 ;;
|
||||
*) die "Unknown argument: $1 (use --help)" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# ---------- defaults ----------
|
||||
[[ -z "$IMAGE_NAME" ]] && IMAGE_NAME=$(read_pom "/*[local-name()='project']/*[local-name()='artifactId']" "test-quarkus")
|
||||
[[ -z "$IMAGE_TAG" ]] && IMAGE_TAG=$(read_pom "/*[local-name()='project']/*[local-name()='version']" "1.0.0-SNAPSHOT")
|
||||
[[ -z "$REMOTE" ]] && die "Missing --remote USER@HOST[:PORT] (use --help)."
|
||||
|
||||
IMAGE_REF="${IMAGE_NAME}:${IMAGE_TAG}"
|
||||
|
||||
# ---------- pre-flight ----------
|
||||
log "Image: ${IMAGE_REF}"
|
||||
log "Platform: ${DEFAULT_PLATFORM}"
|
||||
log "Dockerfile: ${DOCKERFILE}"
|
||||
log "Remote: ${REMOTE}"
|
||||
|
||||
require docker
|
||||
docker info >/dev/null 2>&1 \
|
||||
|| die "Docker daemon is not reachable. Start Docker Desktop (or your daemon) and retry."
|
||||
|
||||
if ! docker pussh --help >/dev/null 2>&1; then
|
||||
cat >&2 <<EOF
|
||||
|
||||
'docker pussh' plugin not found. Install it with:
|
||||
|
||||
mkdir -p ${PLUGIN_DIR}
|
||||
curl -fsSL ${UNREGISTRY_PLUGIN} -o ${PLUGIN_PATH}
|
||||
chmod +x ${PLUGIN_PATH}
|
||||
|
||||
Then rerun this script.
|
||||
EOF
|
||||
exit 1
|
||||
fi
|
||||
|
||||
[[ -f "$DOCKERFILE" ]] || die "Dockerfile not found at ${DOCKERFILE}."
|
||||
[[ -f pom.xml ]] || die "pom.xml not found in current directory."
|
||||
|
||||
# ---------- build ----------
|
||||
if [[ "$SKIP_BUILD" -eq 1 ]]; then
|
||||
warn "Skipping build (--skip-build). Assuming ${IMAGE_REF} exists locally."
|
||||
docker image inspect "$IMAGE_REF" >/dev/null 2>&1 \
|
||||
|| die "Image ${IMAGE_REF} not present locally; remove --skip-build to build it."
|
||||
else
|
||||
log "Building ${IMAGE_REF} for ${DEFAULT_PLATFORM} (this may take several minutes)…"
|
||||
docker build \
|
||||
--platform "$DEFAULT_PLATFORM" \
|
||||
-f "$DOCKERFILE" \
|
||||
-t "$IMAGE_REF" \
|
||||
.
|
||||
ok "Build finished."
|
||||
fi
|
||||
|
||||
# ---------- pussh ----------
|
||||
log "Pusshing ${IMAGE_REF} to ${REMOTE} via unregistry…"
|
||||
|
||||
pussh_args=( "$REMOTE" )
|
||||
[[ -n "$SSH_KEY" ]] && pussh_args+=( -i "$SSH_KEY" )
|
||||
[[ -n "$SSH_CONFIG" ]] && pussh_args+=( -F "$SSH_CONFIG" )
|
||||
|
||||
docker pussh "${IMAGE_REF}" "${pussh_args[@]}"
|
||||
ok "Pussh complete."
|
||||
|
||||
# ---------- verify ----------
|
||||
if [[ "$SKIP_VERIFY" -eq 1 ]]; then
|
||||
warn "Skipping post-push verification (--no-verify)."
|
||||
else
|
||||
log "Verifying ${IMAGE_REF} on ${REMOTE}…"
|
||||
ssh_args=( "$REMOTE" )
|
||||
[[ -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."
|
||||
fi
|
||||
ok "Image present on remote."
|
||||
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"
|
||||
Reference in New Issue
Block a user