From 31d564ca5fa0c723592b1a6aadae3929b2e8abdb Mon Sep 17 00:00:00 2001 From: Frecherenkel60 Date: Thu, 1 Feb 2024 17:56:19 +0100 Subject: [PATCH] organize pipelines with reusable workflows --- .github/workflows/build-push-image.yaml | 48 +++++ .github/workflows/cd.yaml | 91 ++------- .github/workflows/ci.yaml | 1 + .github/workflows/comment-pr.yaml | 61 ++++++ .github/workflows/deploy.yaml | 79 ++++++++ .github/workflows/pr-cleanup.yaml | 1 + .github/workflows/pr-deploy.yaml | 255 ++++++------------------ 7 files changed, 272 insertions(+), 264 deletions(-) create mode 100644 .github/workflows/build-push-image.yaml create mode 100644 .github/workflows/comment-pr.yaml create mode 100644 .github/workflows/deploy.yaml diff --git a/.github/workflows/build-push-image.yaml b/.github/workflows/build-push-image.yaml new file mode 100644 index 0000000..3e9cabc --- /dev/null +++ b/.github/workflows/build-push-image.yaml @@ -0,0 +1,48 @@ +name: Build and push Docker Image to GitHub Container Registry +run-name: Build&Push Image of ${{ github.event.inputs.preview == 'true' && 'Preview' || 'Main' }} to GitHub Container Registry + +on: + workflow_call: + inputs: + preview: + required: true + type: boolean + secrets: + GITHUB_TOKEN: + required: true + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + +jobs: + build-push-docker: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Get Docker metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + ${{ github.event.inputs.preview == 'true' && 'type=ref,enable=true,priority=600,prefix=pr-,suffix=,event=pr' || ''}} + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: . + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + file: ./build/package/Dockerfile diff --git a/.github/workflows/cd.yaml b/.github/workflows/cd.yaml index fbf9af1..36758af 100644 --- a/.github/workflows/cd.yaml +++ b/.github/workflows/cd.yaml @@ -1,4 +1,5 @@ name: Continuous Delivery +run-name: Deploy to Production on: push: @@ -9,74 +10,26 @@ env: REGISTRY: ghcr.io IMAGE_NAME: ${{ github.repository }} +permissions: + contents: read + packages: write + jobs: build-push-docker: - runs-on: ubuntu-latest - - permissions: - contents: read - packages: write - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Login to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Get Docker metadata - id: meta - uses: docker/metadata-action@v5 - with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - - - name: Build and push Docker image - uses: docker/build-push-action@v5 - with: - context: . - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - file: ./build/package/Dockerfile - - - name: Copy docker-compose prod file to server - uses: appleboy/scp-action@v0.1.7 - with: - host: ${{ secrets.DEPLOY_HOST }} - username: ${{ secrets.DEPLOY_USER }} - key: ${{ secrets.DEPLOY_SSH_KEY }} - port: ${{ secrets.DEPLOY_PORT }} - source: "deployments/docker-compose.prod.yml,deployments/server_alpha_db.sql,deployments/provisioning/*,configs/*.yaml" - target: "/home/serveralpha" - - - name: Setup SSH - uses: appleboy/ssh-action@v1.0.3 - with: - host: ${{ secrets.DEPLOY_HOST }} - username: ${{ secrets.DEPLOY_USER }} - key: ${{ secrets.DEPLOY_SSH_KEY }} - port: ${{ secrets.DEPLOY_PORT }} - script: | - set_key_value() { - key="$1" - value="$2" - file="$3" - - if grep -q "^$key=" "$file"; then - sed -i "s/^$key=.*/$key=$value/" "$file" - else - echo "$key=$value" >> "$file" - fi - } - - set_key_value "SERVER_IMAGE" "ghcr.io/wwi21seb-projekt/server-alpha:main" ".env" - set_key_value "TRAFFIC_RULE" "Host(\`server-alpha.tech\`)" ".env" - set_key_value "MONITORING_TRAFFIC_RULE" "Host(\`monitoring.server-alpha.tech\`)" ".env" - - docker pull ${{ steps.meta.outputs.tags }} - docker-compose -f deployments/docker-compose.prod.yml -p server_alpha down - docker-compose -f deployments/docker-compose.prod.yml -p server_alpha up -d + uses: ./.github/workflows/build-push-image.yaml + with: + preview: false + secrets: inherit + + deploy: + needs: build-push-docker + uses: ./.github/workflows/pr-cleanup.yaml + with: + preview: false + docker-image: ${{ steps.meta.outputs.tags }} + compose-name: "server_alpha" + ssh-script: | + set_key_value "SERVER_IMAGE" "ghcr.io/wwi21seb-projekt/server-alpha:main" ".env" + set_key_value "TRAFFIC_RULE" "Host(\`server-alpha.tech\`)" ".env" + set_key_value "MONITORING_TRAFFIC_RULE" "Host(\`monitoring.server-alpha.tech\`)" ".env" + secrets: inherit diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 71778b1..faf745b 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -1,4 +1,5 @@ name: Continuous Integration +run-name: Continuous Integration on: pull_request: diff --git a/.github/workflows/comment-pr.yaml b/.github/workflows/comment-pr.yaml new file mode 100644 index 0000000..48fbfb8 --- /dev/null +++ b/.github/workflows/comment-pr.yaml @@ -0,0 +1,61 @@ +name: 'Comment on PR' +run-name: 'Comment on PR' + +on: + workflow_call: + inputs: + message: + required: true + type: string + secrets: + GITHUB_TOKEN: + required: true + +jobs: + comment: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Comment PR + uses: actions/github-script@v7 + with: + github-token: ${{secrets.GITHUB_TOKEN}} + script: | + const issue_number = context.issue.number; + const owner = context.repo.owner; + const repo = context.repo.repo; + const message = ` + ${{ github.event.inputs.message }} + `; + + // Fetch all comments + const comments = await github.rest.issues.listComments({ + owner: owner, + repo: repo, + issue_number: issue_number + }); + + // Find the previous bot comment + const botComment = comments.data.find(comment => comment.user.login === 'github-actions[bot]'); + + // If it exists, update it + if (botComment) { + await github.rest.issues.updateComment({ + owner: owner, + repo: repo, + comment_id: botComment.id, + body: message + }); + } + // Else, create a new one + else { + const comment = await github.rest.issues.createComment({ + owner: owner, + repo: repo, + issue_number: issue_number, + body: message + }); + } \ No newline at end of file diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml new file mode 100644 index 0000000..2ab1552 --- /dev/null +++ b/.github/workflows/deploy.yaml @@ -0,0 +1,79 @@ +name: Deploy to Server +run-name: ${{ github.event.inputs.preview == 'true' && 'Deploy to Preview' || 'Deploy to Production' }} + +on: + workflow_call: + inputs: + preview: + required: true + type: boolean + description: 'Whether to deploy to the preview domain or not' + ssh-script: + required: true + type: string + description: 'The script to run on the server to set the environment variables' + docker-image: + required: true + type: string + description: 'The docker image to use for the server' + compose-name: + required: true + type: string + description: 'The name of the docker-compose project to use' + secrets: + DEPLOY_HOST: + required: true + description: 'The hostname or IP address of the server to deploy to' + DEPLOY_USER: + required: true + description: 'The username to use for SSH authentication' + DEPLOY_SSH_KEY: + required: true + description: 'The SSH private key to use for authentication' + DEPLOY_PORT: + required: true + description: 'The SSH port to use for authentication' + +jobs: + deploy: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Copy files to server + uses: appleboy/scp-action@v0.1.7 + with: + host: ${{ secrets.DEPLOY_HOST }} + username: ${{ secrets.DEPLOY_USER }} + key: ${{ secrets.DEPLOY_SSH_KEY }} + port: ${{ secrets.DEPLOY_PORT }} + source: "deployments/docker-compose.prod.yml,deployments/server_alpha_db.sql,deployments/provisioning/*,configs/*.yaml" + target: "/home/serveralpha/preview/pr-${{ github.event.pull_request.number }}" + + - name: Setup SSH + uses: appleboy/ssh-action@v1.0.3 + with: + host: ${{ secrets.DEPLOY_HOST }} + username: ${{ secrets.DEPLOY_USER }} + key: ${{ secrets.DEPLOY_SSH_KEY }} + port: ${{ secrets.DEPLOY_PORT }} + script: | + set_key_value() { + key="$1" + value="$2" + file="$3" + + if grep -q "^$key=" "$file"; then + sed -i "s/^$key=.*/$key=$value/" "$file" + else + echo "$key=$value" >> "$file" + fi + } + + ${{ github.event.inputs.ssh-script }} + + docker pull ${{ github.event.inputs.docker-image }} + docker-compose -f deployments/docker-compose.prod.yml -p ${{ github.event.inputs.compose-name }} down + docker-compose -f deployments/docker-compose.prod.yml -p ${{ github.event.inputs.compose-name }} up -d \ No newline at end of file diff --git a/.github/workflows/pr-cleanup.yaml b/.github/workflows/pr-cleanup.yaml index 30989ae..d783c4d 100644 --- a/.github/workflows/pr-cleanup.yaml +++ b/.github/workflows/pr-cleanup.yaml @@ -1,4 +1,5 @@ name: PR Cleanup +run-name: Cleanup PR-${{ github.event.pull_request.number }} Preview on: pull_request: diff --git a/.github/workflows/pr-deploy.yaml b/.github/workflows/pr-deploy.yaml index 0528563..04a0b4d 100644 --- a/.github/workflows/pr-deploy.yaml +++ b/.github/workflows/pr-deploy.yaml @@ -1,4 +1,5 @@ name: PR Deployment +run-name: Deploy PR-${{ github.event.pull_request.number }} to Testing Subdomain on: pull_request: @@ -10,203 +11,67 @@ env: REGISTRY: ghcr.io IMAGE_NAME: ${{ github.repository }} +permissions: + contents: read + packages: write + pull-requests: write + jobs: - preview-deploy: - runs-on: ubuntu-latest + start-comment: + uses: ./.github/workflows/comment-pr.yaml if: github.actor != 'renovate[bot]' # Don't run this workflow for renovate bot + with: + message: | + :rocket: Your changes are being deployed to a preview domain. + secrets: inherit + + preview-build-push-docker: + needs: start-comment + uses: ./.github/workflows/build-push-image.yaml + with: + preview: true + secrets: inherit - permissions: - contents: read - packages: write - pull-requests: write - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Comment PR on Deployment Start - id: comment - uses: actions/github-script@v7 - with: - github-token: ${{secrets.GITHUB_TOKEN}} - script: | - const issue_number = context.issue.number; - const owner = context.repo.owner; - const repo = context.repo.repo; - const message = ` - :construction: Your changes are being deployed to a preview domain... - `; - - // Fetch all comments - const comments = await github.rest.issues.listComments({ - owner: owner, - repo: repo, - issue_number: issue_number - }); - - // Find a specific comment - const botComment = comments.data.find(comment => comment.user.login === 'github-actions[bot]'); - - let comment_id; - - // If it exists, update it - if (botComment) { - comment_id = botComment.id; - await github.rest.issues.updateComment({ - owner: owner, - repo: repo, - comment_id: comment_id, - body: message - }); - } - // Else, create a new one - else { - const comment = await github.rest.issues.createComment({ - owner: owner, - repo: repo, - issue_number: issue_number, - body: message - }); - comment_id = comment.data.id; - } - - core.setOutput('comment_id', comment_id); - - - name: Login to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Get Docker metadata - id: meta - uses: docker/metadata-action@v5 - with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - tags: | - type=ref,enable=true,priority=600,prefix=pr-,suffix=,event=pr - - - name: Build and push Docker image - uses: docker/build-push-action@v5 - with: - context: . - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - file: ./build/package/Dockerfile - - - name: Copy needed files to server - uses: appleboy/scp-action@v0.1.7 - with: - host: ${{ secrets.DEPLOY_HOST }} - username: ${{ secrets.DEPLOY_USER }} - key: ${{ secrets.DEPLOY_SSH_KEY }} - port: ${{ secrets.DEPLOY_PORT }} - source: "deployments/docker-compose.prod.yml,deployments/server_alpha_db.sql,deployments/provisioning/*,configs/*.yaml" - target: "/home/serveralpha/preview/pr-${{ github.event.pull_request.number }}" - - - name: Deploy PR to Testing Subdomain - uses: appleboy/ssh-action@v1.0.3 - with: - host: ${{ secrets.DEPLOY_HOST }} - username: ${{ secrets.DEPLOY_USER }} - key: ${{ secrets.DEPLOY_SSH_KEY }} - port: ${{ secrets.DEPLOY_PORT }} - script: | - cd ./preview/pr-${{ github.event.pull_request.number }} - cp ~/.env .env - mkdir -p ./deployments/keys - - echo "PR_NUMBER=${{ github.event.pull_request.number }}" >> .env - echo "SERVER_IMAGE=ghcr.io/wwi21seb-projekt/server-alpha:pr-${{ github.event.pull_request.number }}" >> .env - echo "TRAFFIC_RULE=Host(\`pr-${{ github.event.pull_request.number }}.preview.server-alpha.tech\`)" >> .env - echo "MONITORING_TRAFFIC_RULE=Host(\`monitoring.pr-${{ github.event.pull_request.number }}.preview.server-alpha.tech\`)" >> .env - - jq --arg pr_number "${{ github.event.pull_request.number }}" ' - .panels[] |= - if .title == "Server-Alpha Logs" then .targets[0].expr = "{container_name=\"pr_" + $pr_number + "_app_1\"}" - elif .title == "Database Logs" then .targets[0].expr = "{container_name=\"pr_" + $pr_number + "_db_1\"}" - else . end - ' deployments/provisioning/dashboards/logs.json > tmp.json && mv tmp.json deployments/provisioning/dashboards/logs.json - - docker pull ${{ steps.meta.outputs.tags }} - docker-compose -f deployments/docker-compose.prod.yml -p pr_${{ github.event.pull_request.number }} down || true - docker-compose -f deployments/docker-compose.prod.yml -p pr_${{ github.event.pull_request.number }} up -d - - - name: Update Comment with Success Message - uses: actions/github-script@v7 - with: - github-token: ${{secrets.GITHUB_TOKEN}} - script: | - const issue_number = context.issue.number; - const owner = context.repo.owner; - const repo = context.repo.repo; - const comment_id = "${{ steps.comment.outputs.comment_id }}"; - const url = `https://pr-${issue_number}.preview.server-alpha.tech`; - const message = ` - :tada: Your changes are live! :rocket: - You can view them under this url: ${url} - Happy reviewing! :eyeglasses: - `; - github.rest.issues.updateComment({ - owner: owner, - repo: repo, - comment_id: comment_id, - body: message - }); + preview-deploy: + needs: preview-build-push-docker + uses: ./.github/workflows/deploy.yaml + with: + preview: true + docker-image: ${{ steps.meta.outputs.tags }} + compose-name: pr_${{ github.event.pull_request.number }} + ssh-script: | + cd ./preview/pr-${{ github.event.pull_request.number }} + cp ~/.env .env + mkdir -p ./deployments/keys + + echo "PR_NUMBER=${{ github.event.pull_request.number }}" >> .env + echo "SERVER_IMAGE=ghcr.io/wwi21seb-projekt/server-alpha:pr-${{ github.event.pull_request.number }}" >> .env + echo "TRAFFIC_RULE=Host(\`pr-${{ github.event.pull_request.number }}.preview.server-alpha.tech\`)" >> .env + echo "MONITORING_TRAFFIC_RULE=Host(\`monitoring.pr-${{ github.event.pull_request.number }}.preview.server-alpha.tech\`)" >> .env + + jq --arg pr_number "${{ github.event.pull_request.number }}" ' + .panels[] |= + if .title == "Server-Alpha Logs" then .targets[0].expr = "{container_name=\"pr_" + $pr_number + "_app_1\"}" + elif .title == "Database Logs" then .targets[0].expr = "{container_name=\"pr_" + $pr_number + "_db_1\"}" + else . end + ' deployments/provisioning/dashboards/logs.json > tmp.json && mv tmp.json deployments/provisioning/dashboards/logs.json + secrets: inherit + + success-comment: + uses: ./.github/workflows/comment-pr.yaml + if: success() + with: + message: | + :white_check_mark: Your changes have been deployed to a preview domain. + You can access the preview at https://pr-${{ github.event.pull_request.number }}.preview.server-alpha.tech + :warning: Please note that the preview will be deleted once the pull request is closed. + secrets: inherit failure-comment: - runs-on: ubuntu-latest - if: failure() && github.actor != 'renovate[bot]' # Don't run this workflow for renovate bot - - permissions: - contents: read - pull-requests: write - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Comment PR on Failure - id: comment - uses: actions/github-script@v7 - with: - github-token: ${{secrets.GITHUB_TOKEN}} - script: | - const issue_number = context.issue.number; - const owner = context.repo.owner; - const repo = context.repo.repo; - const message = ` - :x: Your changes could not be deployed to a preview domain. - Please check the logs for more information. - `; - - // Fetch all comments - const comments = await github.rest.issues.listComments({ - owner: owner, - repo: repo, - issue_number: issue_number - }); - - // Find a specific comment - const botComment = comments.data.find(comment => comment.user.login === 'github-actions[bot]'); - - // If it exists, update it - if (botComment) { - await github.rest.issues.updateComment({ - owner: owner, - repo: repo, - comment_id: botComment.id, - body: message - }); - } - // Else, create a new one - else { - await github.rest.issues.createComment({ - owner: owner, - repo: repo, - issue_number: issue_number, - body: message - }); - } \ No newline at end of file + uses: ./.github/workflows/comment-pr.yaml + if: failure() + with: + message: | + :x: Your changes could not be deployed to a preview domain. + Please check the logs for more information. + secrets: inherit \ No newline at end of file