Reset preview environments #34
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
| # this workflow redeploys the sandbox and preview environments every 6 hours | |
| # to ensure that the environments are always in a clean state | |
| name: Reset preview environments | |
| on: | |
| schedule: | |
| # every 6 hours | |
| - cron: '0 */6 * * *' | |
| workflow_dispatch: | |
| permissions: | |
| id-token: write | |
| contents: read | |
| packages: write | |
| pull-requests: write | |
| concurrency: | |
| group: reset-preview-envs | |
| cancel-in-progress: false | |
| jobs: | |
| reset-sandbox: | |
| uses: ./.github/workflows/deploy-stack.yml | |
| with: | |
| action: deploy | |
| image_tag: latest | |
| stack_name: sandbox | |
| hostname: sandbox.pubstar.org | |
| env_file: .env.sandbox.enc | |
| stack_file: stack.preview.yml | |
| uses_gateway: true | |
| ssh_host_secret: SSH_HOST_PREVIEW | |
| secrets: | |
| SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} | |
| SSH_USER: ${{ secrets.SSH_USER }} | |
| SSH_HOST: ${{ secrets.SSH_HOST_PREVIEW }} | |
| GHCR_USER: ${{ secrets.GHCR_USER }} | |
| GHCR_TOKEN: ${{ secrets.GHCR_TOKEN }} | |
| find-active-previews: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| matrix: ${{ steps.find.outputs.matrix }} | |
| has_previews: ${{ steps.find.outputs.has_previews }} | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Find open PRs with deployable images | |
| id: find | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| prs=$(gh pr list --label preview --state open --json number,headRefOid,headRefName) | |
| count=$(echo "$prs" | jq length) | |
| echo "found $count PR(s) with preview label" | |
| if [ "$count" -eq 0 ]; then | |
| echo "has_previews=false" >> "$GITHUB_OUTPUT" | |
| echo "matrix={\"include\":[]}" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| REGISTRY_TOKEN=$(curl -s \ | |
| -u "x-access-token:${GH_TOKEN}" \ | |
| "https://ghcr.io/token?scope=repository:knowledgefutures/platform:pull&service=ghcr.io" \ | |
| | jq -r '.token') | |
| image_exists() { | |
| local tag="$1" | |
| local status | |
| status=$(curl -s -o /dev/null -w "%{http_code}" \ | |
| -H "Authorization: Bearer $REGISTRY_TOKEN" \ | |
| -H "Accept: application/vnd.oci.image.index.v1+json,application/vnd.docker.distribution.manifest.v2+json,application/vnd.docker.distribution.manifest.list.v2+json" \ | |
| "https://ghcr.io/v2/knowledgefutures/platform/manifests/${tag}") | |
| [ "$status" = "200" ] | |
| } | |
| results="[]" | |
| for row in $(echo "$prs" | jq -r '.[] | @base64'); do | |
| pr=$(echo "$row" | base64 -d) | |
| pr_number=$(echo "$pr" | jq -r '.number') | |
| head_sha=$(echo "$pr" | jq -r '.headRefOid') | |
| branch=$(echo "$pr" | jq -r '.headRefName') | |
| if image_exists "$head_sha"; then | |
| echo "PR #${pr_number}: image exists for HEAD ($head_sha)" | |
| results=$(echo "$results" | jq --arg n "$pr_number" --arg s "$head_sha" \ | |
| '. + [{"pr_number": ($n|tonumber), "sha": $s}]') | |
| continue | |
| fi | |
| echo "PR #${pr_number}: no image for HEAD ($head_sha), searching previous builds..." | |
| found_sha="" | |
| run_ids=$(gh api "/repos/${{ github.repository }}/actions/workflows/on_pr.yml/runs?event=pull_request&branch=${branch}&per_page=10" \ | |
| --jq '[.workflow_runs[].id] | .[]' 2>/dev/null || echo "") | |
| for run_id in $run_ids; do | |
| build_ok=$(gh api "/repos/${{ github.repository }}/actions/runs/${run_id}/jobs" \ | |
| --jq '[.jobs[] | select(.name | startswith("build-all")) | .conclusion] | if length > 0 and all(. == "success") then "yes" else "no" end' 2>/dev/null || echo "no") | |
| if [ "$build_ok" = "yes" ]; then | |
| found_sha=$(gh api "/repos/${{ github.repository }}/actions/runs/${run_id}" --jq '.head_sha') | |
| echo "PR #${pr_number}: found built image at $found_sha (run $run_id)" | |
| break | |
| fi | |
| done | |
| if [ -n "$found_sha" ]; then | |
| results=$(echo "$results" | jq --arg n "$pr_number" --arg s "$found_sha" \ | |
| '. + [{"pr_number": ($n|tonumber), "sha": $s}]') | |
| else | |
| echo "::warning::PR #${pr_number}: no deployable image found, skipping" | |
| fi | |
| done | |
| count=$(echo "$results" | jq length) | |
| if [ "$count" -eq 0 ]; then | |
| echo "has_previews=false" >> "$GITHUB_OUTPUT" | |
| echo "matrix={\"include\":[]}" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "has_previews=true" >> "$GITHUB_OUTPUT" | |
| echo "matrix=$(echo "$results" | jq -c '{include: .}')" >> "$GITHUB_OUTPUT" | |
| fi | |
| reset-previews: | |
| needs: find-active-previews | |
| if: needs.find-active-previews.outputs.has_previews == 'true' | |
| strategy: | |
| matrix: ${{ fromJson(needs.find-active-previews.outputs.matrix) }} | |
| fail-fast: false | |
| uses: ./.github/workflows/deploy-stack.yml | |
| with: | |
| action: deploy | |
| image_tag: ${{ matrix.sha }} | |
| stack_name: preview-pr-${{ matrix.pr_number }} | |
| hostname: pr-${{ matrix.pr_number }}.pubstar.org | |
| env_file: .env.preview.enc | |
| stack_file: stack.preview.yml | |
| uses_gateway: true | |
| ssh_host_secret: SSH_HOST_PREVIEW | |
| secrets: | |
| SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} | |
| SSH_USER: ${{ secrets.SSH_USER }} | |
| SSH_HOST: ${{ secrets.SSH_HOST_PREVIEW }} | |
| GHCR_USER: ${{ secrets.GHCR_USER }} | |
| GHCR_TOKEN: ${{ secrets.GHCR_TOKEN }} |