Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions .github/workflows/preview-cleanup.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
name: Preview Cleanup

on:
pull_request:
types: [closed]

jobs:
cleanup-preview:
name: Remove preview (PR #${{ github.event.pull_request.number }})
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
deployments: write

steps:
- uses: actions/checkout@v4

- name: Delete Cloudflare Pages deployment
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: pages deployment delete --project-name=notify-chain-dashboard --branch=pr-${{ github.event.pull_request.number }} --yes

- name: Update PR comment to mark preview removed
uses: actions/github-script@v7
env:
PR_NUMBER: ${{ github.event.pull_request.number }}
with:
script: |
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: Number(process.env.PR_NUMBER),
});

const marker = '<!-- notify-chain-preview-bot -->';
const existing = comments.find(c => c.body.includes(marker));
if (!existing) return;

const reason = context.payload.pull_request.merged ? 'merged' : 'closed';
const body = [
marker,
'## 🚀 Preview Deployment',
'',
`| | |`,
`|---|---|`,
`| **Status** | 🗑️ Removed (PR ${reason}) |`,
].join('\n');

await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: existing.id,
body,
});
94 changes: 94 additions & 0 deletions .github/workflows/preview.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
name: Preview Deployment

on:
pull_request:
types: [opened, synchronize, reopened]

concurrency:
group: preview-${{ github.event.pull_request.number }}
cancel-in-progress: true

jobs:
deploy-preview:
name: Deploy preview (PR #${{ github.event.pull_request.number }})
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
deployments: write

steps:
- uses: actions/checkout@v4

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 18
cache: npm
cache-dependency-path: dashboard/package-lock.json

- name: Install dependencies
working-directory: dashboard
run: npm ci

- name: Build dashboard
working-directory: dashboard
env:
VITE_EVENTS_API_URL: ${{ secrets.PREVIEW_EVENTS_API_URL }}
VITE_STELLAR_NETWORK: TESTNET
run: npm run build

- name: Deploy to Cloudflare Pages
id: deploy
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: pages deploy dashboard/dist --project-name=notify-chain-dashboard --branch=pr-${{ github.event.pull_request.number }}

- name: Post preview URL comment
uses: actions/github-script@v7
env:
DEPLOYMENT_URL: ${{ steps.deploy.outputs.deployment-url }}
PR_NUMBER: ${{ github.event.pull_request.number }}
COMMIT_SHA: ${{ github.event.pull_request.head.sha }}
with:
script: |
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: Number(process.env.PR_NUMBER),
});

const marker = '<!-- notify-chain-preview-bot -->';
const existing = comments.find(c => c.body.includes(marker));

const short = process.env.COMMIT_SHA.slice(0, 7);
const body = [
marker,
'## 🚀 Preview Deployment',
'',
`| | |`,
`|---|---|`,
`| **URL** | ${process.env.DEPLOYMENT_URL} |`,
`| **Commit** | \`${short}\` |`,
`| **Status** | ✅ Ready |`,
'',
'_This comment is updated automatically on every push to the PR._',
].join('\n');

if (existing) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: existing.id,
body,
});
} else {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: Number(process.env.PR_NUMBER),
body,
});
}
96 changes: 96 additions & 0 deletions PREVIEW_DEPLOYMENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# Preview Deployments

Every pull request that touches the repository automatically gets a live preview
of the dashboard deployed to Cloudflare Pages.

---

## How it works

| Event | Action |
|---|---|
| PR opened / new commit pushed | Build the dashboard and deploy to a unique Cloudflare Pages branch URL |
| PR merged or closed | Delete the preview deployment and update the PR comment |

The deployment URL is posted as a comment on the PR and updated on every
subsequent push. Only one comment is ever created per PR — it is edited in
place, not duplicated.

---

## Accessing a preview

1. Open the pull request on GitHub.
2. Find the comment from the `github-actions` bot titled **🚀 Preview
Deployment**.
3. Click the URL in the table. The preview reflects the exact commit at the
head of the PR branch.

---

## Required repository secrets

A repository administrator must configure the following secrets under
**Settings → Secrets and variables → Actions** before previews will work.

| Secret | Where to find it |
|---|---|
| `CLOUDFLARE_API_TOKEN` | Cloudflare dashboard → **My Profile → API Tokens**. Create a token with the **Cloudflare Pages: Edit** permission scoped to your account. |
| `CLOUDFLARE_ACCOUNT_ID` | Cloudflare dashboard → right-hand sidebar on the **Workers & Pages** overview page. |
| `PREVIEW_EVENTS_API_URL` | The URL of a shared testnet listener instance used by all previews, e.g. `https://listener.testnet.example.com/api/events`. If none is available, leave this secret unset and the dashboard falls back to mock data automatically. |

---

## Cloudflare Pages project setup

The workflow targets a project named **`notify-chain-dashboard`**. If this
project does not exist yet, create it once:

```bash
# Install Wrangler if you don't already have it
npm install -g wrangler

# Authenticate
wrangler login

# Create the project (one-time setup — no need to link a Git repo)
wrangler pages project create notify-chain-dashboard
```

All subsequent deployments are handled entirely by the GitHub Actions workflows.

---

## Workflow files

| File | Purpose |
|---|---|
| `.github/workflows/preview.yml` | Triggered on `pull_request` (opened / synchronize / reopened). Builds the dashboard and deploys to Cloudflare Pages on a branch named `pr-<number>`. Posts or updates a PR comment with the URL. |
| `.github/workflows/preview-cleanup.yml` | Triggered on `pull_request` (closed). Deletes the Cloudflare Pages deployment for the branch and updates the PR comment to indicate the preview has been removed. |

---

## Environment variables in previews

Previews are built with the following Vite environment variables:

| Variable | Value |
|---|---|
| `VITE_EVENTS_API_URL` | Value of the `PREVIEW_EVENTS_API_URL` repository secret |
| `VITE_STELLAR_NETWORK` | `TESTNET` |

If `PREVIEW_EVENTS_API_URL` is not set, the dashboard automatically falls back
to built-in mock event data so the preview is still usable for UI review.

---

## Isolation

Each PR gets its own Cloudflare Pages branch deployment at a URL in the form:

```
https://pr-<number>.<your-pages-subdomain>.pages.dev
```

Deployments are fully isolated — they do not share state, cookies, or API
connections with each other or with the staging/production environment.
Loading