-
Notifications
You must be signed in to change notification settings - Fork 2
feat(ci): add label-gated pull request preview deployments #1557
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
4394634
feat(ci): add label-gated pull request preview deployments
FelixTJDietrich f97be56
Merge branch 'main' into fix/selective-preview-deployments
FelixTJDietrich 6279418
refactor(ci): drop host-side teardown verification from previews
FelixTJDietrich 9369309
fix(ci): render the preview stack without the ambient compose env files
FelixTJDietrich File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| --- | ||
| "hephaestus": minor | ||
| --- | ||
|
|
||
| Pull request previews are now self-service. Add the `preview` label to a pull request in this repository and it deploys; every commit after that redeploys on its own. A preview waits only for its images to be published, never for the test suite, so it exists even when the tests are red — and it runs the same artifacts staging and production run, so what you see is what ships. A comment on the pull request carries the preview link, and GitHub's native deployment link opens it too. Removing the label, closing the pull request, or converting it back to draft removes the stack. Up to three previews run at once by default, and when the host is full the pull request comment names the ones holding the slots. | ||
|
|
||
| Preview stacks use their own database, message broker, credentials, and Docker networks, and reach neither the staging Docker socket, its data, nor any integration credential. Previews never run for forks, nor for changes to the deployment workflows themselves. Stacked pull requests each get their own preview. | ||
|
|
||
| **Operators:** follow the preview runbook before enabling the Coolify application. It requires a `preview` repository label, a preview-only Coolify application, two scoped Coolify secrets, and the repository variables listed there — including the optional `PREVIEW_MAX_ACTIVE` limit. Keep Coolify's automatic repository webhook disabled. |
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 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 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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,51 +1,92 @@ | ||
| name: Preview cleanup | ||
|
|
||
| # Since 2025-12-08 `pull_request_target` always takes the workflow file and the checked-out commit from | ||
| # the default branch, so no fork code runs beside the secrets here. | ||
| on: | ||
| pull_request: | ||
| branches: ["**"] | ||
| types: [closed] | ||
| pull_request_target: | ||
| types: [closed, unlabeled, converted_to_draft] | ||
|
|
||
| permissions: | ||
| contents: read | ||
| permissions: {} | ||
|
|
||
| concurrency: | ||
| group: hephaestus-preview-lifecycle | ||
| queue: max | ||
|
|
||
| jobs: | ||
| cleanup: | ||
| name: "Preview / Delete Coolify resources" | ||
| name: "Preview / Remove and verify resources" | ||
| if: >- | ||
| vars.COOLIFY_URL != '' && | ||
| vars.COOLIFY_APP_UUID != '' && | ||
| github.event.pull_request.head.repo.full_name == github.repository | ||
| github.event.pull_request.head.repo.full_name == github.repository && | ||
| (github.event.label.name == 'preview' || | ||
| (github.event.action != 'unlabeled' && | ||
| contains(github.event.pull_request.labels.*.name, 'preview'))) | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 3 | ||
| permissions: | ||
| contents: read | ||
| deployments: write | ||
| pull-requests: write | ||
| timeout-minutes: 8 | ||
| env: | ||
| COOLIFY_URL: ${{ vars.COOLIFY_URL }} | ||
| COOLIFY_APP_UUID: ${{ vars.COOLIFY_APP_UUID }} | ||
| COOLIFY_TOKEN: ${{ secrets.COOLIFY_API_TOKEN }} | ||
| PR_NUMBER: ${{ github.event.pull_request.number }} | ||
| PR_URL: ${{ github.event.pull_request.html_url }} | ||
| PR_TITLE: ${{ github.event.pull_request.title }} | ||
| AUTHOR_ASSOCIATION: ${{ github.event.pull_request.author_association }} | ||
| HEAD_REF: ${{ github.event.pull_request.head.ref }} | ||
| HEAD_SHA: ${{ github.event.pull_request.head.sha }} | ||
| # Coolify's routing key: the branch its preview application is configured for. | ||
| BASE_REF: ${{ github.event.repository.default_branch }} | ||
| ENVIRONMENT: preview/pr-${{ github.event.pull_request.number }} | ||
| steps: | ||
| - name: Delete preview containers, volumes, and network | ||
| - name: Load the trusted preview adapter | ||
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | ||
| with: | ||
| ref: ${{ github.sha }} | ||
| persist-credentials: false | ||
| sparse-checkout: | | ||
| .github/actions/setup-bun | ||
| package.json | ||
| scripts | ||
|
|
||
| - name: Set up the repository's Bun version | ||
| uses: ./.github/actions/setup-bun | ||
|
|
||
| - name: Request Coolify cleanup through the signed close event | ||
| id: close | ||
| continue-on-error: true | ||
| env: | ||
| COOLIFY_WEBHOOK_SECRET: ${{ secrets.COOLIFY_PREVIEW_WEBHOOK_SECRET }} | ||
| run: bun scripts/coolify-preview.ts close | ||
|
|
||
| - name: Keep a verified cleanup tombstone | ||
| id: tombstone | ||
| if: steps.close.outcome == 'success' | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| with: | ||
| script: | | ||
| const controller = await import(`${process.env.GITHUB_WORKSPACE}/scripts/preview-controller.ts`); | ||
| await controller.inactivate({ github, context, core }); | ||
|
|
||
| - name: Report the released preview | ||
| # Always overwrites the sticky comment, closed pull requests included: a torn-down preview | ||
| # must never leave a live-looking link behind. | ||
| if: always() && steps.tombstone.outcome == 'success' | ||
| uses: marocchino/sticky-pull-request-comment@773744901bac0e8cbb5a0dc842800d45e9b2b405 # v2 | ||
| with: | ||
| header: app-preview | ||
| number: ${{ github.event.pull_request.number }} | ||
| message: | | ||
| ## 🧹 App Preview | ||
|
|
||
| ~~Preview removed~~ — its slot is free again. | ||
|
|
||
| <sub>Add the `preview` label, or mark the pull request ready for review, to deploy it again.</sub> | ||
|
|
||
| - name: Fail if Coolify did not accept the close event | ||
| if: always() && steps.close.outcome != 'success' | ||
| run: | | ||
| set -euo pipefail | ||
|
|
||
| if [ -z "${COOLIFY_TOKEN}" ]; then | ||
| echo "::notice::COOLIFY_API_TOKEN is not configured; preview cleanup must be done manually." | ||
| exit 0 | ||
| fi | ||
|
|
||
| body=$(mktemp) | ||
| status=$(curl -sS -o "${body}" -w '%{http_code}' -X DELETE \ | ||
| -H "Authorization: Bearer ${COOLIFY_TOKEN}" \ | ||
| -H 'Accept: application/json' \ | ||
| --retry 3 --retry-connrefused --max-time 60 \ | ||
| "${COOLIFY_URL}/api/v1/applications/${COOLIFY_APP_UUID}/previews/${PR_NUMBER}") | ||
|
|
||
| if [ "${status}" -eq 404 ]; then | ||
| echo "::notice::PR #${PR_NUMBER} had no Coolify preview to clean up." | ||
| exit 0 | ||
| fi | ||
| if [ "${status}" -ge 400 ]; then | ||
| echo "::error::Coolify preview cleanup returned HTTP ${status}: $(cat "${body}")" | ||
| exit 1 | ||
| fi | ||
|
|
||
| echo "::notice::Deleted Coolify preview and persistent volumes for PR #${PR_NUMBER}." | ||
| echo "::error::Coolify did not accept the close event; the nightly reconcile will retry." | ||
| exit 1 | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.