From 2744b1727ad0804f895ff73d7c274a3362291fd7 Mon Sep 17 00:00:00 2001 From: Christian Merten Date: Tue, 2 Dec 2025 22:38:26 +0100 Subject: [PATCH] ci: deploy pull requests to staging server (#24) --- .github/workflows/build-docker.yml | 61 ++++++++++++++++--- .github/workflows/check-deployment.yml | 81 ++++++++++++++++++++++++++ 2 files changed, 134 insertions(+), 8 deletions(-) create mode 100644 .github/workflows/check-deployment.yml diff --git a/.github/workflows/build-docker.yml b/.github/workflows/build-docker.yml index a3c6cfb..b967350 100644 --- a/.github/workflows/build-docker.yml +++ b/.github/workflows/build-docker.yml @@ -16,11 +16,13 @@ env: NGINX_IMAGE_NAME: ${{ github.repository }}-nginx jobs: - build-test-and-deploy: + build: runs-on: ubuntu-latest permissions: contents: write packages: write + pull-requests: write + actions: write steps: - name: Checkout repository @@ -32,7 +34,6 @@ jobs: uses: docker/setup-buildx-action@v3 - name: Log in to GitHub Container Registry - if: github.event_name != 'pull_request' uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} @@ -47,7 +48,7 @@ jobs: tags: | type=ref,event=branch type=ref,event=pr - type=sha,prefix={{branch}}- + type=sha,prefix={{branch}}-,enable={{is_default_branch}} type=raw,value=latest,enable={{is_default_branch}} - name: Extract metadata for nginx image @@ -58,7 +59,7 @@ jobs: tags: | type=ref,event=branch type=ref,event=pr - type=sha,prefix={{branch}}- + type=sha,prefix={{branch}}-,enable={{is_default_branch}} type=raw,value=latest,enable={{is_default_branch}} - name: Build application image @@ -95,9 +96,25 @@ jobs: with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: ./docs-output - destination_dir: ${{ github.ref == 'refs/heads/main' && '.' || github.ref_name }} + destination_dir: ${{ github.ref == 'refs/heads/main' && '.' || github.head_ref || github.ref_name }} keep_files: true + - name: Comment documentation link + if: github.event_name == 'pull_request' + uses: marocchino/sticky-pull-request-comment@v2 + with: + header: deployment + message: | + 📚 **Documentation deployed!** + + **Documentation:** https://${{ github.repository_owner }}.github.io/${{ github.event.repository.name }}/${{ github.head_ref }}/ + + **Docker Images:** + - App: `${{ env.REGISTRY }}/${{ env.APP_IMAGE_NAME }}:pr-${{ github.event.pull_request.number }}` + - Nginx: `${{ env.REGISTRY }}/${{ env.NGINX_IMAGE_NAME }}:pr-${{ github.event.pull_request.number }}` + + Add the `awaiting-deployment` label to deploy this PR to a staging server. + - name: Run tests run: make test-only @@ -111,7 +128,6 @@ jobs: fi - name: Tag and push application image - if: github.event_name != 'pull_request' run: | # Tag the built image with all required tags echo "${{ steps.meta-app.outputs.tags }}" | while read -r tag; do @@ -120,7 +136,6 @@ jobs: done - name: Build and push nginx image - if: github.event_name != 'pull_request' uses: docker/build-push-action@v5 with: context: docker/production/nginx @@ -138,10 +153,40 @@ jobs: BUILDKIT_INLINE_CACHE=1 - name: Output image tags - if: github.event_name != 'pull_request' run: | echo "Application image tags:" echo "${{ steps.meta-app.outputs.tags }}" echo "" echo "Nginx image tags:" echo "${{ steps.meta-nginx.outputs.tags }}" + + - name: Check for awaiting-deployment label and trigger deploy + if: github.event_name == 'pull_request' + uses: actions/github-script@v7 + with: + script: | + const prNumber = context.payload.pull_request.number; + + // Check if PR has awaiting-deployment label + const { data: pr } = await github.rest.pulls.get({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: prNumber + }); + + const hasLabel = pr.labels.some(label => label.name === 'awaiting-deployment'); + + if (hasLabel) { + console.log('PR has awaiting-deployment label, triggering deployment'); + await github.rest.actions.createWorkflowDispatch({ + owner: context.repo.owner, + repo: context.repo.repo, + workflow_id: 'deploy-pr.yml', + ref: context.payload.pull_request.head.ref, + inputs: { + pr_number: prNumber.toString() + } + }); + } else { + console.log('PR does not have awaiting-deployment label, skipping deployment'); + } diff --git a/.github/workflows/check-deployment.yml b/.github/workflows/check-deployment.yml new file mode 100644 index 0000000..79b127e --- /dev/null +++ b/.github/workflows/check-deployment.yml @@ -0,0 +1,81 @@ +name: Check Deployment Readiness + +on: + pull_request: + types: [labeled] + +jobs: + check-and-deploy: + runs-on: ubuntu-latest + if: github.event.label.name == 'awaiting-deployment' + permissions: + actions: write + pull-requests: read + contents: write + + steps: + - name: Check build workflow status + id: check + uses: actions/github-script@v7 + with: + script: | + const prNumber = context.payload.pull_request.number; + const headSha = context.payload.pull_request.head.sha; + + console.log(`Checking build status for PR #${prNumber}, commit ${headSha}`); + + // Use Check Runs API to get the status of the build workflow + const { data: checkRuns } = await github.rest.checks.listForRef({ + owner: context.repo.owner, + repo: context.repo.repo, + ref: headSha, + check_name: 'build' + }); + + console.log(`Found ${checkRuns.total_count} check runs for 'build'`); + + if (checkRuns.total_count === 0) { + console.log('No build check found for this commit'); + core.setOutput('should_deploy', 'false'); + core.setOutput('reason', 'No build check found'); + return; + } + + const buildCheck = checkRuns.check_runs[0]; + console.log(`Build check status: ${buildCheck.status}, conclusion: ${buildCheck.conclusion}`); + + // Check if build is still running + if (buildCheck.status !== 'completed') { + console.log('Build check is still running'); + core.setOutput('should_deploy', 'false'); + core.setOutput('reason', 'Build check is still running'); + return; + } + + // Check if build failed + if (buildCheck.conclusion !== 'success') { + console.log(`Build check failed with conclusion: ${buildCheck.conclusion}`); + core.setOutput('should_deploy', 'false'); + core.setOutput('reason', `Build check ${buildCheck.conclusion}`); + return; + } + + // Build completed successfully + console.log('Build check completed successfully, ready to deploy'); + core.setOutput('should_deploy', 'true'); + + - name: Trigger deployment + if: steps.check.outputs.should_deploy == 'true' + uses: actions/github-script@v7 + with: + script: | + await github.rest.actions.createWorkflowDispatch({ + owner: context.repo.owner, + repo: context.repo.repo, + workflow_id: 'deploy-pr.yml', + ref: context.payload.pull_request.head.ref, + inputs: { + pr_number: context.payload.pull_request.number.toString() + } + }); + console.log('Deployment workflow triggered');