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
84 changes: 84 additions & 0 deletions .github/workflows/pr-comment/action.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
name: PR Comment
description: Create or update a PR comment. Updates existing comment if one with the same identifier exists.
inputs:
pr-number:
description: "Pull request number"
required: true
comment-body:
description: "Body of the comment to create or update"
required: true
comment-identifier:
description: "Unique identifier string to find existing comments (e.g., '🚀 Preview Deployment Ready!')"
required: true
github-token:
description: "GitHub token for API access"
required: true
default: ${{ github.token }}
outputs:
comment-id:
description: "ID of the created or updated comment"
value: ${{ steps.comment.outputs.comment-id }}
comment-created:
description: "Whether a new comment was created (true) or existing one was updated (false)"
value: ${{ steps.comment.outputs.comment-created }}

runs:
using: "composite"
steps:
- name: Create or Update PR Comment
id: comment
uses: actions/github-script@v7
env:
PR_NUMBER: ${{ inputs.pr-number }}
COMMENT_BODY: ${{ inputs.comment-body }}
COMMENT_IDENTIFIER: ${{ inputs.comment-identifier }}
with:
github-token: ${{ inputs.github-token }}
script: |
const prNumber = parseInt(process.env.PR_NUMBER);
const body = process.env.COMMENT_BODY;
const identifier = process.env.COMMENT_IDENTIFIER;

// Find existing comment with the identifier
const comments = await github.rest.issues.listComments({
issue_number: prNumber,
owner: context.repo.owner,
repo: context.repo.repo,
});

const existingComment = comments.data.find(
comment =>
comment.user.type === 'Bot' &&
comment.user.login === 'github-actions[bot]' &&
comment.body.includes(identifier)
);

let commentId;
let commentCreated = false;

if (existingComment) {
// Update existing comment
const response = await github.rest.issues.updateComment({
comment_id: existingComment.id,
owner: context.repo.owner,
repo: context.repo.repo,
body: body
});
commentId = response.data.id.toString();
console.log(`Updated existing comment #${commentId}`);
} else {
// Create new comment
const response = await github.rest.issues.createComment({
issue_number: prNumber,
owner: context.repo.owner,
repo: context.repo.repo,
body: body
});
commentId = response.data.id.toString();
commentCreated = true;
console.log(`Created new comment #${commentId}`);
}

core.setOutput('comment-id', commentId);
core.setOutput('comment-created', commentCreated.toString());

40 changes: 40 additions & 0 deletions .github/workflows/pr-preview-cleanup.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Documentation: docs/workflow/cloudflare-pr-preview.md
name: Cleanup PR Preview

on:
pull_request:
types: [closed]
branches:
- dev
- main

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

permissions:
contents: read

env:
PROJECT_NAME: nova-spektr-pr-${{ github.event.pull_request.number }}

jobs:
cleanup-deployment:
runs-on: ubuntu-latest
timeout-minutes: 5

steps:
- name: ⚙️ Checkout
uses: actions/checkout@v4

- name: ⚙️ Install dependencies
uses: ./.github/workflows/install-pnpm

- name: 🗑️ Delete Cloudflare Pages project
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
workingDirectory: .
command: pages project delete ${{ env.PROJECT_NAME }} || echo "Project may have been already deleted or does not exist"

130 changes: 130 additions & 0 deletions .github/workflows/pr-preview-deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# Documentation: docs/workflow/cloudflare-pr-preview.md
name: Deploy PR Preview

on:
pull_request:
types: [opened, synchronize, reopened]
branches:
- dev
- main

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

permissions:
contents: write
pull-requests: write

env:
PROJECT_NAME: nova-spektr-pr-${{ github.event.pull_request.number }}

jobs:
deploy-preview:
runs-on: ubuntu-latest
timeout-minutes: 15

steps:
- name: ⚙️ Checkout
uses: actions/checkout@v4

- name: ⚙️ Install dependencies
uses: ./.github/workflows/install-pnpm

- name: 📝 Update package.json version with commit SHA
run: |
PR_COMMIT_SHA="${{ github.event.pull_request.head.sha }}"
COMMIT_SHA="${PR_COMMIT_SHA:-${{ github.sha }}}"
COMMIT_SHA_SHORT=$(echo "${COMMIT_SHA}" | cut -c1-8)
CURRENT_VERSION=$(node -p "require('./package.json').version")
NEW_VERSION="${CURRENT_VERSION}-${COMMIT_SHA_SHORT}"
node -e "const fs = require('fs'); const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8')); pkg.version = '${NEW_VERSION}'; fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2) + '\n');"
echo "Updated version to: ${NEW_VERSION}"
echo "COMMIT_SHA=${COMMIT_SHA_SHORT}" >> $GITHUB_ENV

- name: 🏗️ Build renderer (staging)
env:
CHAINS_FILE: chains_dev
TOKENS_FILE: tokens_dev
run: |
pnpm clean:build
pnpm renderer:staging
pnpm postbuild:staging

- name: 📦 Ensure Cloudflare Pages project exists
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
run: |
# Check if project exists
echo "Checking if project ${PROJECT_NAME} exists..."
if pnpm wrangler pages project list 2>/dev/null | grep -q "${PROJECT_NAME}"; then
echo "Project ${PROJECT_NAME} already exists."
else
echo "Project ${PROJECT_NAME} does not exist. Creating it now..."
pnpm wrangler pages project create "$PROJECT_NAME" --production-branch=main
fi

