feat(docker): add release script and enhance push script
- Add release.sh for automated versioning and image pushing - Enhance push.sh with target selection (app/vpn/all) - Add docker/podman engine auto-detection - Improve usage docs and error handling
This commit is contained in:
101
Docker/push.sh
101
Docker/push.sh
@@ -1,15 +1,19 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# filepath: /home/lukas/Volume/repo/Aniworld/Docker/push.sh
|
|
||||||
#
|
#
|
||||||
# Build and push Aniworld container images to the Gitea registry.
|
# Build and push AniWorld container images to the Gitea registry.
|
||||||
#
|
#
|
||||||
# Usage:
|
# Usage:
|
||||||
# ./push.sh # builds & pushes with tag "latest"
|
# ./push.sh # builds & pushes app with tag "latest"
|
||||||
# ./push.sh v1.2.3 # builds & pushes with tag "v1.2.3"
|
# ./push.sh app # builds & pushes app image
|
||||||
# ./push.sh v1.2.3 --no-build # pushes existing images only
|
# ./push.sh vpn # builds & pushes vpn image
|
||||||
|
# ./push.sh all # builds & pushes both images
|
||||||
|
# ./push.sh app v1.2.3 # builds & pushes app with tag "v1.2.3"
|
||||||
|
# ./push.sh vpn v1.2.3 # builds & pushes vpn with tag "v1.2.3"
|
||||||
|
# ./push.sh all v1.2.3 # builds & pushes both images
|
||||||
|
# ./push.sh app v1.2.3 --no-build # pushes existing image only
|
||||||
#
|
#
|
||||||
# Prerequisites:
|
# Prerequisites:
|
||||||
# podman login git.lpl-mind.de
|
# podman login git.lpl-mind.de (or: docker login git.lpl-mind.de)
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
@@ -23,12 +27,20 @@ PROJECT="aniworld"
|
|||||||
APP_IMAGE="${REGISTRY}/${NAMESPACE}/${PROJECT}/app"
|
APP_IMAGE="${REGISTRY}/${NAMESPACE}/${PROJECT}/app"
|
||||||
VPN_IMAGE="${REGISTRY}/${NAMESPACE}/${PROJECT}/vpn"
|
VPN_IMAGE="${REGISTRY}/${NAMESPACE}/${PROJECT}/vpn"
|
||||||
|
|
||||||
TAG="${1:-latest}"
|
# Parse arguments
|
||||||
|
TARGET="${1:-app}"
|
||||||
|
TAG="${2:-latest}"
|
||||||
SKIP_BUILD=false
|
SKIP_BUILD=false
|
||||||
if [[ "${2:-}" == "--no-build" ]]; then
|
if [[ "${3:-}" == "--no-build" ]]; then
|
||||||
SKIP_BUILD=true
|
SKIP_BUILD=true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Validate target
|
||||||
|
if [[ "${TARGET}" != "app" && "${TARGET}" != "vpn" && "${TARGET}" != "all" ]]; then
|
||||||
|
echo "ERROR: Invalid target '${TARGET}'. Must be one of: app, vpn, all" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||||
|
|
||||||
@@ -36,62 +48,93 @@ PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
|||||||
# Helpers
|
# Helpers
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
log() { echo -e "\n>>> $*"; }
|
log() { echo -e "\n>>> $*"; }
|
||||||
err() { echo -e "\n❌ ERROR: $*" >&2; exit 1; }
|
err() { echo -e "\nERROR: $*" >&2; exit 1; }
|
||||||
|
|
||||||
|
# Detect container engine (podman preferred, docker fallback)
|
||||||
|
if command -v podman &>/dev/null; then
|
||||||
|
ENGINE="podman"
|
||||||
|
elif command -v docker &>/dev/null; then
|
||||||
|
ENGINE="docker"
|
||||||
|
else
|
||||||
|
err "Neither podman nor docker is installed."
|
||||||
|
fi
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Pre-flight checks
|
# Pre-flight checks
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
echo "============================================"
|
echo "============================================"
|
||||||
echo " Aniworld — Build & Push"
|
echo " AniWorld — Build & Push"
|
||||||
|
echo " Engine : ${ENGINE}"
|
||||||
echo " Registry : ${REGISTRY}"
|
echo " Registry : ${REGISTRY}"
|
||||||
|
echo " Target : ${TARGET}"
|
||||||
echo " Tag : ${TAG}"
|
echo " Tag : ${TAG}"
|
||||||
echo "============================================"
|
echo "============================================"
|
||||||
|
|
||||||
command -v podman &>/dev/null || err "podman is not installed."
|
log "Logging in to ${REGISTRY}"
|
||||||
|
"${ENGINE}" login "${REGISTRY}"
|
||||||
if ! podman login --get-login "${REGISTRY}" &>/dev/null; then
|
|
||||||
err "Not logged in. Run:\n podman login ${REGISTRY}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Build
|
# Build
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
if [[ "${SKIP_BUILD}" == false ]]; then
|
build_app() {
|
||||||
log "Building app image → ${APP_IMAGE}:${TAG}"
|
log "Building app image → ${APP_IMAGE}:${TAG}"
|
||||||
podman build \
|
"${ENGINE}" build \
|
||||||
-t "${APP_IMAGE}:${TAG}" \
|
-t "${APP_IMAGE}:${TAG}" \
|
||||||
-f "${SCRIPT_DIR}/Dockerfile.app" \
|
-f "${SCRIPT_DIR}/Dockerfile.app" \
|
||||||
"${PROJECT_ROOT}"
|
"${PROJECT_ROOT}"
|
||||||
|
}
|
||||||
|
|
||||||
log "Building VPN image → ${VPN_IMAGE}:${TAG}"
|
build_vpn() {
|
||||||
podman build \
|
log "Building vpn image → ${VPN_IMAGE}:${TAG}"
|
||||||
|
"${ENGINE}" build \
|
||||||
-t "${VPN_IMAGE}:${TAG}" \
|
-t "${VPN_IMAGE}:${TAG}" \
|
||||||
-f "${SCRIPT_DIR}/Containerfile" \
|
-f "${SCRIPT_DIR}/Containerfile" \
|
||||||
"${SCRIPT_DIR}"
|
"${SCRIPT_DIR}"
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ "${SKIP_BUILD}" == false ]]; then
|
||||||
|
case "${TARGET}" in
|
||||||
|
app) build_app ;;
|
||||||
|
vpn) build_vpn ;;
|
||||||
|
all) build_app; build_vpn ;;
|
||||||
|
esac
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Push
|
# Push
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
log "Pushing ${APP_IMAGE}:${TAG}"
|
push_app() {
|
||||||
podman push "${APP_IMAGE}:${TAG}"
|
log "Pushing ${APP_IMAGE}:${TAG}"
|
||||||
|
"${ENGINE}" push "${APP_IMAGE}:${TAG}"
|
||||||
|
}
|
||||||
|
|
||||||
log "Pushing ${VPN_IMAGE}:${TAG}"
|
push_vpn() {
|
||||||
podman push "${VPN_IMAGE}:${TAG}"
|
log "Pushing ${VPN_IMAGE}:${TAG}"
|
||||||
|
"${ENGINE}" push "${VPN_IMAGE}:${TAG}"
|
||||||
|
}
|
||||||
|
|
||||||
|
case "${TARGET}" in
|
||||||
|
app) push_app ;;
|
||||||
|
vpn) push_vpn ;;
|
||||||
|
all) push_app; push_vpn ;;
|
||||||
|
esac
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Summary
|
# Summary
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
echo ""
|
echo ""
|
||||||
echo "============================================"
|
echo "============================================"
|
||||||
echo " ✅ Push complete!"
|
echo " Push complete!"
|
||||||
echo ""
|
echo ""
|
||||||
echo " Images:"
|
echo " Images:"
|
||||||
echo " ${APP_IMAGE}:${TAG}"
|
case "${TARGET}" in
|
||||||
echo " ${VPN_IMAGE}:${TAG}"
|
app) echo " ${APP_IMAGE}:${TAG}" ;;
|
||||||
|
vpn) echo " ${VPN_IMAGE}:${TAG}" ;;
|
||||||
|
all) echo " ${APP_IMAGE}:${TAG}"; echo " ${VPN_IMAGE}:${TAG}" ;;
|
||||||
|
esac
|
||||||
echo ""
|
echo ""
|
||||||
echo " Deploy on server:"
|
echo " Deploy on server:"
|
||||||
echo " podman login ${REGISTRY}"
|
echo " ${ENGINE} login ${REGISTRY}"
|
||||||
echo " podman-compose -f podman-compose.prod.yml pull"
|
echo " ${ENGINE} compose -f Docker/podman-compose.prod.yml pull"
|
||||||
echo " podman-compose -f podman-compose.prod.yml up -d"
|
echo " ${ENGINE} compose -f Docker/podman-compose.prod.yml up -d"
|
||||||
echo "============================================"
|
echo "============================================"
|
||||||
129
Docker/release.sh
Normal file
129
Docker/release.sh
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# Bump the project version and push images to the registry.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ./release.sh
|
||||||
|
#
|
||||||
|
# The current version is stored in VERSION (next to this script).
|
||||||
|
# You will be asked whether to bump major, minor, or patch.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
VERSION_FILE="${SCRIPT_DIR}/VERSION"
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Read current version
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
if [[ ! -f "${VERSION_FILE}" ]]; then
|
||||||
|
echo "0.0.0" > "${VERSION_FILE}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
CURRENT="$(cat "${VERSION_FILE}")"
|
||||||
|
# Strip leading 'v' for arithmetic
|
||||||
|
VERSION="${CURRENT#v}"
|
||||||
|
|
||||||
|
IFS='.' read -r MAJOR MINOR PATCH <<< "${VERSION}"
|
||||||
|
|
||||||
|
echo "============================================"
|
||||||
|
echo " AniWorld — Release"
|
||||||
|
echo " Current version: v${MAJOR}.${MINOR}.${PATCH}"
|
||||||
|
echo "============================================"
|
||||||
|
echo ""
|
||||||
|
echo "Which image(s) would you like to release?"
|
||||||
|
echo " 1) app (Dockerfile.app)"
|
||||||
|
echo " 2) vpn (Containerfile)"
|
||||||
|
echo " 3) all (both images)"
|
||||||
|
echo ""
|
||||||
|
read -rp "Enter choice [1/2/3]: " TARGET_CHOICE
|
||||||
|
|
||||||
|
case "${TARGET_CHOICE}" in
|
||||||
|
1) TARGET="app" ;;
|
||||||
|
2) TARGET="vpn" ;;
|
||||||
|
3) TARGET="all" ;;
|
||||||
|
*)
|
||||||
|
echo "Invalid choice. Aborting." >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "How would you like to bump the version?"
|
||||||
|
echo " 1) patch (v${MAJOR}.${MINOR}.${PATCH} → v${MAJOR}.${MINOR}.$((PATCH + 1)))"
|
||||||
|
echo " 2) minor (v${MAJOR}.${MINOR}.${PATCH} → v${MAJOR}.$((MINOR + 1)).0)"
|
||||||
|
echo " 3) major (v${MAJOR}.${MINOR}.${PATCH} → v$((MAJOR + 1)).0.0)"
|
||||||
|
echo ""
|
||||||
|
read -rp "Enter choice [1/2/3]: " CHOICE
|
||||||
|
|
||||||
|
case "${CHOICE}" in
|
||||||
|
1) NEW_TAG="v${MAJOR}.${MINOR}.$((PATCH + 1))" ;;
|
||||||
|
2) NEW_TAG="v${MAJOR}.$((MINOR + 1)).0" ;;
|
||||||
|
3) NEW_TAG="v$((MAJOR + 1)).0.0" ;;
|
||||||
|
*)
|
||||||
|
echo "Invalid choice. Aborting." >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "New version: ${NEW_TAG}"
|
||||||
|
echo "Target: ${TARGET}"
|
||||||
|
read -rp "Confirm? [y/N]: " CONFIRM
|
||||||
|
if [[ ! "${CONFIRM}" =~ ^[yY]$ ]]; then
|
||||||
|
echo "Aborted."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Write new version
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
echo "${NEW_TAG}" > "${VERSION_FILE}"
|
||||||
|
echo "Version file updated → ${VERSION_FILE}"
|
||||||
|
|
||||||
|
# Keep root package.json in sync.
|
||||||
|
FRONT_VERSION="${NEW_TAG#v}"
|
||||||
|
FRONT_PKG="${SCRIPT_DIR}/../package.json"
|
||||||
|
if [[ -f "${FRONT_PKG}" ]]; then
|
||||||
|
sed -i "s/\"version\": \"[^\"]*\"/\"version\": \"${FRONT_VERSION}\"/" "${FRONT_PKG}"
|
||||||
|
echo "package.json version updated → ${FRONT_VERSION}"
|
||||||
|
else
|
||||||
|
echo "Warning: package.json not found, skipping package.json version sync" >&2
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Keep root pyproject.toml in sync.
|
||||||
|
BACKEND_PYPROJECT="${SCRIPT_DIR}/../pyproject.toml"
|
||||||
|
if [[ -f "${BACKEND_PYPROJECT}" ]]; then
|
||||||
|
# Update version under [project] section if present
|
||||||
|
if grep -q '^\[project\]' "${BACKEND_PYPROJECT}"; then
|
||||||
|
sed -i "/^\[project\]/,/^\[/ s/^version = \".*\"/version = \"${FRONT_VERSION}\"/" "${BACKEND_PYPROJECT}"
|
||||||
|
else
|
||||||
|
sed -i "s/^version = \".*\"/version = \"${FRONT_VERSION}\"/" "${BACKEND_PYPROJECT}"
|
||||||
|
fi
|
||||||
|
echo "pyproject.toml version updated → ${FRONT_VERSION}"
|
||||||
|
else
|
||||||
|
echo "Warning: pyproject.toml not found, skipping pyproject.toml version sync" >&2
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Push containers
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
bash "${SCRIPT_DIR}/push.sh" "${TARGET}" "${NEW_TAG}"
|
||||||
|
bash "${SCRIPT_DIR}/push.sh" "${TARGET}"
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Git tag (local only; push after container build)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
cd "${SCRIPT_DIR}/.."
|
||||||
|
git add Docker/VERSION package.json pyproject.toml
|
||||||
|
git commit -m "chore: release ${NEW_TAG}"
|
||||||
|
git tag -a "${NEW_TAG}" -m "Release ${NEW_TAG}"
|
||||||
|
echo "Local git commit + tag ${NEW_TAG} created."
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Push git commits & tag
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
git push origin HEAD
|
||||||
|
git push origin "${NEW_TAG}"
|
||||||
|
echo "Git commit and tag ${NEW_TAG} pushed."
|
||||||
Reference in New Issue
Block a user