This commit is contained in:
2026-09-24 18:50:08 -03:00
parent 4417645639
commit f7bcad0b38
3 changed files with 69 additions and 20 deletions
+11 -8
View File
@@ -16,16 +16,19 @@ remote Docker daemon. No external registry is required.
./scripts/build-and-push.sh --remote deploy@prod.example.com ./scripts/build-and-push.sh --remote deploy@prod.example.com
``` ```
The script reads `artifactId` and `version` from `pom.xml` for the image name The script reads `artifactId` and `version` from `pom.xml` for the local build tag
and version tag, builds for `linux/amd64`, and verifies the image is present on and image name, builds for `linux/amd64`, and verifies the deploy on the remote.
the remote after pushing. On the remote (and locally) that push is tagged three On the remote, durable tags are only:
ways, all pointing at the same image:
- `<name>:<version>` — the version from `pom.xml` or `--tag` - `<name>:latest` — always the most recent successful push
- `<name>:latest` — moved to this push on every successful deploy - `<name>:vYYYY.MM.DD-HH.MM` — UTC timestamp of that push (for rollback)
- `<name>:vYYYY.MM.DD-HH.MM` — UTC timestamp of the push (for rollback; list with `docker images`)
To roll back on the remote, pick an older `v…` tag from `docker images test-quarkus` and run that reference instead of `:latest`. Older releases stay tagged by date only: the script keeps the current image (with
`latest` and its date tag) and the previous date tag, and removes other tags and
images (including old `1.0.x`, `sha256-*`, and third copies). Run
`--cleanup` on the remote to apply the same pruning without a new push.
To roll back on the remote, run a previous `v…` tag from `docker images` instead of `:latest`.
The temporary registry container and SSH tunnel are removed when the script The temporary registry container and SSH tunnel are removed when the script
finishes. See `./scripts/build-and-push.sh --help` for all flags. finishes. See `./scripts/build-and-push.sh --help` for all flags.
+1 -1
View File
@@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId> <groupId>com.example</groupId>
<artifactId>test-quarkus</artifactId> <artifactId>test-quarkus</artifactId>
<version>1.0.2</version> <version>1.0.3</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>Test Quarkus</name> <name>Test Quarkus</name>
+57 -11
View File
@@ -6,7 +6,7 @@
# - Image name/tag default to pom.xml properties (artifactId and version) # - 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 vYYYY.MM.DD-HH.MM (UTC) # - On the remote, durable tags are :latest and vYYYY.MM.DD-HH.MM (UTC) only
set -euo pipefail set -euo pipefail
IFS=$'\n\t' IFS=$'\n\t'
@@ -65,8 +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 Version tag. The remote also receives :latest --tag TAG Local build tag (from pom.xml by default). On the
and a vYYYY.MM.DD-HH.MM release tag (UTC). remote only :latest and vYYYY.MM.DD-HH.MM (UTC).
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
@@ -84,9 +84,9 @@ Post-push:
--no-verify Skip the ssh/docker images verification step --no-verify Skip the ssh/docker images verification step
Maintenance: Maintenance:
--cleanup Remove leftover temporary registry containers and --cleanup Remove temporary registry leftovers on the remote,
localhost:<port>/... image tags on the remote drop stale tags (version, sha256-*, etc.), and keep
(no build, no push; Docker local not required) at most two app images (current + previous release)
Other: Other:
-h, --help Show this help -h, --help Show this help
@@ -237,6 +237,46 @@ cleanup_remote_registry() {
ok "Remote cleanup complete." ok "Remote cleanup complete."
} }
# Keep :latest plus the two newest vYYYY.MM.DD-HH.MM releases; drop other tags/IDs.
prune_remote_image_history() {
local repo="${1:-$IMAGE_NAME}"
[[ -n "$repo" ]] || return 0
log "Pruning ${repo} on remote (keep latest and previous release)…"
run_remote "bash -s" <<EOS
set -euo pipefail
RD='${REMOTE_DOCKER}'
REPO='${repo}'
release_re='^v[0-9]{4}\\.[0-9]{2}\\.[0-9]{2}-[0-9]{2}\\.[0-9]{2}\$'
rmi_ref() {
\${RD} rmi "\$1" 2>/dev/null || true
}
while IFS= read -r ref; do
tag="\${ref#*:}"
[[ "\$tag" == latest ]] && continue
if [[ "\$tag" =~ \${release_re} ]]; then continue; fi
rmi_ref "\$ref"
done < <(\${RD} images --format '{{.Repository}}:{{.Tag}}' | grep -E "^\${REPO}:" || true)
mapfile -t release_refs < <(\${RD} images --format '{{.Repository}}:{{.Tag}}' | grep -E "^\${REPO}:v[0-9]{4}\\." | sort -t: -k2)
declare -A keep_ids=()
n=\${#release_refs[@]}
if (( n == 0 )); then exit 0; fi
for (( i=n-1; i>=0 && i>=n-2; i-- )); do
id=\$(\${RD} images --format '{{.ID}}' "\${release_refs[i]}" | head -1)
[[ -n "\$id" ]] && keep_ids["\$id"]=1
done
while IFS= read -r ref; do
id=\$(\${RD} images --format '{{.ID}}' "\$ref" | head -1)
[[ -n "\${keep_ids[\$id]:-}" ]] && continue
rmi_ref "\$ref"
done < <(\${RD} images --format '{{.Repository}}:{{.Tag}}' | grep -E "^\${REPO}:" || true)
EOS
ok "Remote ${repo} pruned."
}
REG_PUSH_CONTAINER="" REG_PUSH_CONTAINER=""
REG_REMOTE_PORT="" REG_REMOTE_PORT=""
REG_LOCAL_PORT="" REG_LOCAL_PORT=""
@@ -324,13 +364,13 @@ push_via_registry_tunnel() {
docker tag "$IMAGE_REF" "$latest_ref" docker tag "$IMAGE_REF" "$latest_ref"
docker tag "$IMAGE_REF" "$RELEASE_REF" docker tag "$IMAGE_REF" "$RELEASE_REF"
log "Pulling image on remote and tagging ${IMAGE_REF}, ${latest_ref}, ${RELEASE_REF}…" log "Pulling image on remote and tagging ${latest_ref}, ${RELEASE_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} ${latest_ref}" run_remote "${REMOTE_DOCKER} tag ${remote_registry_ref} ${latest_ref}"
run_remote "${REMOTE_DOCKER} tag ${remote_registry_ref} ${RELEASE_REF}" run_remote "${REMOTE_DOCKER} tag ${remote_registry_ref} ${RELEASE_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}, ${RELEASE_REF}." prune_remote_image_history "${IMAGE_NAME}"
ok "Remote tags: ${latest_ref}, ${RELEASE_REF}."
} }
# ---------- arg parsing ---------- # ---------- arg parsing ----------
@@ -368,10 +408,16 @@ fi
if [[ "$CLEANUP" -eq 1 ]]; then if [[ "$CLEANUP" -eq 1 ]]; then
log "Remote: ${REMOTE_TARGET}" log "Remote: ${REMOTE_TARGET}"
require ssh require ssh
if ! cd "${PROJECT_DIR}" 2>/dev/null; then
die "Invalid --project-dir: ${PROJECT_DIR}"
fi
[[ -z "$IMAGE_NAME" ]] \
&& IMAGE_NAME=$(read_pom "/*[local-name()='project']/*[local-name()='artifactId']" "test-quarkus" "artifactId")
parse_ssh_target parse_ssh_target
build_ssh_args build_ssh_args
detect_remote_docker detect_remote_docker
cleanup_remote_registry cleanup_remote_registry
prune_remote_image_history "${IMAGE_NAME}"
exit 0 exit 0
fi fi
@@ -441,14 +487,14 @@ else
log "Verifying tags on ${REMOTE_TARGET}…" log "Verifying tags on ${REMOTE_TARGET}…"
remote_images=$(run_remote "${REMOTE_DOCKER} images --format '{{.Repository}}:{{.Tag}}'") remote_images=$(run_remote "${REMOTE_DOCKER} images --format '{{.Repository}}:{{.Tag}}'")
missing=0 missing=0
for ref in "${IMAGE_REF}" "${IMAGE_NAME}:latest" "${RELEASE_REF}"; do for ref in "${IMAGE_NAME}:latest" "${RELEASE_REF}"; do
if ! printf '%s\n' "$remote_images" | grep -Fxq "$ref"; then if ! printf '%s\n' "$remote_images" | grep -Fxq "$ref"; then
err "Missing on remote: ${ref}" err "Missing on remote: ${ref}"
missing=1 missing=1
fi fi
done done
[[ "$missing" -eq 0 ]] || die "Expected tags were not found on ${REMOTE_TARGET} after push." [[ "$missing" -eq 0 ]] || die "Expected tags were not found on ${REMOTE_TARGET} after push."
ok "Remote has ${IMAGE_REF}, ${IMAGE_NAME}:latest and ${RELEASE_REF}." ok "Remote has ${IMAGE_NAME}:latest and ${RELEASE_REF}."
fi fi
# ---------- next step hint ---------- # ---------- next step hint ----------