cambios cleanup del registry remoto y tags

This commit is contained in:
2026-09-24 17:18:05 -03:00
parent 5b4bbce5c8
commit faabf0b82b
2 changed files with 112 additions and 28 deletions
+19 -3
View File
@@ -17,9 +17,25 @@ remote Docker daemon. No external registry is required.
``` ```
The script reads `artifactId` and `version` from `pom.xml` for the image name 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 and version tag, builds for `linux/amd64`, and verifies the image is present on
remote after pushing. The temporary registry container and SSH tunnel are removed the remote after pushing. On the remote (and locally) that push is tagged three
when the script finishes. See `./scripts/build-and-push.sh --help` for all flags. ways, all pointing at the same image:
- `<name>:<version>` — the version from `pom.xml` or `--tag`
- `<name>:latest` — moved to this push on every successful deploy
- `<name>:sha256-<12 hex chars>` — immutable tag taken from the registry digest
The temporary registry container and SSH tunnel are removed when the script
finishes. See `./scripts/build-and-push.sh --help` for all flags.
To remove leftover temporary registry containers and `localhost:<port>/...` image
tags on the remote (for example after an interrupted push or manual tests):
```shell script
./scripts/build-and-push.sh --remote deploy@prod.example.com --cleanup
```
This does not rebuild or push; it only needs SSH access to the remote host.
It can be invoked from any directory: by default it resolves the project root 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 from the script's own location. Override with `--project-dir PATH` or the
+91 -23
View File
@@ -3,9 +3,10 @@
# Build the native Quarkus image and push it to a remote Docker host # Build the native Quarkus image and push it to a remote Docker host
# via a temporary registry:2 on the remote, reached through an SSH tunnel. # via a temporary registry:2 on the remote, reached through an SSH tunnel.
# #
# - Image name/tag default to pom.xml properties (quarkus.application.*) # - Image name/tag default to pom.xml properties (artifactId and version)
# - Builds for linux/amd64 by default (cross-compile from arm64 hosts) # - Builds for linux/amd64 by default (cross-compile from arm64 hosts)
# - Transfers layers over the tunnel (no external registry required) # - Transfers layers over the tunnel (no external registry required)
# - On the remote, tags the image as <version>, latest, and sha256-<digest>
set -euo pipefail set -euo pipefail
IFS=$'\n\t' IFS=$'\n\t'
@@ -23,6 +24,7 @@ SSH_CONFIG=""
PROJECT_DIR="${PROJECT_DIR:-${SCRIPT_DIR}/..}" PROJECT_DIR="${PROJECT_DIR:-${SCRIPT_DIR}/..}"
SKIP_BUILD=0 SKIP_BUILD=0
SKIP_VERIFY=0 SKIP_VERIFY=0
CLEANUP=0
# ---------- logging ---------- # ---------- logging ----------
if [[ -t 1 ]]; then if [[ -t 1 ]]; then
@@ -63,7 +65,8 @@ Required:
Image identity (defaults read from pom.xml): Image identity (defaults read from pom.xml):
--name NAME Image repository name --name NAME Image repository name
--tag TAG Image tag --tag TAG Version tag. The remote also receives :latest
and a sha256-<digest> tag for this push.
Working directory (defaults to the parent of the script's directory): Working directory (defaults to the parent of the script's directory):
--project-dir PATH Project root containing pom.xml --project-dir PATH Project root containing pom.xml
@@ -80,6 +83,11 @@ SSH options:
Post-push: Post-push:
--no-verify Skip the ssh/docker images verification step --no-verify Skip the ssh/docker images verification step
Maintenance:
--cleanup Remove leftover temporary registry containers and
localhost:<port>/... image tags on the remote
(no build, no push; Docker local not required)
Other: Other:
-h, --help Show this help -h, --help Show this help
@@ -94,6 +102,7 @@ Examples:
$(basename "$0") --remote prod # uses 'Host prod' in ~/.ssh/config $(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 ubuntu@1.2.3.4 --ssh-key ~/.ssh/deploy_ed25519
$(basename "$0") --remote deploy@prod --skip-build --tag 2.0.0 $(basename "$0") --remote deploy@prod --skip-build --tag 2.0.0
$(basename "$0") --remote prod --cleanup
/absolute/path/to/test-quarkus/scripts/$(basename "$0") --remote deploy@prod /absolute/path/to/test-quarkus/scripts/$(basename "$0") --remote deploy@prod
PROJECT_DIR=/srv/proj $(basename "$0") --remote deploy@prod --skip-build PROJECT_DIR=/srv/proj $(basename "$0") --remote deploy@prod --skip-build
EOF EOF
@@ -217,13 +226,27 @@ detect_remote_docker() {
die "Cannot run docker on ${REMOTE_TARGET}. Ensure Docker is installed and the SSH user may run docker (or passwordless sudo docker)." die "Cannot run docker on ${REMOTE_TARGET}. Ensure Docker is installed and the SSH user may run docker (or passwordless sudo docker)."
} }
cleanup_remote_registry() {
log "Cleaning temporary registry leftovers on ${REMOTE_TARGET}…"
run_remote "${REMOTE_DOCKER} ps -aq --filter name=reg-push- | xargs -r ${REMOTE_DOCKER} rm -f" \
>/dev/null 2>&1 || true
run_remote "${REMOTE_DOCKER} rm -f regtest" >/dev/null 2>&1 || true
run_remote "${REMOTE_DOCKER} images --format '{{.Repository}}:{{.Tag}}' \
| grep -E '^localhost:[0-9]+/' | xargs -r ${REMOTE_DOCKER} rmi" \
>/dev/null 2>&1 || true
ok "Remote cleanup complete."
}
REG_PUSH_CONTAINER="" REG_PUSH_CONTAINER=""
REG_REMOTE_PORT="" REG_REMOTE_PORT=""
REG_LOCAL_PORT="" REG_LOCAL_PORT=""
TUNNEL_PID="" TUNNEL_PID=""
LOCAL_REGISTRY_REF="" LOCAL_REGISTRY_REF=""
DIGEST_REF=""
PUSH_LOG=""
cleanup_push_resources() { cleanup_push_resources() {
[[ -n "${PUSH_LOG:-}" ]] && rm -f "$PUSH_LOG"
[[ -n "${LOCAL_REGISTRY_REF:-}" ]] \ [[ -n "${LOCAL_REGISTRY_REF:-}" ]] \
&& docker rmi "$LOCAL_REGISTRY_REF" >/dev/null 2>&1 || true && docker rmi "$LOCAL_REGISTRY_REF" >/dev/null 2>&1 || true
if [[ -n "${TUNNEL_PID:-}" ]]; then if [[ -n "${TUNNEL_PID:-}" ]]; then
@@ -280,20 +303,47 @@ start_ssh_tunnel() {
die "Failed to establish SSH tunnel to remote registry." die "Failed to establish SSH tunnel to remote registry."
} }
digest_tag_from_push_log() {
local log_file="$1"
local digest hex
digest=$(sed -n 's/.*digest: \(sha256:[0-9a-f]\{64\}\).*/\1/p' "$log_file" | tail -1)
if [[ -z "$digest" ]]; then
digest=$(docker image inspect --format '{{index .RepoDigests 0}}' "$LOCAL_REGISTRY_REF" 2>/dev/null \
| sed -n 's/.*@\(sha256:[0-9a-f]\{64\}\)/\1/p')
fi
[[ -n "$digest" ]] || die "Could not read the image digest after push."
hex="${digest#sha256:}"
printf 'sha256-%s' "${hex:0:12}"
}
push_via_registry_tunnel() { push_via_registry_tunnel() {
LOCAL_REGISTRY_REF="localhost:${REG_LOCAL_PORT}/${IMAGE_NAME}:${IMAGE_TAG}" LOCAL_REGISTRY_REF="localhost:${REG_LOCAL_PORT}/${IMAGE_NAME}:${IMAGE_TAG}"
local remote_registry_ref="localhost:${REG_REMOTE_PORT}/${IMAGE_NAME}:${IMAGE_TAG}" local remote_registry_ref="localhost:${REG_REMOTE_PORT}/${IMAGE_NAME}:${IMAGE_TAG}"
local latest_ref="${IMAGE_NAME}:latest"
local digest_tag
log "Tagging ${IMAGE_REF} as ${LOCAL_REGISTRY_REF}…" log "Tagging ${IMAGE_REF} as ${LOCAL_REGISTRY_REF}…"
docker tag "$IMAGE_REF" "$LOCAL_REGISTRY_REF" docker tag "$IMAGE_REF" "$LOCAL_REGISTRY_REF"
log "Pushing ${LOCAL_REGISTRY_REF} through SSH tunnel…" log "Pushing ${LOCAL_REGISTRY_REF} through SSH tunnel…"
docker push "$LOCAL_REGISTRY_REF" PUSH_LOG=$(mktemp)
docker push "$LOCAL_REGISTRY_REF" | tee "$PUSH_LOG"
digest_tag=$(digest_tag_from_push_log "$PUSH_LOG")
rm -f "$PUSH_LOG"
PUSH_LOG=""
DIGEST_REF="${IMAGE_NAME}:${digest_tag}"
log "Pulling image on remote and retagging as ${IMAGE_REF}…" log "Tagging local image as ${latest_ref} and ${DIGEST_REF}…"
docker tag "$IMAGE_REF" "$latest_ref"
docker tag "$IMAGE_REF" "$DIGEST_REF"
log "Pulling image on remote and tagging ${IMAGE_REF}, ${latest_ref}, ${DIGEST_REF}…"
run_remote "${REMOTE_DOCKER} pull ${remote_registry_ref}" run_remote "${REMOTE_DOCKER} pull ${remote_registry_ref}"
run_remote "${REMOTE_DOCKER} tag ${remote_registry_ref} ${IMAGE_REF}" run_remote "${REMOTE_DOCKER} tag ${remote_registry_ref} ${IMAGE_REF}"
run_remote "${REMOTE_DOCKER} tag ${remote_registry_ref} ${latest_ref}"
run_remote "${REMOTE_DOCKER} tag ${remote_registry_ref} ${DIGEST_REF}"
run_remote "${REMOTE_DOCKER} rmi ${remote_registry_ref}" >/dev/null || true run_remote "${REMOTE_DOCKER} rmi ${remote_registry_ref}" >/dev/null || true
ok "Remote tags: ${IMAGE_REF}, ${latest_ref}, ${DIGEST_REF}."
} }
# ---------- arg parsing ---------- # ---------- arg parsing ----------
@@ -307,24 +357,14 @@ while [[ $# -gt 0 ]]; do
--project-dir) PROJECT_DIR="$2"; shift 2 ;; --project-dir) PROJECT_DIR="$2"; shift 2 ;;
--skip-build) SKIP_BUILD=1; shift ;; --skip-build) SKIP_BUILD=1; shift ;;
--no-verify) SKIP_VERIFY=1; shift ;; --no-verify) SKIP_VERIFY=1; shift ;;
--cleanup) CLEANUP=1; shift ;;
-h|--help) usage; exit 0 ;; -h|--help) usage; exit 0 ;;
*) die "Unknown argument: $1 (use --help)" ;; *) die "Unknown argument: $1 (use --help)" ;;
esac esac
done done
# ---------- resolve project root ----------
if ! cd "${PROJECT_DIR}" 2>/dev/null; then
die "Invalid --project-dir: ${PROJECT_DIR}"
fi
PROJECT_DIR="$(pwd)"
# ---------- defaults ----------
[[ -z "$IMAGE_NAME" ]] && IMAGE_NAME=$(read_pom "/*[local-name()='project']/*[local-name()='artifactId']" "test-quarkus" "artifactId")
[[ -z "$IMAGE_TAG" ]] && IMAGE_TAG=$(read_pom "/*[local-name()='project']/*[local-name()='version']" "1.0.0-SNAPSHOT" "version")
[[ -z "$REMOTE" ]] && die "Missing --remote USER@HOST[:PORT] (use --help)." [[ -z "$REMOTE" ]] && die "Missing --remote USER@HOST[:PORT] (use --help)."
IMAGE_REF="${IMAGE_NAME}:${IMAGE_TAG}"
# ---------- resolve SSH target ---------- # ---------- resolve SSH target ----------
resolved=$(resolve_ssh_alias "$REMOTE") resolved=$(resolve_ssh_alias "$REMOTE")
REMOTE_TARGET="${resolved%|*}" REMOTE_TARGET="${resolved%|*}"
@@ -337,6 +377,29 @@ if [[ -z "$SSH_KEY" && -n "$REMOTE_IDENTITY" ]]; then
log "Using IdentityFile from SSH config: ${SSH_KEY}" log "Using IdentityFile from SSH config: ${SSH_KEY}"
fi fi
# ---------- remote cleanup only ----------
if [[ "$CLEANUP" -eq 1 ]]; then
log "Remote: ${REMOTE_TARGET}"
require ssh
parse_ssh_target
build_ssh_args
detect_remote_docker
cleanup_remote_registry
exit 0
fi
# ---------- resolve project root ----------
if ! cd "${PROJECT_DIR}" 2>/dev/null; then
die "Invalid --project-dir: ${PROJECT_DIR}"
fi
PROJECT_DIR="$(pwd)"
# ---------- defaults ----------
[[ -z "$IMAGE_NAME" ]] && IMAGE_NAME=$(read_pom "/*[local-name()='project']/*[local-name()='artifactId']" "test-quarkus" "artifactId")
[[ -z "$IMAGE_TAG" ]] && IMAGE_TAG=$(read_pom "/*[local-name()='project']/*[local-name()='version']" "1.0.0-SNAPSHOT" "version")
IMAGE_REF="${IMAGE_NAME}:${IMAGE_TAG}"
# ---------- pre-flight ---------- # ---------- pre-flight ----------
log "Image: ${IMAGE_REF}" log "Image: ${IMAGE_REF}"
log "Platform: ${DEFAULT_PLATFORM}" log "Platform: ${DEFAULT_PLATFORM}"
@@ -388,15 +451,20 @@ ok "Push complete."
if [[ "$SKIP_VERIFY" -eq 1 ]]; then if [[ "$SKIP_VERIFY" -eq 1 ]]; then
warn "Skipping post-push verification (--no-verify)." warn "Skipping post-push verification (--no-verify)."
else else
log "Verifying ${IMAGE_REF} on ${REMOTE_TARGET}…" log "Verifying tags on ${REMOTE_TARGET}…"
remote_images=$(run_remote "${REMOTE_DOCKER} images --format '{{.Repository}}:{{.Tag}}'")
if ! run_remote "${REMOTE_DOCKER} images --format '{{.Repository}}:{{.Tag}} {{.Size}}' | grep -E '^${IMAGE_NAME}:${IMAGE_TAG} '"; then missing=0
die "Image ${IMAGE_REF} not found on ${REMOTE_TARGET} after push." for ref in "${IMAGE_REF}" "${IMAGE_NAME}:latest" "${DIGEST_REF}"; do
if ! printf '%s\n' "$remote_images" | grep -Fxq "$ref"; then
err "Missing on remote: ${ref}"
missing=1
fi fi
ok "Image present on remote." done
[[ "$missing" -eq 0 ]] || die "Expected tags were not found on ${REMOTE_TARGET} after push."
ok "Remote has ${IMAGE_REF}, ${IMAGE_NAME}:latest and ${DIGEST_REF}."
fi fi
# ---------- next step hint ---------- # ---------- next step hint ----------
printf '\n%s→ To run on the remote:%s\n' "$C_OK" "$C_RESET" printf '\n%s→ To run the latest image 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' \ printf ' ssh %s "docker run -d --name %s --restart unless-stopped -p 8080:8080 %s:latest"\n\n' \
"$REMOTE_TARGET" "$IMAGE_NAME" "$IMAGE_REF" "$REMOTE_TARGET" "$IMAGE_NAME" "$IMAGE_NAME"