- 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 release/build --project-name=${{ env.PROJECT_NAME }} --branch=${{ github.event.pull_request.head.ref }}

- name: 💬 Prepare comment body
id: prepare-comment
if: always()
env:
DEPLOY_OUTCOME: ${{ steps.deploy.outcome }}
PREVIEW_URL: ${{ steps.deploy.outputs.pages-deployment-alias-url }}
BRANCH: ${{ github.event.pull_request.head.ref }}
COMMIT_SHA: ${{ github.event.pull_request.head.sha }}
PR_NUMBER: ${{ github.event.pull_request.number }}
run: |
if [ "$DEPLOY_OUTCOME" == "success" ]; then
{
echo "COMMENT_BODY<<EOF"
echo "## 🚀 Preview Deployment Ready!"
echo ""
echo "Your PR preview has been deployed to Cloudflare Pages!"
echo ""
echo "**Preview URL:** $PREVIEW_URL"
echo ""
echo "**Branch:** \`$BRANCH\`"
echo "**Deployed Commit:** $COMMIT_SHA"
echo "**PR:** #$PR_NUMBER"
echo ""
echo "> ⚠️ Note: The deployment may take a few minutes to propagate. If the URL doesn't work immediately, wait a moment and try again."
echo ""
echo "---"
echo "*This preview will be automatically updated when you push new commits to this PR.*"
echo "EOF"
} >> $GITHUB_OUTPUT
else
{
echo "COMMENT_BODY<<EOF"
echo "## ❌ Preview Deployment Failed"
echo ""
echo "The preview deployment failed. Please check the workflow logs for details."
echo ""
echo "**PR:** #$PR_NUMBER"
echo ""
echo "Common issues:"
echo "- Build errors in the renderer"
echo "- Network issues during deployment"
echo "- Cloudflare API authentication problems"
echo "EOF"
} >> $GITHUB_OUTPUT
fi
shell: bash

- name: 💬 Comment PR with deployment status
if: always()
uses: ./.github/workflows/pr-comment
with:
pr-number: ${{ github.event.pull_request.number }}
comment-identifier: "Preview Deployment"
comment-body: ${{ steps.prepare-comment.outputs.COMMENT_BODY }}

67 changes: 67 additions & 0 deletions docs/workflow/cloudflare-pr-preview.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Cloudflare Pages PR Preview Deployment

Automatic PR preview deployments for the Nova Spektr renderer application using Cloudflare Pages.

## Quick Reference

- **Workflow Files**:
- [Deploy Workflow](/.github/workflows/pr-preview-deploy.yml)
- [Cleanup Workflow](/.github/workflows/pr-preview-cleanup.yml)
- **Triggers**: PR opened, updated, or reopened (deploy) | PR closed (cleanup)
- **Project Naming**: `nova-spektr-pr-{pr_number}`
- **Build Output**: `release/build/`

## How It Works

### Deployment Flow

1. **Build**: Runs `pnpm clean:build`, `pnpm renderer:staging`, and `pnpm postbuild:staging`
2. **Project Creation**: Creates Cloudflare Pages project if it doesn't exist
3. **Deploy**: Deploys `release/build/` to Cloudflare Pages
4. **Notification**: Posts preview URL as PR comment

### Cleanup Flow

When a PR is closed (merged or closed):
- Automatically deletes the Cloudflare Pages project `nova-spektr-pr-{pr_number}`
- Removes all associated deployments and builds
- Runs silently (no PR comment)

## Project Details

- **Project Name Format**: `nova-spektr-pr-{pr_number}` (e.g., `nova-spektr-pr-123`)
- **Preview URL**: Automatically generated by Cloudflare Pages (e.g., `https://nova-spektr-pr-123.pages.dev`)
- **URL Delivery**: Posted as a comment on the PR after successful deployment
- **SPA Support**: Cloudflare Pages automatically handles SPA routing

## Monitoring

- **GitHub Actions**: Check workflow runs for deployment status
- **PR Comments**: Preview URLs are automatically posted
- **Cloudflare Dashboard**: View projects at Workers & Pages → Pages

## Manual Operations

### Delete a Project Manually

**Via Cloudflare Dashboard:**
1. Go to **Workers & Pages** → **Pages**
2. Find project `nova-spektr-pr-{pr_number}`
3. Delete the project

**Via Wrangler CLI:**
```bash
wrangler pages project delete nova-spektr-pr-{pr_number}
```

## Troubleshooting

**Deployment fails:**
- Check GitHub Actions workflow logs
- Verify `CLOUDFLARE_API_TOKEN` and `CLOUDFLARE_ACCOUNT_ID` secrets are set
- Ensure build completes successfully

**Preview URL not working:**
- Wait a few minutes for DNS propagation
- Check Cloudflare Dashboard for deployment status
- Verify the project exists in Cloudflare Pages
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,8 @@
"vite-plugin-svgr": "4.5.0",
"vite-plugin-target": "0.1.1",
"vite-tsconfig-paths": "5.1.4",
"vitest": "3.2.4"
"vitest": "3.2.4",
"wrangler": "4.51.0"
},
"sideEffects": [
"*.css"
Expand Down
Loading
Loading