Files
Inventarsystem/.github/workflows/release-docker.yml
T

329 lines
11 KiB
YAML

name: https://github.com/AIIrondev/legendary-octo-garbanzo
on:
push:
tags:
- "v*"
workflow_dispatch:
inputs:
bump:
description: "Version bump type (major stays fixed from latest release)"
required: false
default: "patch"
type: choice
options:
- patch
- minor
- development
permissions:
contents: write
packages: write
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
jobs:
release-docker:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set metadata
id: meta
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO: ${{ github.repository }}
EVENT_NAME: ${{ github.event_name }}
REF_NAME: ${{ github.ref_name }}
BUMP_TYPE: ${{ github.event.inputs.bump || 'patch' }}
run: |
if [ "$EVENT_NAME" = "push" ] && [ -n "$REF_NAME" ]; then
TAG="$REF_NAME"
else
TAG="$(python3 - <<'PY'
import json
import os
import re
import urllib.request
repo = os.environ["REPO"]
token = os.environ.get("GH_TOKEN", "")
bump = os.environ.get("BUMP_TYPE", "patch").strip().lower()
req = urllib.request.Request(
f"https://api.github.com/repos/{repo}/releases/latest",
headers={
"Authorization": f"Bearer {token}",
"Accept": "application/vnd.github+json",
"User-Agent": "legendary-octo-garbanzo-release-workflow",
},
)
latest_tag = "v3.0.0"
try:
with urllib.request.urlopen(req, timeout=20) as resp:
data = json.loads(resp.read().decode("utf-8"))
latest_tag = (data.get("tag_name") or latest_tag).strip()
except Exception:
pass
m = re.match(r"^v(\d+)\.(\d+)\.(\d+)$", latest_tag)
if not m:
major, minor, patch = 3, 0, 0
else:
major, minor, patch = map(int, m.groups())
# Bump strategy: major / minor / patch
if bump == "major":
major += 1
minor = 0
patch = 0
elif bump == "minor":
minor += 1
patch = 0
else:
patch += 1
print(f"v{major}.{minor}.{patch}")
PY
)"
fi
if ! echo "$TAG" | grep -Eq '^v[0-9]+\.[0-9]+\.[0-9]+$'; then
echo "Error: tag '$TAG' is not valid semver (vX.Y.Z)"
exit 1
fi
git fetch --tags --force
LATEST_TAG="$(git tag -l 'v*' | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | sort -V | tail -n1)"
if [ -z "$LATEST_TAG" ]; then
LATEST_TAG="v3.0.0"
fi
TAG_MAJOR="${TAG#v}"
TAG_MAJOR="${TAG_MAJOR%%.*}"
LATEST_MAJOR="$(echo "$LATEST_TAG" | grep -Eo '^v[0-9]+' | tr -d 'v')"
if [ -z "$LATEST_MAJOR" ]; then
LATEST_MAJOR="3"
fi
# If not explicitly bumping major, disallow changing major version
if [ "${BUMP_TYPE:-}" != "major" ] && [ "$TAG_MAJOR" != "$LATEST_MAJOR" ]; then
echo "Error: major version must stay v$LATEST_MAJOR.x.x (got $TAG)"
exit 1
fi
while git rev-parse -q --verify "refs/tags/$TAG" >/dev/null; do
BASE="${TAG%.*}"
PATCH="${TAG##*.}"
PATCH="$((PATCH + 1))"
TAG="$BASE.$PATCH"
done
IMAGE="ghcr.io/aiirondev/legendary-octo-garbanzo:${TAG}"
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
echo "image=$IMAGE" >> "$GITHUB_OUTPUT"
if [ "${BUMP_TYPE:-}" = "development" ]; then
echo "is_development=true" >> "$GITHUB_OUTPUT"
else
echo "is_development=false" >> "$GITHUB_OUTPUT"
fi
- name: Create and push tag for manual releases
if: github.event_name == 'workflow_dispatch' && steps.meta.outputs.is_development != 'true'
run: |
TAG="${{ steps.meta.outputs.tag }}"
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git tag "$TAG"
git push origin "$TAG"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push release image
if: steps.meta.outputs.is_development != 'true'
uses: docker/build-push-action@v6
with:
context: .
file: ./Dockerfile
push: true
tags: |
${{ steps.meta.outputs.image }}
ghcr.io/aiirondev/legendary-octo-garbanzo:latest
- name: Build and push development image
uses: docker/build-push-action@v6
with:
context: .
file: ./Dockerfile
push: true
tags: |
ghcr.io/aiirondev/legendary-octo-garbanzo:development
- name: Build local image tar for offline deploy
run: |
set -euo pipefail
IMG="${{ steps.meta.outputs.image }}"
TAG="${{ steps.meta.outputs.tag }}"
if docker image inspect "$IMG" >/dev/null 2>&1; then
echo "Using local image $IMG"
docker save "$IMG" | gzip > "inventarsystem-image-${TAG}.tar.gz"
exit 0
fi
echo "Local image $IMG not found, trying to pull"
if docker pull "$IMG" >/dev/null 2>&1; then
docker save "$IMG" | gzip > "inventarsystem-image-${TAG}.tar.gz"
exit 0
fi
echo "Pull failed, attempting local docker build as fallback"
docker build -t "$IMG" .
docker save "$IMG" | gzip > "inventarsystem-image-${TAG}.tar.gz"
- name: Build local development image tar for offline deploy
run: |
set -euo pipefail
DEV_IMG=ghcr.io/aiirondev/legendary-octo-garbanzo:development
if docker image inspect "$DEV_IMG" >/dev/null 2>&1; then
echo "Using local development image $DEV_IMG"
docker save "$DEV_IMG" | gzip > "inventarsystem-image-development.tar.gz"
exit 0
fi
echo "Local development image not found, trying to pull $DEV_IMG"
if docker pull "$DEV_IMG" >/dev/null 2>&1; then
docker save "$DEV_IMG" | gzip > "inventarsystem-image-development.tar.gz"
exit 0
fi
echo "Pull failed, attempting local docker build for development image"
docker build -t "$DEV_IMG" .
docker save "$DEV_IMG" | gzip > "inventarsystem-image-development.tar.gz"
- name: Create release-only docker bundle
run: |
mkdir -p release-bundle
cat > release-bundle/docker-compose.yml <<EOF
services:
app:
image: ${INVENTAR_APP_IMAGE:-ghcr.io/aiirondev/legendary-octo-garbanzo:${{ steps.meta.outputs.tag }}}
container_name: inventarsystem-app
restart: unless-stopped
ports:
- "${INVENTAR_HTTP_PORT:-10000}:8000"
depends_on:
- mongodb
- redis
environment:
INVENTAR_MONGODB_HOST: mongodb
INVENTAR_MONGODB_PORT: "27017"
INVENTAR_MONGODB_DB: Inventarsystem
INVENTAR_BACKUP_FOLDER: /data/backups
INVENTAR_LOGS_FOLDER: /data/logs
expose:
- "8000"
volumes:
- ./config.json:/app/config.json:ro
- app_uploads:/app/Web/uploads
- app_thumbnails:/app/Web/thumbnails
- app_previews:/app/Web/previews
- app_qrcodes:/app/Web/QRCodes
- app_backups:/data/backups
- app_logs:/data/logs
mongodb:
image: mongo:7.0
container_name: inventarsystem-mongodb
restart: unless-stopped
volumes:
- mongodb_data:/data/db
healthcheck:
test: ["CMD", "mongosh", "--quiet", "--eval", "db.adminCommand('ping').ok"]
interval: 10s
timeout: 5s
retries: 10
redis:
image: redis:7-alpine
container_name: inventarsystem-redis
restart: unless-stopped
command: redis-server --appendonly yes --maxmemory 512mb --maxmemory-policy allkeys-lru
ports:
- "6379:6379"
volumes:
- redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
volumes:
mongodb_data:
app_uploads:
app_thumbnails:
app_previews:
app_qrcodes:
app_backups:
app_logs:
redis_data:
EOF
# Copy runtime scripts and config if present
for f in start.sh stop.sh restart.sh backup.sh config.json update.sh; do
if [ -f "$f" ]; then
cp "$f" "release-bundle/$(basename "$f")"
fi
done
# Multitenant scripts & docs (optional)
for f in docker-compose-multitenant.yml manage-tenant.sh run-tenant-cmd.sh MULTITENANT_DEPLOYMENT.md MULTITENANT_PYTHON_API.md; do
if [ -f "$f" ]; then
cp "$f" "release-bundle/$(basename "$f")"
fi
done
# Include optional development image tar and helper note
if [ -f "inventarsystem-image-development.tar.gz" ]; then
cp inventarsystem-image-development.tar.gz release-bundle/ || true
fi
cat > release-bundle/DEVELOPMENT.md <<'EOF'
This bundle contains an optional development image tar: inventarsystem-image-development.tar.gz
To install the development build on a target host, extract the bundle and run:
./update.sh development
EOF
# Make any shipped scripts executable
find release-bundle -maxdepth 1 -type f -name '*.sh' -exec chmod +x {} \; || true
tar -czf inventarsystem-docker-bundle.tar.gz -C release-bundle .
- name: Create or update GitHub Release
if: steps.meta.outputs.is_development != 'true'
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.meta.outputs.tag }}
files: |
inventarsystem-docker-bundle.tar.gz
inventarsystem-image-${{ steps.meta.outputs.tag }}.tar.gz
inventarsystem-image-development.tar.gz
fail_on_unmatched_files: false
generate_release_notes: true