From 134f0b0a7acbd57dcd86c22bfd835c30385af15a Mon Sep 17 00:00:00 2001 From: Amritanshu Date: Sun, 6 Sep 2026 13:21:16 +0530 Subject: [PATCH] Deploy workflow: commit-id image tags, guarded deploy.sh, opt-in prune - Makefile builds from 'git archive HEAD' (stale-remote-HEAD impossible): tags registry.tanshu.com/mozimo: + :main; build-check target verifies both architectures without pushing - deploy.sh: refuses dirty tracked tree / HEAD != origin/main (--force to bypass), ff-only pull, passes the commit as image_tag to ansible, --prune flag for opt-in image cleanup - ansible: docker_image uses image_tag extra-var (falls back to tag), prune task keeps the 3 newest mozimo images, mozimo-repo only --- Makefile | 21 +++++++--- ansible/roles/mozimo/tasks/main.yaml | 7 ++++ ansible/vars/default.yml | 4 +- deploy.sh | 57 +++++++++++++++++++++++----- 4 files changed, 73 insertions(+), 16 deletions(-) diff --git a/Makefile b/Makefile index 021c938..135f8aa 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,19 @@ +COMMIT ?= $(shell git rev-parse --short HEAD) + .PHONY: build-production -build-production: ## Build the production docker image. - @docker buildx build \ +build-production: ## Multi-arch build from HEAD; tags registry.tanshu.com/mozimo: + :main + @git archive --format=tar HEAD | docker buildx build \ --platform linux/amd64,linux/arm64 \ - --tag registry.tanshu.com/mozimo:staging \ - $(if $(TAG),--tag registry.tanshu.com/mozimo:$(TAG)) \ + --tag registry.tanshu.com/mozimo:$(COMMIT) \ + --tag registry.tanshu.com/mozimo:main \ --push \ - git@git.tanshu.com:tanshu/mozimo.in.git + - + +.PHONY: build-check +build-check: ## Multi-arch compile check from HEAD, nothing pushed + @git archive --format=tar HEAD | docker buildx build \ + --platform linux/amd64,linux/arm64 \ + --tag mozimo:check \ + --pull \ + --progress=plain \ + - diff --git a/ansible/roles/mozimo/tasks/main.yaml b/ansible/roles/mozimo/tasks/main.yaml index 65c422b..f2d0aa9 100644 --- a/ansible/roles/mozimo/tasks/main.yaml +++ b/ansible/roles/mozimo/tasks/main.yaml @@ -31,3 +31,10 @@ - "{{ host_directory }}/pb_data:/app/pb_data" networks: - name: "{{ docker_network }}" + +- name: Prune old mozimo images, keep newest 3 (opt-in via ./deploy.sh --prune) + ansible.builtin.shell: > + docker images "registry.tanshu.com/mozimo" --format "{{.ID}}" | tail -n +4 | xargs -r docker rmi + when: mozimo_image_prune | default(false) | bool + failed_when: false + changed_when: true diff --git a/ansible/vars/default.yml b/ansible/vars/default.yml index 320d1e8..7408add 100644 --- a/ansible/vars/default.yml +++ b/ansible/vars/default.yml @@ -12,7 +12,9 @@ host: "staging.mozimo.in" docker_network: "{{ title }}_net" -docker_image: "{{ registry }}/{{ title }}:{{ tag }}" +# image tag: deploy.sh passes -e image_tag=; "tag" is the fallback +# when running the playbook by hand — rollback = -e image_tag= +docker_image: "{{ registry }}/{{ title }}:{{ image_tag | default(tag) }}" docker_container: "{{ title }}-staging" docker_port: 3000 diff --git a/deploy.sh b/deploy.sh index 5d19a74..3a697be 100755 --- a/deploy.sh +++ b/deploy.sh @@ -1,14 +1,51 @@ #!/usr/bin/env bash +# Mozimo deploy — build the exact commit at origin/main, tag it with its +# commit id, and hand that tag to ansible. Guards make it impossible to +# deploy anything other than what git knows about. +# +# ./deploy.sh deploy HEAD (must be pushed, tree must be clean) +# ./deploy.sh --prune also drop old mozimo images on the server (keep 3) +# ./deploy.sh --force skip the guards (know what you are doing) set -euo pipefail -parent_path=$( cd "$(dirname "${BASH_SOURCE[0]}")" || exit ; pwd -P ) -cd "$parent_path" || exit -git pull -if [ 1 -eq "$#" ] -then - make build-production TAG="$1" -else - make build-production +cd "$(dirname "${BASH_SOURCE[0]}")" + +FORCE=false +PRUNE=false +for arg in "$@"; do + case "$arg" in + --force) FORCE=true ;; + --prune) PRUNE=true ;; + *) + echo "Usage: $0 [--force] [--prune]" + exit 1 + ;; + esac +done + +# guards: only tracked-file state matters (untracked files never enter a +# `git archive HEAD` build); HEAD must equal origin/main +if [ "$FORCE" != true ]; then + if [ -n "$(git status --porcelain --untracked-files=no)" ]; then + echo "✗ tracked files modified — commit or stash first (or --force)" >&2 + exit 1 + fi + git fetch origin --quiet + if [ "$(git rev-parse HEAD)" != "$(git rev-parse origin/main)" ]; then + echo "✗ HEAD != origin/main — push or pull first (or --force)" >&2 + exit 1 + fi fi -cd "$parent_path/ansible" || exit -ansible-playbook playbook.yml +git pull --ff-only +TAG=$(git rev-parse --short HEAD) +echo "▶ deploying commit $TAG" +make build-production TAG="$TAG" + +PRUNE_FLAG="" +if [ "$PRUNE" = true ]; then + PRUNE_FLAG="-e mozimo_image_prune=true" +fi + +cd ansible +# shellcheck disable=SC2086 +ansible-playbook playbook.yml -e "image_tag=$TAG" $PRUNE_FLAG