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 - major - development push_dev: description: "If true, push the :dev image to GHCR for development releases" required: false default: "false" type: choice options: - "true" - "false" 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' }} PUSH_DEV: ${{ github.event.inputs.push_dev || 'false' }} run: | if [ "$EVENT_NAME" = "push" ] && [ -n "$REF_NAME" ]; then TAG="$REF_NAME" else # Fetch latest release tag via GitHub API (fall back to v3.0.0) latest_tag="v3.0.0" if meta_json=$(curl -fsSL -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" "https://api.github.com/repos/$REPO/releases/latest" 2>/dev/null); then tag_name=$(printf "%s" "$meta_json" | sed -n 's/.*"tag_name"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -n1) if [ -n "$tag_name" ]; then latest_tag="$tag_name" fi fi if [[ "$latest_tag" =~ ^v([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then major=${BASH_REMATCH[1]} minor=${BASH_REMATCH[2]} patch=${BASH_REMATCH[3]} else major=3; minor=0; patch=0 fi # Bump strategy: major / minor / patch if [ "${BUMP_TYPE:-}" = "major" ]; then major=$((major + 1)); minor=0; patch=0 elif [ "${BUMP_TYPE:-}" = "minor" ]; then minor=$((minor + 1)); patch=0 else patch=$((patch + 1)) fi if [ "${BUMP_TYPE:-}" = "development" ]; then TAG="v${major}.${minor}.${patch}-dev" else TAG="v${major}.${minor}.${patch}" fi fi if ! echo "$TAG" | grep -Eq '^v[0-9]+\.[0-9]+\.[0-9]+(-dev(\.[0-9]+)?)?$'; then echo "Error: tag '$TAG' is not valid semver (vX.Y.Z or vX.Y.Z-dev)" 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 # Ensure tag uniqueness: if tag exists append numeric suffix if git rev-parse -q --verify "refs/tags/$TAG" >/dev/null; then i=1 base="$TAG" while git rev-parse -q --verify "refs/tags/${base}.${i}" >/dev/null; do i="$((i + 1))" done TAG="${base}.${i}" fi 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 if [ "${BUMP_TYPE:-}" = "development" ] && [ "${PUSH_DEV:-}" = "true" ]; then echo "push_dev=true" >> "$GITHUB_OUTPUT" else echo "push_dev=false" >> "$GITHUB_OUTPUT" fi - name: Update .release-version file run: | echo "${{ steps.meta.outputs.tag }}" > .release-version cat .release-version - name: Create and push tag for manual releases if: github.event_name == 'workflow_dispatch' 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 add .release-version git commit -m "chore: bump version to $TAG" || true git tag "$TAG" git push origin "$TAG" git push origin HEAD:${{ github.ref_name }} - 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 (:dev) if: steps.meta.outputs.is_development == 'true' && steps.meta.outputs.push_dev == 'true' uses: docker/build-push-action@v6 with: context: . file: ./Dockerfile push: true tags: | ghcr.io/aiirondev/legendary-octo-garbanzo:dev - 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" # development tar omitted: dev releases will be versioned (vX.Y.Z-dev) and handled by update.sh using the tag - name: Commit .release-version for tag pushes if: github.event_name == 'push' run: | git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" if ! git diff --quiet .release-version; then git add .release-version git commit -m "chore: update version to ${{ steps.meta.outputs.tag }}" git push origin HEAD:${{ github.ref_name }} fi - name: Create release-only docker bundle run: | mkdir -p release-bundle cat > release-bundle/docker-compose.yml < release-bundle/DEVELOPMENT.md <