From 29bb7b4aac32f79a29bcf007ed362c39dc1195d6 Mon Sep 17 00:00:00 2001 From: Gergely Nagy Date: Mon, 27 May 2024 11:46:45 +0200 Subject: [PATCH] Github actions WIP --- .github/actions/argocd-deploy/action.yml | 55 +++++++++++++++ .github/actions/docker-build/action.yml | 51 ++++++++++++++ .github/actions/ecr-auth/action.yml | 30 ++++++++ .github/actions/ecr-push/action.yml | 64 +++++++++++++++++ .github/actions/gradle-build/action.yml | 32 +++++++++ .github/actions/run-app-background/action.yml | 57 +++++++++++++++ .github/workflows/deploy-spring-webmvc.yml | 70 +++++++++++++++++++ 7 files changed, 359 insertions(+) create mode 100644 .github/actions/argocd-deploy/action.yml create mode 100644 .github/actions/docker-build/action.yml create mode 100644 .github/actions/ecr-auth/action.yml create mode 100644 .github/actions/ecr-push/action.yml create mode 100644 .github/actions/gradle-build/action.yml create mode 100644 .github/actions/run-app-background/action.yml create mode 100644 .github/workflows/deploy-spring-webmvc.yml diff --git a/.github/actions/argocd-deploy/action.yml b/.github/actions/argocd-deploy/action.yml new file mode 100644 index 0000000..50a7789 --- /dev/null +++ b/.github/actions/argocd-deploy/action.yml @@ -0,0 +1,55 @@ +name: Deploy app with Argo CD +description: Sets tag to Argo CD app and syncs it + +inputs: + argocd-url: + required: true + description: 'URL for ArgoCD server' + argocd-user: + required: true + description: 'User name for logging in to ArgoCD' + argocd-password: + required: true + description: 'Password for logging in to ArgoCD' + argocd-app-name: + required: true + description: 'Argo CD app name' + image-tag: + required: true + description: 'ECR image tag' + +runs: + using: composite + steps: + - name: Install ArgoCD CLI + shell: bash + run: | + # get latest release version + version=$(git ls-remote --tags https://github.com/argoproj/argo-cd | cut -f 2 | awk -F/ '{print $NF}' | sort -V | tail -n 1) + echo "Argo CD CLI version: ${version}" + + # download the binary and make it executable + curl -L -o /usr/local/bin/argocd https://github.com/argoproj/argo-cd/releases/download/${version}/argocd-linux-amd64 + chmod +x /usr/local/bin/argocd + + # test + which argocd + + - name: Log in to ArgoCD + shell: bash + run: | + echo argocd login --grpc-web --username ${{ inputs.argocd-user }} --password ${{ inputs.argocd-password }} ${{ inputs.argocd-url }} + argocd login --grpc-web --username ${{ inputs.argocd-user }} --password ${{ inputs.argocd-password }} ${{ inputs.argocd-url }} + + - name: Update image tag in ArgoCD + shell: bash + run: | + # setup env + imageVersion=$(echo ${{ inputs.image-tag }} | cut -d ':' -f 2) + echo "App name: ${{ inputs.argocd-app-name }}" + echo "Image version: ${imageVersion}" + + # do the app work! + argocd --grpc-web app set ${{ inputs.argocd-app-name }} -p image.tag=${imageVersion} + argocd --grpc-web app sync ${{ inputs.argocd-app-name }} + argocd --grpc-web app wait ${{ inputs.argocd-app-name }} diff --git a/.github/actions/docker-build/action.yml b/.github/actions/docker-build/action.yml new file mode 100644 index 0000000..4dbc6d8 --- /dev/null +++ b/.github/actions/docker-build/action.yml @@ -0,0 +1,51 @@ +name: Build and push Docker image with Gradle +description: Logs into ECR, then builds, tags and pushes Docker image using Gradle + +inputs: + image-name: + required: true + description: 'Docker image name to build - in other words AWS repository name' + +runs: + using: composite + steps: + - name: Setup Docker BuildX + uses: docker/setup-buildx-action@v2 + +# Disable caching because it is not used by `gradle bootBuildImage`; TODO: revisit later maybe it can be made to work +# See also: +# https://github.com/spring-projects/spring-boot/issues/28386 +# https://github.com/docker/build-push-action/issues/538 +# +# +# - name: Cache local Docker registry +# uses: actions/cache@v3 +# with: +# path: /tmp/docker-registry +# key: docker-registry-${{ runner.os }}-${{ hashFiles(inputs.dockerfile) }} +# +# - name: Run local Docker registry +# shell: bash +# run: >- +# docker run +# --detach +# --publish 5000:5000 +# --restart=always +# --name registry +# --volume /tmp/docker-registry:/var/lib/registry +# registry:2 +# +# - name: Pull image cache +# shell: bash +# run: >- +# docker pull localhost:5000/${{ env.imageName }} && +# docker image tag localhost:5000/${{ env.imageName }} ${{ env.imageName }} || true + + - name: Build Docker image with Gradle + uses: gradle/gradle-build-action@v2 + with: + build-root-directory: ${{ env.WORKDIR }} + arguments: >- + bootBuildImage + --imageName ${{ inputs.image-name }} + diff --git a/.github/actions/ecr-auth/action.yml b/.github/actions/ecr-auth/action.yml new file mode 100644 index 0000000..849320b --- /dev/null +++ b/.github/actions/ecr-auth/action.yml @@ -0,0 +1,30 @@ +name: Log into ECR +description: Logs into ECR + +inputs: + aws-region: + required: true + description: 'AWS region to deploy into' + aws-role: + required: true + description: 'ARN of the role to push to ECR' + +outputs: + registry: + value: ${{ steps.login-ecr.outputs.registry }} + description: 'Docker registry' + +runs: + using: composite + steps: + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v1-node16 + with: + aws-region: ${{ inputs.aws-region }} + role-to-assume: ${{ inputs.aws-role }} + mask-aws-account-id: false + + - name: Login to Amazon ECR + id: login-ecr + uses: aws-actions/amazon-ecr-login@v1 + #outputs: ${{ steps.login-ecr.outputs.registry }} diff --git a/.github/actions/ecr-push/action.yml b/.github/actions/ecr-push/action.yml new file mode 100644 index 0000000..99336ca --- /dev/null +++ b/.github/actions/ecr-push/action.yml @@ -0,0 +1,64 @@ +name: Build and push Docker image with Gradle +description: Logs into ECR, then builds, tags and pushes Docker image using Gradle + +inputs: + image-name: + required: true + description: 'Docker image name to build - in other words AWS repository name' + aws-region: + required: true + description: 'AWS region to deploy into' + aws-role: + required: true + description: 'ARN of the role to push to ECR' + +outputs: + image-tag: + value: ${{ steps.login-ecr.outputs.registry }}/${{ inputs.image-name }}:${{ steps.docker-tags.outputs.version }} + description: 'Complete Docker image tag' + +runs: + using: composite + steps: + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v1-node16 + with: + aws-region: ${{ inputs.aws-region }} + role-to-assume: ${{ inputs.aws-role }} + mask-aws-account-id: false + + - name: Login to Amazon ECR + id: login-ecr + uses: aws-actions/amazon-ecr-login@v1 + #outputs: ${{ steps.login-ecr.outputs.registry }} + + - name: Set Docker tags + id: docker-tags + uses: docker/metadata-action@v4 + with: + images: ${{ inputs.image-name }} + tags: | + type=sha,priority=1100 + type=ref,event=branch + sep-tags: ' ' + + # we don't use push --all-tags since we don't always want to push also latest tag + - name: Tag and push Docker images + shell: bash + run: | + echo "## Pushed docker images" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + for tag in ${{ steps.docker-tags.outputs.tags }}; do + docker image tag ${{ inputs.image-name }} ${{ steps.login-ecr.outputs.registry }}/${tag} + docker image push ${{ steps.login-ecr.outputs.registry }}/${tag} + echo "- ``${{ steps.login-ecr.outputs.registry }}/${tag}``" >> $GITHUB_STEP_SUMMARY + done + +# Disable caching because it is not used by `gradle bootBuildImage`; TODO: revisit later maybe it can be made to work +# +# - name: Push image cache +# if: steps.cache.outputs.cache-hit != 'true' +# shell: bash +# run: >- +# docker tag ${{ inputs.image-name }} localhost:5000/${{ inputs.image-name }} && +# docker push localhost:5000/${{ inputs.image-name }} || true diff --git a/.github/actions/gradle-build/action.yml b/.github/actions/gradle-build/action.yml new file mode 100644 index 0000000..03b9edb --- /dev/null +++ b/.github/actions/gradle-build/action.yml @@ -0,0 +1,32 @@ +name: Build +description: Build a Spring Boot application with Gradle + +runs: + using: composite + steps: + - name: Setup Java ${{ env.JAVA_VERSION }}-${{ env.JAVA_DISTRIBUTION }} + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'temurin' + cache: gradle + + - name: Validate Gradle wrapper against SHA-256 checksums of official releases + uses: gradle/wrapper-validation-action@v1 + + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 + with: + build-root-directory: ${{ env.WORKDIR }} + + - name: Ktlint Check + uses: gradle/gradle-build-action@v2 + with: + arguments: ktlintCheck + build-root-directory: ${{ env.WORKDIR }} + + - name: Build and Test + uses: gradle/gradle-build-action@v2 + with: + arguments: build + build-root-directory: ${{ env.WORKDIR }} diff --git a/.github/actions/run-app-background/action.yml b/.github/actions/run-app-background/action.yml new file mode 100644 index 0000000..09b809b --- /dev/null +++ b/.github/actions/run-app-background/action.yml @@ -0,0 +1,57 @@ +name: Run app in a background +description: Run application in docker container in a background + +inputs: + network: + required: true + description: 'Network to run the container in' + app-image: + required: true + description: 'Image to run' + postgres-host: + required: true + description: 'Postgres host' + +runs: + using: composite + steps: + - name: Run mockserver in background + uses: gacts/run-and-post-run@v1 + with: + run: docker run --rm -d + --network "${{ inputs.network }}" + --name mock + -v "${{ github.workspace }}/docker/bb-backend/mockserver:/config:ro" + jordimartin/mmock:v3.1.5 -server-statistics=false -console=false -server-port 8080 + post: docker stop mock + + - name: Run app in a background + uses: gacts/run-and-post-run@v1 + with: + run: docker run --rm -d + --network "${{ inputs.network }}" + --name app + -e "TM_CORE_URL=http://mock:8080" + -e "POSTGRES_HOST=postgres" + ${{ inputs.app-image }} + post: docker stop app + + - name: Wait app up and running + shell: bash + run: | + echo "Waiting a MMock..." + docker run --rm --network "${{ inputs.network }}" curlimages/curl \ + --output /dev/null \ + --head -X GET --retry 20 \ + --silent \ + --retry-all-errors --retry-delay 1 \ + http://mock:8080/live + + echo "MMock is ready! waiting app.." + + docker run --rm --network "${{ inputs.network }}" curlimages/curl \ + --output /dev/null \ + --head -X GET --retry 20 \ + --silent \ + --retry-all-errors --retry-delay 1 \ + http://app:8083/actuator/health/readiness diff --git a/.github/workflows/deploy-spring-webmvc.yml b/.github/workflows/deploy-spring-webmvc.yml new file mode 100644 index 0000000..71b3fe3 --- /dev/null +++ b/.github/workflows/deploy-spring-webmvc.yml @@ -0,0 +1,70 @@ +name: spring-webmvc - Build and Deploy + +on: + push: + paths: + - spring-webmvc/** + - .github/workflows/deploy-spring-webmvc.yml + workflow_dispatch: # Enables triggering the workflow manually from the Actions tab + +permissions: + id-token: write + contents: read + checks: write + +env: + WORKDIR: spring-webmvc + IMAGE_NAME: spring-webmvc + +#defaults: +# run: +# shell: bash +# working-directory: ${{ env.WORKDIR }} + +jobs: + build: + name: Build and Push + runs-on: ubuntu-latest + outputs: + pushed-image-tag: ${{ steps.ecr-push.outputs.image-tag }} + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Gradle Build + uses: ./.github/actions/gradle-build + + - name: Docker Build + uses: ./.github/actions/docker-build + with: + image-name: ${{ env.IMAGE_NAME }} + + - name: ECR Push + id: ecr-push + uses: ./.github/actions/ecr-push + with: + image-name: ${{ env.IMAGE_NAME }} + aws-role: ${{ vars.AWS_ROLE }} + aws-region: ${{ vars.AWS_REGION }} + + deploy: + name: Deploy with ArgoCD + runs-on: ubuntu-latest + environment: dev + needs: build + if: success() && github.ref == 'refs/heads/master' + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Deploy + id: argocd-deploy + uses: ./.github/actions/argocd-deploy + with: + argocd-url: ${{ vars.ARGOCD_URL }} + argocd-user: ${{ vars.ARGOCD_USER }} + argocd-password: ${{ secrets.ARGOCD_PASSWORD }} + argocd-app-name: ${{ env.IMAGE_NAME }} + image-tag: ${{ needs.build.outputs.pushed-image-tag }}