Add loader for long running operations #2010
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Build, Tag, Push, and Release to GitHub Container Registry | |
| on: | |
| push: | |
| branches: | |
| - '**' | |
| jobs: | |
| build-and-push: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| packages: write | |
| issues: read | |
| checks: write | |
| pull-requests: write | |
| steps: | |
| - name: Checkout Repository | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Log in to Docker Hub | |
| uses: docker/login-action@v2 | |
| with: | |
| username: ${{ secrets.DOCKER_USERNAME }} | |
| password: ${{ secrets.DOCKER_PASSWORD }} | |
| - name: Log in to GitHub Container Registry | |
| uses: docker/login-action@v2 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ github.token }} | |
| - name: Set up QEMU for multi-arch builds | |
| uses: docker/setup-qemu-action@v3 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Set up JDK 21 | |
| uses: actions/setup-java@v4 | |
| with: | |
| java-version: '21' | |
| distribution: 'temurin' | |
| - name: Run Backend Tests | |
| id: backend_tests | |
| working-directory: ./booklore-api | |
| run: | | |
| echo "Running backend tests with testcontainers..." | |
| ./gradlew test | |
| continue-on-error: true | |
| - name: Publish Test Results | |
| uses: EnricoMi/publish-unit-test-result-action@v2 | |
| if: always() | |
| with: | |
| files: booklore-api/build/test-results/**/*.xml | |
| check_name: Backend Test Results | |
| comment_title: Backend Test Results | |
| report_individual_runs: true | |
| report_suite_logs: 'any' | |
| - name: Upload Test Reports | |
| uses: actions/upload-artifact@v4 | |
| if: always() | |
| with: | |
| name: test-reports | |
| path: | | |
| booklore-api/build/reports/tests/ | |
| booklore-api/build/test-results/ | |
| retention-days: 30 | |
| - name: Check Test Results | |
| if: steps.backend_tests.outcome == 'failure' | |
| run: | | |
| echo "❌ Backend tests failed! Check the test results above." | |
| exit 1 | |
| - name: Get Latest Master Version | |
| id: get_version | |
| run: | | |
| latest_tag=$(git tag --list "v*" --sort=-v:refname | head -n 1) | |
| latest_tag=${latest_tag:-"v0.0.0"} | |
| echo "latest_tag=$latest_tag" >> $GITHUB_ENV | |
| echo "Latest master tag: $latest_tag" | |
| - name: Determine Version Bump (Only for Master) | |
| if: github.ref == 'refs/heads/master' | |
| id: determine_bump | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| echo "Determining version bump from PR labels..." | |
| # Extract PR number from merge commit | |
| pr_number=$(git log -1 --pretty=%B | grep -oE 'Merge pull request #[0-9]+' | grep -oE '[0-9]+') || true | |
| if [ -z "$pr_number" ]; then | |
| pr_number=$(gh pr list --state merged --base master --limit 1 --json number --jq '.[0].number') | |
| fi | |
| echo "PR number: $pr_number" | |
| labels=$(gh pr view "$pr_number" --json labels --jq '.labels[].name' || echo "") | |
| echo "PR labels: $labels" | |
| if echo "$labels" | grep -q 'major'; then | |
| bump="major" | |
| elif echo "$labels" | grep -q 'minor'; then | |
| bump="minor" | |
| elif echo "$labels" | grep -q 'patch'; then | |
| bump="patch" | |
| else | |
| last_commit_msg=$(git log -1 --pretty=%B) | |
| if echo "$last_commit_msg" | grep -iq '#major'; then | |
| bump="major" | |
| elif echo "$last_commit_msg" | grep -iq '#minor'; then | |
| bump="minor" | |
| elif echo "$last_commit_msg" | grep -iq '#patch'; then | |
| bump="patch" | |
| else | |
| bump="patch" | |
| fi | |
| fi | |
| # Calculate next version | |
| semver=$(echo ${{ env.latest_tag }} | sed 's/^v//') | |
| major=$(echo $semver | cut -d. -f1) | |
| minor=$(echo $semver | cut -d. -f2) | |
| patch=$(echo $semver | cut -d. -f3) | |
| case "$bump" in | |
| major) major=$((major+1)); minor=0; patch=0 ;; | |
| minor) minor=$((minor+1)); patch=0 ;; | |
| patch) patch=$((patch+1)) ;; | |
| esac | |
| next_version="v${major}.${minor}.${patch}" | |
| echo "Version bump type: $bump" | |
| echo "Next version: $next_version" | |
| echo "bump=$bump" >> $GITHUB_ENV | |
| echo "new_tag=$next_version" >> $GITHUB_ENV | |
| - name: Generate Image Tag | |
| id: set_image_tag | |
| run: | | |
| branch="${GITHUB_REF#refs/heads/}" | |
| if [[ "$branch" == "master" ]]; then | |
| image_tag="${{ env.new_tag }}" | |
| elif [[ "$branch" == "develop" ]]; then | |
| short_sha=$(git rev-parse --short HEAD) | |
| image_tag="${{ env.latest_tag }}-develop-${short_sha}" | |
| else | |
| short_sha=$(git rev-parse --short HEAD) | |
| image_tag="${short_sha}" | |
| fi | |
| echo "image_tag=$image_tag" >> $GITHUB_ENV | |
| echo "Image tag: $image_tag" | |
| - name: Build and Push Docker Image | |
| run: | | |
| docker buildx create --use | |
| docker buildx build \ | |
| --platform linux/amd64,linux/arm64 \ | |
| --build-arg APP_VERSION=${{ env.image_tag }} \ | |
| --build-arg APP_REVISION=${{ github.sha }} \ | |
| --tag booklore/booklore:${{ env.image_tag }} \ | |
| --tag ghcr.io/booklore-app/booklore:${{ env.image_tag }} \ | |
| --push . | |
| - name: Push Latest Tag (Only for Master) | |
| if: github.ref == 'refs/heads/master' | |
| run: | | |
| docker buildx build \ | |
| --platform linux/amd64,linux/arm64 \ | |
| --build-arg APP_VERSION=${{ env.new_tag }} \ | |
| --tag booklore/booklore:latest \ | |
| --tag ghcr.io/booklore-app/booklore:latest \ | |
| --push . | |
| - name: Update Release Draft (Only for Master) | |
| if: github.ref == 'refs/heads/master' | |
| uses: release-drafter/release-drafter@v6 | |
| with: | |
| tag: ${{ env.new_tag }} | |
| name: "Release ${{ env.new_tag }}" | |
| env: | |
| GITHUB_TOKEN: ${{ github.token }} | |
| - name: Publish Draft Release (Only for Master) | |
| if: github.ref == 'refs/heads/master' | |
| env: | |
| GITHUB_TOKEN: ${{ github.token }} | |
| run: | | |
| gh release edit ${{ env.new_tag }} --draft=false | |
| - name: Notify Discord of New Release | |
| if: false | |
| continue-on-error: true | |
| shell: bash | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }} | |
| NEW_TAG: ${{ env.new_tag }} | |
| run: | | |
| set -euo pipefail | |
| if [ -z "${DISCORD_WEBHOOK_URL:-}" ]; then | |
| echo "DISCORD_WEBHOOK_URL not set, skipping Discord notification." | |
| exit 0 | |
| fi | |
| release_json=$(gh release view "$NEW_TAG" --json name,body,url) | |
| release_name=$(jq -r '.name' <<< "$release_json") | |
| release_body=$(jq -r '.body' <<< "$release_json") | |
| release_url=$(jq -r '.url' <<< "$release_json") | |
| clean_body=$(echo "$release_body" | tr -d '\r') | |
| max_length=1800 | |
| if [ ${#clean_body} -gt $max_length ]; then | |
| clean_body="${clean_body:0:$((max_length-12))}… [truncated]" | |
| fi | |
| payload=$(jq -n \ | |
| --arg title "New Release: $release_name" \ | |
| --arg url "$release_url" \ | |
| --arg desc "$clean_body" \ | |
| '{ | |
| content: null, | |
| embeds: [{ | |
| title: $title, | |
| url: $url, | |
| description: $desc, | |
| color: 3066993 | |
| }] | |
| }') | |
| curl -H "Content-Type: application/json" -d "$payload" "$DISCORD_WEBHOOK_URL" |