diff --git a/.github/workflows/ci-petclinic-eks.yaml b/.github/workflows/ci-petclinic-eks.yaml index 24d96a635..94215b698 100644 --- a/.github/workflows/ci-petclinic-eks.yaml +++ b/.github/workflows/ci-petclinic-eks.yaml @@ -6,19 +6,21 @@ on: pull_request: branches: ["main"] +# Queue (do not cancel) so EKS/GKE GitOps commits don't race each other +concurrency: + group: gitops-main + cancel-in-progress: false + env: AWS_REGION: ${{ vars.AWS_REGION || 'ap-northeast-2' }} ECR_REPOSITORY: ${{ vars.ECR_REPOSITORY || 'eks/petclinic' }} + YAML_PATH: k8s/aws/20-petclinic-Deployments-postgre.yaml jobs: - build-and-push-eks: + build-test: runs-on: ubuntu-latest - if: github.actor != 'github-actions[bot]' - permissions: - id-token: write contents: read - steps: - name: Checkout uses: actions/checkout@v4 @@ -30,31 +32,55 @@ jobs: java-version: "25" cache: maven - - name: Maven build + - name: Maven build & test run: | if [ -x "./mvnw" ]; then - ./mvnw -B clean package + ./mvnw -B clean test package else - mvn -B clean package + mvn -B clean test package fi - # ---- Push-related steps: only on main pushes (not PR) ---- + publish-and-gitops: + needs: build-test + runs-on: ubuntu-latest + + # Only publish/commit on main pushes (never on PRs) + if: github.event_name == 'push' && github.ref == 'refs/heads/main' && github.actor != 'github-actions[bot]' + + permissions: + id-token: write + contents: write + + steps: + - name: Checkout (with token for push) + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up JDK + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: "25" + cache: maven + - name: Configure AWS credentials (OIDC) - if: github.event_name != 'pull_request' uses: aws-actions/configure-aws-credentials@v4 with: role-to-assume: ${{ secrets.AWS_ROLE_ARN }} aws-region: ${{ env.AWS_REGION }} - name: Login to Amazon ECR - if: github.event_name != 'pull_request' id: ecr-login uses: aws-actions/amazon-ecr-login@v2 - - name: Build APP_VERSION (AWS) - if: github.event_name != 'pull_request' + - name: Compute IMAGE_TAG / IMAGE_URI / APP_VERSION (AWS) run: | - SHORT_SHA=${GITHUB_SHA::7} + set -euo pipefail + + IMAGE_TAG="${GITHUB_SHA::7}" + echo "IMAGE_TAG=${IMAGE_TAG}" >> "$GITHUB_ENV" + BASE_VERSION=$( if [ -x "./mvnw" ]; then ./mvnw -q -Dexpression=project.version -DforceStdout help:evaluate @@ -62,49 +88,76 @@ jobs: mvn -q -Dexpression=project.version -DforceStdout help:evaluate fi ) - BASE_VERSION=${BASE_VERSION%-SNAPSHOT} - APP_VERSION="A-${BASE_VERSION}-${SHORT_SHA}" + BASE_VERSION="${BASE_VERSION%-SNAPSHOT}" + echo "BASE_VERSION=${BASE_VERSION}" >> "$GITHUB_ENV" + + APP_VERSION="A-${BASE_VERSION}-${IMAGE_TAG}" echo "APP_VERSION=${APP_VERSION}" >> "$GITHUB_ENV" - - name: Build image URI - if: github.event_name != 'pull_request' - run: | ECR_REGISTRY="${{ steps.ecr-login.outputs.registry }}" - IMAGE_TAG="${GITHUB_SHA::7}" - IMAGE_URI="${ECR_REGISTRY}/${{ env.ECR_REPOSITORY }}:${IMAGE_TAG}" IMAGE_BASE="${ECR_REGISTRY}/${{ env.ECR_REPOSITORY }}" - echo "IMAGE_TAG=${IMAGE_TAG}" >> "$GITHUB_ENV" - echo "IMAGE_URI=${IMAGE_URI}" >> "$GITHUB_ENV" + IMAGE_URI="${IMAGE_BASE}:${IMAGE_TAG}" echo "IMAGE_BASE=${IMAGE_BASE}" >> "$GITHUB_ENV" + echo "IMAGE_URI=${IMAGE_URI}" >> "$GITHUB_ENV" - - name: Build & Push Docker image (ECR) - if: github.event_name != 'pull_request' + echo "IMAGE_URI=${IMAGE_URI}" + echo "APP_VERSION=${APP_VERSION}" + + - name: Build & Push image (ECR) + env: + DOCKER_BUILDKIT: 1 run: | + set -euo pipefail docker build -t "$IMAGE_URI" . docker push "$IMAGE_URI" - name: Tag & Push latest (ECR) - if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main' run: | + set -euo pipefail docker tag "${IMAGE_BASE}:${IMAGE_TAG}" "${IMAGE_BASE}:latest" docker push "${IMAGE_BASE}:latest" - - name: Inject APP_VERSION into EKS Deployment - if: github.event_name != 'pull_request' + - name: Update EKS manifest (image + APP_VERSION) run: | - # Assumes your Deployment already contains: - # - name: APP_VERSION - # value: "local" - sudo snap install yq --channel=v4/stable - yq -i ' - (.spec.template.spec.containers[].env[] - | select(.name == "APP_VERSION").value) = "'"${APP_VERSION}"'" - ' k8s/aws/20-petclinic-Deployments-postgre.yaml + set -euo pipefail + test -f "$YAML_PATH" || { echo "Missing manifest: $YAML_PATH"; exit 1; } - - name: Inject image tag into EKS Deployment - if: github.event_name != 'pull_request' - run: | sudo snap install yq --channel=v4/stable - yq -i ' - (.spec.template.spec.containers[].image) = "'"${IMAGE_URI}"'" - ' k8s/aws/20-petclinic-Deployments-postgre.yaml + + yq -i '(.spec.template.spec.containers[] | select(.name == "petclinic-container") | .image) = strenv(IMAGE_URI)' "$YAML_PATH" + + yq -i '(.spec.template.spec.containers[] | select(.name == "petclinic-container") | .env[] | select(.name == "APP_VERSION").value) = strenv(APP_VERSION)' "$YAML_PATH" + + echo "Updated $YAML_PATH" + yq '.spec.template.spec.containers[] | select(.name == "petclinic-container") | {image, env}' "$YAML_PATH" + + - name: Commit & push GitOps change (EKS manifest only) + run: | + set -euo pipefail + + git config user.name "github-actions" + git config user.email "github-actions@github.com" + + git add "$YAML_PATH" + + if git diff --cached --quiet; then + echo "No manifest changes to commit." + exit 0 + fi + + git commit -m "chore(eks): deploy ${IMAGE_TAG} (APP_VERSION=${APP_VERSION}) [skip ci]" + + for i in 1 2 3 4 5; do + echo "Push attempt ${i}..." + git fetch origin main + git rebase origin/main + if git push origin HEAD:main; then + echo "Push succeeded." + exit 0 + fi + echo "Push failed; retrying..." + sleep 2 + done + + echo "Push failed after retries." + exit 1 diff --git a/.github/workflows/ci-petclinic-eks.yaml.bak b/.github/workflows/ci-petclinic-eks.yaml.bak index 233be3f8e..24d96a635 100644 --- a/.github/workflows/ci-petclinic-eks.yaml.bak +++ b/.github/workflows/ci-petclinic-eks.yaml.bak @@ -1,71 +1,27 @@ name: CI - Petclinic EKS -permissions: - contents: read - on: push: branches: ["main"] pull_request: branches: ["main"] -concurrency: - group: ci-${{ github.ref }} - cancel-in-progress: false - env: AWS_REGION: ${{ vars.AWS_REGION || 'ap-northeast-2' }} ECR_REPOSITORY: ${{ vars.ECR_REPOSITORY || 'eks/petclinic' }} jobs: - # 1) Maven 빌드 + 테스트 (PR 포함) - build-test: + build-and-push-eks: + runs-on: ubuntu-latest if: github.actor != 'github-actions[bot]' - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Set up JDK - uses: actions/setup-java@v4 - with: - distribution: temurin - java-version: "25" - cache: maven - - - name: Maven build & test - run: | - if [ -x "./mvnw" ]; then - ./mvnw -B clean test package - else - mvn -B clean test package - fi - - - name: Archive built JAR - uses: actions/upload-artifact@v4 - with: - name: petclinic-jar - path: target/*.jar - - # 2) Docker 이미지 빌드 + ECR Push + k8s manifest 이미지 태그/앱 버전 업데이트 (main push만) - build-and-push-image: - needs: build-test - runs-on: ubuntu-latest - - # PR에서는 push/commit 금지 + github-actions bot 커밋으로 재트리거 방지 - if: github.event_name != 'pull_request' && github.actor != 'github-actions[bot]' permissions: id-token: write - contents: write + contents: read steps: - name: Checkout uses: actions/checkout@v4 - with: - fetch-depth: 0 - name: Set up JDK uses: actions/setup-java@v4 @@ -74,109 +30,81 @@ jobs: java-version: "25" cache: maven - - name: Configure AWS credentials (OIDC / Assume Role) + - name: Maven build + run: | + if [ -x "./mvnw" ]; then + ./mvnw -B clean package + else + mvn -B clean package + fi + + # ---- Push-related steps: only on main pushes (not PR) ---- + - name: Configure AWS credentials (OIDC) + if: github.event_name != 'pull_request' uses: aws-actions/configure-aws-credentials@v4 with: role-to-assume: ${{ secrets.AWS_ROLE_ARN }} aws-region: ${{ env.AWS_REGION }} - name: Login to Amazon ECR + if: github.event_name != 'pull_request' id: ecr-login uses: aws-actions/amazon-ecr-login@v2 - - name: Set image tag + - name: Build APP_VERSION (AWS) + if: github.event_name != 'pull_request' run: | SHORT_SHA=${GITHUB_SHA::7} - echo "IMAGE_TAG=${SHORT_SHA}" >> "$GITHUB_ENV" - echo "IMAGE_TAG=${SHORT_SHA}" - - - name: Read base version from pom.xml - run: | - if [ -x "./mvnw" ]; then - POM_VERSION=$(./mvnw -q -Dexpression=project.version -DforceStdout help:evaluate) - else - POM_VERSION=$(mvn -q -Dexpression=project.version -DforceStdout help:evaluate) - fi - echo "POM_VERSION=${POM_VERSION}" - BASE_VERSION=${POM_VERSION%-SNAPSHOT} - echo "BASE_VERSION=${BASE_VERSION}" >> "$GITHUB_ENV" - - - name: Build APP_VERSION (base + image tag) - run: | - APP_VERSION="A-${BASE_VERSION}-${IMAGE_TAG}" + BASE_VERSION=$( + if [ -x "./mvnw" ]; then + ./mvnw -q -Dexpression=project.version -DforceStdout help:evaluate + else + mvn -q -Dexpression=project.version -DforceStdout help:evaluate + fi + ) + BASE_VERSION=${BASE_VERSION%-SNAPSHOT} + APP_VERSION="A-${BASE_VERSION}-${SHORT_SHA}" echo "APP_VERSION=${APP_VERSION}" >> "$GITHUB_ENV" - echo "APP_VERSION=${APP_VERSION}" - - - name: Update application version property - run: | - sed -i "s/^app.version=.*/app.version=${APP_VERSION}/" src/main/resources/application.properties - name: Build image URI + if: github.event_name != 'pull_request' run: | ECR_REGISTRY="${{ steps.ecr-login.outputs.registry }}" + IMAGE_TAG="${GITHUB_SHA::7}" IMAGE_URI="${ECR_REGISTRY}/${{ env.ECR_REPOSITORY }}:${IMAGE_TAG}" IMAGE_BASE="${ECR_REGISTRY}/${{ env.ECR_REPOSITORY }}" + echo "IMAGE_TAG=${IMAGE_TAG}" >> "$GITHUB_ENV" echo "IMAGE_URI=${IMAGE_URI}" >> "$GITHUB_ENV" echo "IMAGE_BASE=${IMAGE_BASE}" >> "$GITHUB_ENV" - echo "IMAGE_URI=${IMAGE_URI}" - - name: Build Docker image - env: - DOCKER_BUILDKIT: 1 + - name: Build & Push Docker image (ECR) + if: github.event_name != 'pull_request' run: | docker build -t "$IMAGE_URI" . - - - name: Push Docker image - run: | docker push "$IMAGE_URI" - - name: Tag image as latest (only on main) - if: github.ref == 'refs/heads/main' + - name: Tag & Push latest (ECR) + if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main' run: | docker tag "${IMAGE_BASE}:${IMAGE_TAG}" "${IMAGE_BASE}:latest" docker push "${IMAGE_BASE}:latest" - - name: Update Kubernetes manifest image tag (only on main) - if: github.ref == 'refs/heads/main' - env: - YAML_PATH: k8s/aws/20-petclinic-Deployments-postgre.yaml + - name: Inject APP_VERSION into EKS Deployment + if: github.event_name != 'pull_request' run: | - test -f "$YAML_PATH" || (echo "Missing manifest: $YAML_PATH" && exit 1) - echo "Updating $YAML_PATH -> ${IMAGE_URI}" - grep -n "image:" "$YAML_PATH" || (echo "No image field found in $YAML_PATH" && exit 1) - sed -i "s#^\(\s*image:\s*\).*#\1${IMAGE_URI}#" "$YAML_PATH" + # Assumes your Deployment already contains: + # - name: APP_VERSION + # value: "local" + sudo snap install yq --channel=v4/stable + yq -i ' + (.spec.template.spec.containers[].env[] + | select(.name == "APP_VERSION").value) = "'"${APP_VERSION}"'" + ' k8s/aws/20-petclinic-Deployments-postgre.yaml - - name: Commit and push changes (only on main) - if: github.ref == 'refs/heads/main' + - name: Inject image tag into EKS Deployment + if: github.event_name != 'pull_request' run: | - git config user.name "github-actions" - git config user.email "github-actions@github.com" - git add src/main/resources/application.properties k8s/aws/20-petclinic-Deployments-postgre.yaml - - if git diff --cached --quiet; then - echo "No changes to commit" - exit 0 - fi - - git commit -m "chore: update petclinic image to ${APP_VERSION} [skip ci]" - - # --- Sync with remote main then push (avoid non-fast-forward) --- - for i in 1 2 3; do - echo "Push attempt ${i}..." - git fetch origin main - - # Rebase our commit on top of latest main - git rebase origin/main || { git rebase --abort; exit 1; } - - # Push - if git push origin HEAD:main; then - echo "Push succeeded." - exit 0 - fi - - echo "Push failed; retrying after short delay..." - sleep 2 - done - - echo "Push failed after retries." - exit 1 + sudo snap install yq --channel=v4/stable + yq -i ' + (.spec.template.spec.containers[].image) = "'"${IMAGE_URI}"'" + ' k8s/aws/20-petclinic-Deployments-postgre.yaml diff --git a/.github/workflows/ci-petclinic-gke.yaml b/.github/workflows/ci-petclinic-gke.yaml index 5038fdfdc..4eb0db970 100644 --- a/.github/workflows/ci-petclinic-gke.yaml +++ b/.github/workflows/ci-petclinic-gke.yaml @@ -6,21 +6,23 @@ on: pull_request: branches: ["main"] +# Queue (do not cancel) so EKS/GKE GitOps commits don't race each other +concurrency: + group: gitops-main + cancel-in-progress: false + env: GCP_PROJECT_ID: ${{ vars.GCP_PROJECT_ID }} GAR_LOCATION: ${{ vars.GAR_LOCATION || 'asia-northeast3' }} GAR_REPOSITORY: ${{ vars.GAR_REPOSITORY || 'petclinic' }} IMAGE_NAME: petclinic + YAML_PATH: k8s/gcp/20-petclinic-Deployments-postgre.yaml jobs: - build-and-push-gke: + build-test: runs-on: ubuntu-latest - if: github.actor != 'github-actions[bot]' - permissions: - id-token: write contents: read - steps: - name: Checkout uses: actions/checkout@v4 @@ -32,35 +34,58 @@ jobs: java-version: "25" cache: maven - - name: Maven build + - name: Maven build & test run: | if [ -x "./mvnw" ]; then - ./mvnw -B clean package + ./mvnw -B clean test package else - mvn -B clean package + mvn -B clean test package fi - # ---- Push-related steps: only on main pushes (not PR) ---- - - name: Authenticate to Google Cloud - if: github.event_name != 'pull_request' + publish-and-gitops: + needs: build-test + runs-on: ubuntu-latest + + # Only publish/commit on main pushes (never on PRs) + if: github.event_name == 'push' && github.ref == 'refs/heads/main' && github.actor != 'github-actions[bot]' + + permissions: + id-token: write + contents: write + + steps: + - name: Checkout (with token for push) + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up JDK + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: "25" + cache: maven + + - name: Authenticate to Google Cloud (WIF) uses: google-github-actions/auth@v2 with: workload_identity_provider: ${{ secrets.GCP_WIF_PROVIDER }} - service_account: ${{ secrets.GCP_SA_EMAIL }} + service_account: ${{ secrets.GCP_SERVICE_ACCOUNT }} - name: Set up gcloud - if: github.event_name != 'pull_request' uses: google-github-actions/setup-gcloud@v2 - name: Configure Docker for GAR - if: github.event_name != 'pull_request' run: | gcloud auth configure-docker ${GAR_LOCATION}-docker.pkg.dev --quiet - - name: Build APP_VERSION (GCP) - if: github.event_name != 'pull_request' + - name: Compute IMAGE_TAG / IMAGE_URI / APP_VERSION (GCP) run: | - SHORT_SHA=${GITHUB_SHA::7} + set -euo pipefail + + IMAGE_TAG="${GITHUB_SHA::7}" + echo "IMAGE_TAG=${IMAGE_TAG}" >> "$GITHUB_ENV" + BASE_VERSION=$( if [ -x "./mvnw" ]; then ./mvnw -q -Dexpression=project.version -DforceStdout help:evaluate @@ -68,45 +93,75 @@ jobs: mvn -q -Dexpression=project.version -DforceStdout help:evaluate fi ) - BASE_VERSION=${BASE_VERSION%-SNAPSHOT} - APP_VERSION="G-${BASE_VERSION}-${SHORT_SHA}" + BASE_VERSION="${BASE_VERSION%-SNAPSHOT}" + echo "BASE_VERSION=${BASE_VERSION}" >> "$GITHUB_ENV" + + APP_VERSION="G-${BASE_VERSION}-${IMAGE_TAG}" echo "APP_VERSION=${APP_VERSION}" >> "$GITHUB_ENV" - - name: Build image URI - if: github.event_name != 'pull_request' - run: | - IMAGE_TAG="${GITHUB_SHA::7}" - IMAGE_URI="${GAR_LOCATION}-docker.pkg.dev/${GCP_PROJECT_ID}/${GAR_REPOSITORY}/${IMAGE_NAME}:${IMAGE_TAG}" - IMAGE_LATEST="${GAR_LOCATION}-docker.pkg.dev/${GCP_PROJECT_ID}/${GAR_REPOSITORY}/${IMAGE_NAME}:latest" - echo "IMAGE_TAG=${IMAGE_TAG}" >> "$GITHUB_ENV" + IMAGE_BASE="${GAR_LOCATION}-docker.pkg.dev/${GCP_PROJECT_ID}/${GAR_REPOSITORY}/${IMAGE_NAME}" + IMAGE_URI="${IMAGE_BASE}:${IMAGE_TAG}" + echo "IMAGE_BASE=${IMAGE_BASE}" >> "$GITHUB_ENV" echo "IMAGE_URI=${IMAGE_URI}" >> "$GITHUB_ENV" - echo "IMAGE_LATEST=${IMAGE_LATEST}" >> "$GITHUB_ENV" - - name: Build & Push Docker image (GAR) - if: github.event_name != 'pull_request' + echo "IMAGE_URI=${IMAGE_URI}" + echo "APP_VERSION=${APP_VERSION}" + + - name: Build & Push image (GAR) + env: + DOCKER_BUILDKIT: 1 run: | + set -euo pipefail docker build -t "$IMAGE_URI" . docker push "$IMAGE_URI" - name: Tag & Push latest (GAR) - if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main' run: | - docker tag "$IMAGE_URI" "$IMAGE_LATEST" - docker push "$IMAGE_LATEST" + set -euo pipefail + docker tag "${IMAGE_BASE}:${IMAGE_TAG}" "${IMAGE_BASE}:latest" + docker push "${IMAGE_BASE}:latest" - - name: Inject APP_VERSION into GKE Deployment - if: github.event_name != 'pull_request' + - name: Update GKE manifest (image + APP_VERSION) run: | - sudo snap install yq --channel=v4/stable - yq -i ' - (.spec.template.spec.containers[].env[] - | select(.name == "APP_VERSION").value) = "'"${APP_VERSION}"'" - ' k8s/gcp/20-petclinic-Deployments-postgre.yaml + set -euo pipefail + test -f "$YAML_PATH" || { echo "Missing manifest: $YAML_PATH"; exit 1; } - - name: Inject image tag into GKE Deployment - if: github.event_name != 'pull_request' - run: | sudo snap install yq --channel=v4/stable - yq -i ' - (.spec.template.spec.containers[].image) = "'"${IMAGE_URI}"'" - ' k8s/gcp/20-petclinic-Deployments-postgre.yaml + + yq -i '(.spec.template.spec.containers[] | select(.name == "petclinic-container") | .image) = strenv(IMAGE_URI)' "$YAML_PATH" + + yq -i '(.spec.template.spec.containers[] | select(.name == "petclinic-container") | .env[] | select(.name == "APP_VERSION").value) = strenv(APP_VERSION)' "$YAML_PATH" + + echo "Updated $YAML_PATH" + yq '.spec.template.spec.containers[] | select(.name == "petclinic-container") | {image, env}' "$YAML_PATH" + + - name: Commit & push GitOps change (GKE manifest only) + run: | + set -euo pipefail + + git config user.name "github-actions" + git config user.email "github-actions@github.com" + + git add "$YAML_PATH" + + if git diff --cached --quiet; then + echo "No manifest changes to commit." + exit 0 + fi + + git commit -m "chore(gke): deploy ${IMAGE_TAG} (APP_VERSION=${APP_VERSION}) [skip ci]" + + for i in 1 2 3 4 5; do + echo "Push attempt ${i}..." + git fetch origin main + git rebase origin/main + if git push origin HEAD:main; then + echo "Push succeeded." + exit 0 + fi + echo "Push failed; retrying..." + sleep 2 + done + + echo "Push failed after retries." + exit 1 diff --git a/.github/workflows/ci-petclinic-gke.yaml.bak b/.github/workflows/ci-petclinic-gke.yaml.bak index f3ca00206..980d32ed7 100644 --- a/.github/workflows/ci-petclinic-gke.yaml.bak +++ b/.github/workflows/ci-petclinic-gke.yaml.bak @@ -1,18 +1,11 @@ name: CI - Petclinic GKE -permissions: - contents: read - on: push: branches: ["main"] pull_request: branches: ["main"] -concurrency: - group: ci-${{ github.ref }} - cancel-in-progress: false - env: GCP_PROJECT_ID: ${{ vars.GCP_PROJECT_ID }} GAR_LOCATION: ${{ vars.GAR_LOCATION || 'asia-northeast3' }} @@ -20,51 +13,17 @@ env: IMAGE_NAME: ${{ vars.IMAGE_NAME || 'petclinic' }} jobs: - build-test: + build-and-push-gke: + runs-on: ubuntu-latest if: github.actor != 'github-actions[bot]' - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Set up JDK - uses: actions/setup-java@v4 - with: - distribution: temurin - java-version: "25" - cache: maven - - - name: Maven build & test - run: | - if [ -x "./mvnw" ]; then - ./mvnw -B clean test package - else - mvn -B clean test package - fi - - - name: Archive built JAR - uses: actions/upload-artifact@v4 - with: - name: petclinic-jar - path: target/*.jar - - build-and-push-image: - needs: build-test - runs-on: ubuntu-latest - - if: github.event_name != 'pull_request' && github.actor != 'github-actions[bot]' permissions: - contents: write id-token: write + contents: read steps: - name: Checkout uses: actions/checkout@v4 - with: - fetch-depth: 0 - name: Set up JDK uses: actions/setup-java@v4 @@ -73,100 +32,81 @@ jobs: java-version: "25" cache: maven - - name: Auth to Google Cloud (WIF) + - name: Maven build + run: | + if [ -x "./mvnw" ]; then + ./mvnw -B clean package + else + mvn -B clean package + fi + + # ---- Push-related steps: only on main pushes (not PR) ---- + - name: Authenticate to Google Cloud + if: github.event_name != 'pull_request' uses: google-github-actions/auth@v2 with: workload_identity_provider: ${{ secrets.GCP_WIF_PROVIDER }} service_account: ${{ secrets.GCP_SA_EMAIL }} - name: Set up gcloud + if: github.event_name != 'pull_request' uses: google-github-actions/setup-gcloud@v2 - - name: Configure docker for Artifact Registry + - name: Configure Docker for GAR + if: github.event_name != 'pull_request' run: | gcloud auth configure-docker ${GAR_LOCATION}-docker.pkg.dev --quiet - - name: Set image tag + - name: Build APP_VERSION (GCP) + if: github.event_name != 'pull_request' run: | SHORT_SHA=${GITHUB_SHA::7} - echo "IMAGE_TAG=${SHORT_SHA}" >> "$GITHUB_ENV" - - - name: Read base version from pom.xml - run: | - if [ -x "./mvnw" ]; then - POM_VERSION=$(./mvnw -q -Dexpression=project.version -DforceStdout help:evaluate) - else - POM_VERSION=$(mvn -q -Dexpression=project.version -DforceStdout help:evaluate) - fi - BASE_VERSION=${POM_VERSION%-SNAPSHOT} - echo "BASE_VERSION=${BASE_VERSION}" >> "$GITHUB_ENV" - - - name: Build APP_VERSION - run: | - APP_VERSION="G-${BASE_VERSION}-${IMAGE_TAG}" + BASE_VERSION=$( + if [ -x "./mvnw" ]; then + ./mvnw -q -Dexpression=project.version -DforceStdout help:evaluate + else + mvn -q -Dexpression=project.version -DforceStdout help:evaluate + fi + ) + BASE_VERSION=${BASE_VERSION%-SNAPSHOT} + APP_VERSION="G-${BASE_VERSION}-${SHORT_SHA}" echo "APP_VERSION=${APP_VERSION}" >> "$GITHUB_ENV" - - name: Update application version property - run: | - sed -i "s/^app.version=.*/app.version=${APP_VERSION}/" src/main/resources/application.properties - - - name: Build Docker image - env: - DOCKER_BUILDKIT: 1 + - name: Build image URI + if: github.event_name != 'pull_request' run: | + IMAGE_TAG="${GITHUB_SHA::7}" IMAGE_URI="${GAR_LOCATION}-docker.pkg.dev/${GCP_PROJECT_ID}/${GAR_REPOSITORY}/${IMAGE_NAME}:${IMAGE_TAG}" + IMAGE_LATEST="${GAR_LOCATION}-docker.pkg.dev/${GCP_PROJECT_ID}/${GAR_REPOSITORY}/${IMAGE_NAME}:latest" + echo "IMAGE_TAG=${IMAGE_TAG}" >> "$GITHUB_ENV" echo "IMAGE_URI=${IMAGE_URI}" >> "$GITHUB_ENV" - docker build -t "$IMAGE_URI" . + echo "IMAGE_LATEST=${IMAGE_LATEST}" >> "$GITHUB_ENV" - - name: Push Docker image + - name: Build & Push Docker image (GAR) + if: github.event_name != 'pull_request' run: | + docker build -t "$IMAGE_URI" . docker push "$IMAGE_URI" - - name: Tag image as latest - if: github.ref == 'refs/heads/main' + - name: Tag & Push latest (GAR) + if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main' run: | - IMAGE_BASE="${GAR_LOCATION}-docker.pkg.dev/${GCP_PROJECT_ID}/${GAR_REPOSITORY}/${IMAGE_NAME}" - docker tag "${IMAGE_BASE}:${IMAGE_TAG}" "${IMAGE_BASE}:latest" - docker push "${IMAGE_BASE}:latest" + docker tag "$IMAGE_URI" "$IMAGE_LATEST" + docker push "$IMAGE_LATEST" - - name: Update Kubernetes manifest image tag - if: github.ref == 'refs/heads/main' - env: - YAML_PATH: k8s/gcp/20-petclinic-Deployments-postgre.yaml + - name: Inject APP_VERSION into GKE Deployment + if: github.event_name != 'pull_request' run: | - NEW_IMAGE="${GAR_LOCATION}-docker.pkg.dev/${GCP_PROJECT_ID}/${GAR_REPOSITORY}/${IMAGE_NAME}:${IMAGE_TAG}" - grep -n "image:" "$YAML_PATH" - sed -i "s#^\(\s*image:\s*\).*#\1${NEW_IMAGE}#" "$YAML_PATH" - - - name: Commit and push changes - if: github.ref == 'refs/heads/main' + sudo snap install yq --channel=v4/stable + yq -i ' + (.spec.template.spec.containers[].env[] + | select(.name == "APP_VERSION").value) = "'"${APP_VERSION}"'" + ' k8s/gcp/20-petclinic-Deployments-postgre.yaml + + - name: Inject image tag into GKE Deployment + if: github.event_name != 'pull_request' run: | - git config user.name "github-actions" - git config user.email "github-actions@github.com" - git add src/main/resources/application.properties k8s/gcp/20-petclinic-Deployments-postgre.yaml - if git diff --cached --quiet; then - echo "No changes to commit" - exit 0 - fi - git commit -m "chore: update petclinic image to ${IMAGE_TAG} [skip ci]" - - # --- Sync with remote main then push (avoid non-fast-forward) --- - for i in 1 2 3; do - echo "Push attempt ${i}..." - git fetch origin main - - # Rebase our commit on top of latest main - git rebase origin/main || { git rebase --abort; exit 1; } - - # Push - if git push origin HEAD:main; then - echo "Push succeeded." - exit 0 - fi - - echo "Push failed; retrying after short delay..." - sleep 2 - done - - echo "Push failed after retries." - exit 1 + sudo snap install yq --channel=v4/stable + yq -i ' + (.spec.template.spec.containers[].image) = "'"${IMAGE_URI}"'" + ' k8s/gcp/20-petclinic-Deployments-postgre.yaml