bastien-mrq/gitfed / deploy / update.sh
#!/usr/bin/env bash
# One-command release/redeploy — works the same whether you're cutting a
# new gitfed release or just redeploying a version someone else already
# released onto your own instance.
#
# Two modes, same script:
#
# deploy/update.sh # bump patch and cut a new release
# deploy/update.sh minor # bump minor, same
# deploy/update.sh major # bump major, same
# deploy/update.sh X.Y.Z # set an explicit version, same
# deploy/update.sh current # git pull (fast-forward only), then
# # build+deploy whatever VERSION ends up
# # saying — no version bump, no commit,
# # no CHANGELOG check. For redeploying
# # releases you don't author yourself
# # (see FEDERATION.md / INSTALL.md's
# # "mises à jour" section). Refuses to
# # run over a dirty working tree.
#
# Release mode (patch/minor/major/X.Y.Z): add the new version's entry to
# CHANGELOG.md yourself first — the script won't guess what changed. It
# commits the VERSION bump as "Release vX.Y.Z", and tags the final
# "Deploy vX.Y.Z" commit "vX.Y.Z" (that tag is what makes
# `go install .../gitfed/cmd/<tool>@latest` resolve to something, see
# FEDERATION.md). Nothing here pushes to your remote, including the tag —
# that's a separate `git push <remote> main --tags` afterward.
#
# "current" mode pulls (fast-forward only — it aborts rather than merge
# or rebase for you) then builds and rolls out whatever that leaves in
# VERSION/CHANGELOG.md/deploy/k8s/deployment.yaml. It's the only mode that
# touches git at all before deploying, and only ever a pull — no commits.
#
# Where this deploys to is never hardcoded — set it before running:
#
# export GITFED_VPS_HOST=you@your-vps # required
# export GITFED_VPS_SRC_DIR=~/gitfed-src # optional, this is the default
#
# ...or put those two lines in deploy/update.env (gitignored, one per
# person/instance) — sourced automatically if present, so you don't have
# to export them in every shell.
set -euo pipefail
cd "$(dirname "${BASH_SOURCE[0]}")/.."
if [ -f deploy/update.env ]; then
# shellcheck source=/dev/null
source deploy/update.env
fi
: "${GITFED_VPS_HOST:?GITFED_VPS_HOST is not set — export it, or put it in deploy/update.env (see the header comment above)}"
VPS_SRC_DIR="${GITFED_VPS_SRC_DIR:-~/gitfed-src}"
NAMESPACE="gitfed"
redeploy_current=0
if [ "${1:-patch}" = "current" ]; then
redeploy_current=1
if [ -n "$(git status --porcelain)" ]; then
echo "error: working tree has uncommitted changes — commit or stash them first. 'current' mode won't pull over local changes." >&2
exit 1
fi
echo "==> pulling latest"
git pull --ff-only
fi
current="$(cat VERSION)"
bump_patch() { IFS=. read -r maj min pat <<<"$1"; echo "$maj.$min.$((pat + 1))"; }
bump_minor() { IFS=. read -r maj min _ <<<"$1"; echo "$maj.$((min + 1)).0"; }
bump_major() { IFS=. read -r maj _ _ <<<"$1"; echo "$((maj + 1)).0.0"; }
case "${1:-patch}" in
current) new_version="$current" ;;
patch) new_version="$(bump_patch "$current")" ;;
minor) new_version="$(bump_minor "$current")" ;;
major) new_version="$(bump_major "$current")" ;;
[0-9]*.[0-9]*.[0-9]*) new_version="$1" ;;
*) echo "usage: $0 [patch|minor|major|X.Y.Z|current]" >&2; exit 1 ;;
esac
if [ "$redeploy_current" -eq 0 ]; then
if ! grep -q "^## $new_version\$" CHANGELOG.md; then
echo "error: CHANGELOG.md has no '## $new_version' entry yet — add one first." >&2
exit 1
fi
echo "==> $current -> $new_version"
echo "$new_version" > VERSION
git add VERSION CHANGELOG.md
git commit -m "Release v$new_version"
else
echo "==> redeploying v$new_version as-is (no version bump, no commit)"
fi
echo "==> syncing source to $GITFED_VPS_HOST:$VPS_SRC_DIR"
rsync -az --delete \
--exclude='.git' --exclude='bin' --exclude='demo' --exclude='.claude' --exclude='*.db' \
./ "$GITFED_VPS_HOST:$VPS_SRC_DIR/"
echo "==> building gitfed:$new_version on the VPS"
ssh "$GITFED_VPS_HOST" "cd $VPS_SRC_DIR && docker build -f deploy/docker/Dockerfile -t gitfed:$new_version -t gitfed:latest ."
echo "==> importing into k3s containerd"
ssh "$GITFED_VPS_HOST" "docker save gitfed:$new_version | sudo k3s ctr images import -"
echo "==> pointing deployment.yaml at gitfed:$new_version"
sed -i.bak "s|image: gitfed:.*|image: gitfed:$new_version|" deploy/k8s/deployment.yaml
rm -f deploy/k8s/deployment.yaml.bak
rsync -az deploy/k8s/deployment.yaml "$GITFED_VPS_HOST:$VPS_SRC_DIR/deploy/k8s/deployment.yaml"
echo "==> applying and rolling out"
ssh "$GITFED_VPS_HOST" "kubectl -n $NAMESPACE apply -f $VPS_SRC_DIR/deploy/k8s/deployment.yaml && kubectl -n $NAMESPACE rollout status deployment/gitfed --timeout=90s"
if [ "$redeploy_current" -eq 1 ]; then
echo "==> done: v$new_version is live"
exit 0
fi
git add deploy/k8s/deployment.yaml
if git diff --cached --quiet; then
echo "==> done: v$new_version is live (deployment.yaml was already up to date, nothing more to commit)"
else
git commit -m "Deploy v$new_version"
git tag "v$new_version"
echo "==> done: v$new_version is live (tagged v$new_version — push with: git push <remote> main --tags)"
fi