diff --git a/.github/workflows/website-preview-dispatch.yaml b/.github/workflows/website-preview-dispatch.yaml new file mode 100644 index 0000000000..048118bcf3 --- /dev/null +++ b/.github/workflows/website-preview-dispatch.yaml @@ -0,0 +1,69 @@ +name: 'Website Preview Dispatch' + +permissions: + contents: read + +on: + pull_request: + types: [opened, synchronize, reopened, closed, labeled] + paths: + - packages/twenty-website/** + - .github/workflows/website-preview-dispatch.yaml + +concurrency: + # Keyed on PR number so independent PRs don't cancel each other. `github.ref` + # would resolve to the base branch under pull_request and collide. + group: ${{ github.workflow }}-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + trigger-build: + # Same fork PRs from outside the org don't have `secrets.*` so the dispatch + # call would fail anyway — skip explicitly to avoid noise. + if: | + github.event.pull_request.head.repo.full_name == github.repository && + github.event.action != 'closed' && ( + (github.event.action == 'labeled' && github.event.label.name == 'preview-website') || + ( + ( + github.event.pull_request.author_association == 'MEMBER' || + github.event.pull_request.author_association == 'OWNER' || + github.event.pull_request.author_association == 'COLLABORATOR' + ) && contains(fromJSON('["opened","synchronize","reopened"]'), github.event.action) + ) + ) + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - name: Dispatch website-preview-build to ci-privileged + env: + GH_TOKEN: ${{ secrets.CI_PRIVILEGED_DISPATCH_TOKEN }} + PR_NUMBER: ${{ github.event.pull_request.number }} + PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }} + PR_HEAD_REF: ${{ github.event.pull_request.head.ref }} + run: | + gh api repos/twentyhq/ci-privileged/dispatches \ + -f event_type=website-preview-build \ + -f "client_payload[pr_number]=$PR_NUMBER" \ + -f "client_payload[pr_head_sha]=$PR_HEAD_SHA" \ + -f "client_payload[pr_head_ref]=$PR_HEAD_REF" + + trigger-cleanup: + # Covers both merge and close-without-merge — pull_request `closed` fires + # for both. PRs left open forever are covered by OpenNext's + # `maxVersionAgeDays: 14` + `maxNumberOfVersions: 50` auto-pruning in + # open-next.config.ts, so nothing leaks even if cleanup never runs. + if: | + github.event.pull_request.head.repo.full_name == github.repository && + github.event.action == 'closed' + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - name: Dispatch website-preview-cleanup to ci-privileged + env: + GH_TOKEN: ${{ secrets.CI_PRIVILEGED_DISPATCH_TOKEN }} + PR_NUMBER: ${{ github.event.pull_request.number }} + run: | + gh api repos/twentyhq/ci-privileged/dispatches \ + -f event_type=website-preview-cleanup \ + -f "client_payload[pr_number]=$PR_NUMBER" diff --git a/packages/twenty-website/open-next.config.ts b/packages/twenty-website/open-next.config.ts index 2a0ccd24dd..275da561b2 100644 --- a/packages/twenty-website/open-next.config.ts +++ b/packages/twenty-website/open-next.config.ts @@ -14,7 +14,9 @@ export default { ...baseConfig.cloudflare, skewProtection: { enabled: true, - maxNumberOfVersions: 10, + // Window large enough to keep prod-version history for skew routing AND + // hold one slot per open PR preview. `maxVersionAgeDays` prunes the rest. + maxNumberOfVersions: 50, maxVersionAgeDays: 14, }, },