Skip to content

Merge pull request #34 from kc3hack/feat/nenrin/#31-page-optimize #73

Merge pull request #34 from kc3hack/feat/nenrin/#31-page-optimize

Merge pull request #34 from kc3hack/feat/nenrin/#31-page-optimize #73

name: Deploy Workers
on:
pull_request:
branches:
- main
- develop
types:
- opened
- synchronize
- reopened
push:
branches:
- main
- develop
permissions:
contents: read
pull-requests: write
jobs:
deploy-frontend:
name: Deploy Frontend Worker
if: ${{ github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == false) }}
runs-on: ubuntu-latest
outputs:
preview_url: ${{ steps.frontend_version.outputs.preview_url }}
public_url: ${{ steps.frontend_env.outputs.public_url }}
concurrency:
group: frontend-worker-${{ github.event_name == 'pull_request' && 'pr' || github.ref_name }}
cancel-in-progress: true
defaults:
run:
working-directory: frontend
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
cache-dependency-path: frontend/pnpm-lock.yaml
- name: Select frontend environment
id: frontend_env
shell: bash
run: |
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
echo "wrangler_env=pr" >> "$GITHUB_OUTPUT"
echo "api_base_url=https://api.test.kc3hack2026-9.yaken.org" >> "$GITHUB_OUTPUT"
echo "public_url=https://test.kc3hack2026-9.yaken.org" >> "$GITHUB_OUTPUT"
elif [[ "${{ github.ref_name }}" == "develop" ]]; then
echo "wrangler_env=develop" >> "$GITHUB_OUTPUT"
echo "api_base_url=https://api.develop.kc3hack2026-9.yaken.org" >> "$GITHUB_OUTPUT"
echo "public_url=https://develop.kc3hack2026-9.yaken.org" >> "$GITHUB_OUTPUT"
elif [[ "${{ github.ref_name }}" == "main" ]]; then
echo "wrangler_env=" >> "$GITHUB_OUTPUT"
echo "api_base_url=https://api.kc3hack2026-9.yaken.org" >> "$GITHUB_OUTPUT"
echo "public_url=https://kc3hack2026-9.yaken.org" >> "$GITHUB_OUTPUT"
else
echo "Unsupported branch: ${{ github.ref_name }}" >&2
exit 1
fi
- name: Install frontend dependencies
run: pnpm install --frozen-lockfile
- name: Validate Cloudflare secrets
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
run: |
if [[ -z "${CLOUDFLARE_API_TOKEN}" ]]; then
echo "::error title=Missing secret::CLOUDFLARE_API_TOKEN is not set in repository secrets."
exit 1
fi
if [[ -z "${CLOUDFLARE_ACCOUNT_ID}" ]]; then
echo "::error title=Missing secret::CLOUDFLARE_ACCOUNT_ID is not set in repository secrets."
exit 1
fi
- name: Upload frontend version
id: frontend_upload
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
# NEXT_PUBLIC_* values are embedded at OpenNext/Next.js build time.
NEXT_PUBLIC_API_BASE_URL: ${{ steps.frontend_env.outputs.api_base_url }}
WRANGLER_OUTPUT_FILE_PATH: /tmp/frontend-wrangler-output.jsonl
shell: bash
run: |
set -euo pipefail
: > "${WRANGLER_OUTPUT_FILE_PATH}"
pnpm exec opennextjs-cloudflare build
if [[ -n "${{ steps.frontend_env.outputs.wrangler_env }}" ]]; then
pnpm exec opennextjs-cloudflare upload --env "${{ steps.frontend_env.outputs.wrangler_env }}" 2>&1 | tee /tmp/frontend-upload.log
else
pnpm exec opennextjs-cloudflare upload 2>&1 | tee /tmp/frontend-upload.log
fi
- name: Read frontend version metadata
id: frontend_version
env:
WRANGLER_OUTPUT_FILE_PATH: /tmp/frontend-wrangler-output.jsonl
shell: bash
run: |
set -euo pipefail
node <<'NODE'
const fs = require("node:fs");
const outputPath = process.env.WRANGLER_OUTPUT_FILE_PATH;
const githubOutput = process.env.GITHUB_OUTPUT;
let versionId = "";
let previewUrl = "";
if (outputPath && fs.existsSync(outputPath)) {
const lines = fs.readFileSync(outputPath, "utf8").split("\n").filter(Boolean);
for (const line of lines) {
try {
const payload = JSON.parse(line);
if (payload?.type === "version-upload") {
versionId = payload.version_id || versionId;
previewUrl = payload.preview_url || previewUrl;
}
} catch {}
}
}
if (!versionId) {
process.stderr.write(`Failed to read version_id from ${outputPath}\n`);
process.exit(1);
}
fs.appendFileSync(githubOutput, `version_id=${versionId}\n`);
fs.appendFileSync(githubOutput, `preview_url=${previewUrl}\n`);
NODE
- name: Deploy uploaded frontend version
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
shell: bash
run: |
set -euo pipefail
if [[ -n "${{ steps.frontend_env.outputs.wrangler_env }}" ]]; then
pnpm exec wrangler versions deploy "${{ steps.frontend_version.outputs.version_id }}@100" --env "${{ steps.frontend_env.outputs.wrangler_env }}" --yes
else
pnpm exec wrangler versions deploy "${{ steps.frontend_version.outputs.version_id }}@100" --yes
fi
- name: Apply frontend triggers
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
shell: bash
run: |
set -euo pipefail
if [[ -n "${{ steps.frontend_env.outputs.wrangler_env }}" ]]; then
pnpm exec wrangler triggers deploy --env "${{ steps.frontend_env.outputs.wrangler_env }}"
else
pnpm exec wrangler triggers deploy
fi
deploy-backend:
name: Deploy Backend Worker
if: ${{ github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == false) }}
runs-on: ubuntu-latest
outputs:
preview_url: ${{ steps.backend_version.outputs.preview_url }}
public_url: ${{ steps.backend_env.outputs.public_url }}
concurrency:
group: backend-worker-${{ github.event_name == 'pull_request' && 'pr' || github.ref_name }}
cancel-in-progress: true
defaults:
run:
working-directory: backend
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
cache-dependency-path: backend/pnpm-lock.yaml
- name: Select backend environment
id: backend_env
shell: bash
run: |
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
echo "wrangler_env=pr" >> "$GITHUB_OUTPUT"
echo "public_url=https://api.test.kc3hack2026-9.yaken.org" >> "$GITHUB_OUTPUT"
elif [[ "${{ github.ref_name }}" == "develop" ]]; then
echo "wrangler_env=develop" >> "$GITHUB_OUTPUT"
echo "public_url=https://api.develop.kc3hack2026-9.yaken.org" >> "$GITHUB_OUTPUT"
elif [[ "${{ github.ref_name }}" == "main" ]]; then
echo "wrangler_env=" >> "$GITHUB_OUTPUT"
echo "public_url=https://api.kc3hack2026-9.yaken.org" >> "$GITHUB_OUTPUT"
else
echo "Unsupported branch: ${{ github.ref_name }}" >&2
exit 1
fi
- name: Install backend dependencies
run: pnpm install --frozen-lockfile
- name: Validate Cloudflare secrets
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
run: |
if [[ -z "${CLOUDFLARE_API_TOKEN}" ]]; then
echo "::error title=Missing secret::CLOUDFLARE_API_TOKEN is not set in repository secrets."
exit 1
fi
if [[ -z "${CLOUDFLARE_ACCOUNT_ID}" ]]; then
echo "::error title=Missing secret::CLOUDFLARE_ACCOUNT_ID is not set in repository secrets."
exit 1
fi
- name: Upload backend version
id: backend_upload
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
WRANGLER_OUTPUT_FILE_PATH: /tmp/backend-wrangler-output.jsonl
shell: bash
run: |
set -euo pipefail
: > "${WRANGLER_OUTPUT_FILE_PATH}"
if [[ -n "${{ steps.backend_env.outputs.wrangler_env }}" ]]; then
pnpm exec wrangler versions upload --env "${{ steps.backend_env.outputs.wrangler_env }}" 2>&1 | tee /tmp/backend-upload.log
else
pnpm exec wrangler versions upload 2>&1 | tee /tmp/backend-upload.log
fi
- name: Read backend version metadata
id: backend_version
env:
WRANGLER_OUTPUT_FILE_PATH: /tmp/backend-wrangler-output.jsonl
shell: bash
run: |
set -euo pipefail
node <<'NODE'
const fs = require("node:fs");
const outputPath = process.env.WRANGLER_OUTPUT_FILE_PATH;
const githubOutput = process.env.GITHUB_OUTPUT;
let versionId = "";
let previewUrl = "";
if (outputPath && fs.existsSync(outputPath)) {
const lines = fs.readFileSync(outputPath, "utf8").split("\n").filter(Boolean);
for (const line of lines) {
try {
const payload = JSON.parse(line);
if (payload?.type === "version-upload") {
versionId = payload.version_id || versionId;
previewUrl = payload.preview_url || previewUrl;
}
} catch {}
}
}
if (!versionId) {
process.stderr.write(`Failed to read version_id from ${outputPath}\n`);
process.exit(1);
}
fs.appendFileSync(githubOutput, `version_id=${versionId}\n`);
fs.appendFileSync(githubOutput, `preview_url=${previewUrl}\n`);
NODE
- name: Deploy uploaded backend version
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
shell: bash
run: |
set -euo pipefail
if [[ -n "${{ steps.backend_env.outputs.wrangler_env }}" ]]; then
pnpm exec wrangler versions deploy "${{ steps.backend_version.outputs.version_id }}@100" --env "${{ steps.backend_env.outputs.wrangler_env }}" --yes
else
pnpm exec wrangler versions deploy "${{ steps.backend_version.outputs.version_id }}@100" --yes
fi
- name: Apply backend triggers
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
shell: bash
run: |
set -euo pipefail
if [[ -n "${{ steps.backend_env.outputs.wrangler_env }}" ]]; then
pnpm exec wrangler triggers deploy --env "${{ steps.backend_env.outputs.wrangler_env }}"
else
pnpm exec wrangler triggers deploy
fi
comment-preview:
name: Comment Preview URLs
if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == false }}
runs-on: ubuntu-latest
needs:
- deploy-frontend
- deploy-backend
steps:
- name: Upsert preview URL comment
uses: actions/github-script@v7
env:
FRONTEND_PREVIEW: ${{ needs.deploy-frontend.outputs.preview_url }}
BACKEND_PREVIEW: ${{ needs.deploy-backend.outputs.preview_url }}
FRONTEND_PUBLIC: ${{ needs.deploy-frontend.outputs.public_url }}
BACKEND_PUBLIC: ${{ needs.deploy-backend.outputs.public_url }}
with:
script: |
const marker = "<!-- preview-urls -->";
const frontendPreview = process.env.FRONTEND_PREVIEW?.trim() || "(not available)";
const backendPreview = process.env.BACKEND_PREVIEW?.trim() || "(not available)";
const frontendPublic = process.env.FRONTEND_PUBLIC?.trim() || "(not set)";
const backendPublic = process.env.BACKEND_PUBLIC?.trim() || "(not set)";
const body = `${marker}
## Preview URLs
- Frontend (workers.dev): ${frontendPreview}
- Backend (workers.dev): ${backendPreview}
- Frontend (custom domain): ${frontendPublic}
- Backend (custom domain): ${backendPublic}`;
const { owner, repo } = context.repo;
const issue_number = context.issue.number;
const comments = await github.paginate(github.rest.issues.listComments, {
owner,
repo,
issue_number,
per_page: 100,
});
const existing = comments.find((c) => c.user?.type === "Bot" && c.body?.includes(marker));
if (existing) {
await github.rest.issues.updateComment({
owner,
repo,
comment_id: existing.id,
body,
});
} else {
await github.rest.issues.createComment({
owner,
repo,
issue_number,
body,
});
}