Skip to content

refactor(cli): adopt direct Vite commands #14093

refactor(cli): adopt direct Vite commands

refactor(cli): adopt direct Vite commands #14093

name: Deploy Examples
on:
push:
branches: [main]
pull_request:
types: [opened, synchronize, reopened, closed]
concurrency:
group: deploy-${{ github.event_name }}-${{ github.ref }}
cancel-in-progress: true
permissions: {}
jobs:
response_store:
# Skip entirely for fork and Dependabot PRs — they don't have access to deploy secrets.
if: github.event_name == 'push' || (github.event.action != 'closed' && github.event.pull_request.head.repo.full_name == github.repository && github.event.pull_request.user.login != 'dependabot[bot]')
name: Deploy and test Workers Response Store
runs-on: ubuntu-latest
permissions:
contents: read
deployments: write
outputs:
service_name: ${{ steps.names.outputs.service_name }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: ./.github/actions/setup
- name: Build Workers Response Store
run: vp run @cloudflare/workers-response-store#build
- name: Select per-run Worker names
id: names
shell: bash
run: |
if [[ "$GITHUB_EVENT_NAME" == "pull_request" ]]; then
prefix="pr-${{ github.event.pull_request.number }}-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}"
service_name="${prefix}-rs-service"
else
prefix="main"
service_name="vinext-workers-response-store-service-poc"
fi
{
echo "service_name=$service_name"
echo "client_name=${prefix}-rs-client"
echo "inline_name=${prefix}-rs-inline"
} >> "$GITHUB_OUTPUT"
- name: Configure live client Worker
working-directory: packages/workers-response-store
env:
CLIENT_NAME: ${{ steps.names.outputs.client_name }}
SERVICE_NAME: ${{ steps.names.outputs.service_name }}
run: |
# Node reads the values from process.env.
# shellcheck disable=SC2016
node -e '
const fs = require("node:fs");
const source = fs.readFileSync("example/service-binding/wrangler.user.jsonc", "utf8");
const config = source
.replace(/"name": "[^"]+"/, `"name": "${process.env.CLIENT_NAME}"`)
.replace(/"main": "user-worker\.ts"/, `"main": "example/service-binding/user-worker.ts"`)
.replace(/"service": "[^"]+"/, `"service": "${process.env.SERVICE_NAME}"`);
fs.writeFileSync(".wrangler-e2e.user.jsonc", config);
'
- name: Deploy live Response Store Workers
working-directory: packages/workers-response-store
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
SERVICE_NAME: ${{ steps.names.outputs.service_name }}
INLINE_NAME: ${{ steps.names.outputs.inline_name }}
run: |
node "$GITHUB_WORKSPACE/.github/scripts/wrangler-deploy.mjs" --config example/service-binding/wrangler.cache.jsonc --name "$SERVICE_NAME" --var WORKERS_RESPONSE_STORE_E2E_EDGE_PURGE_MODE:disabled
node "$GITHUB_WORKSPACE/.github/scripts/wrangler-deploy.mjs" --config .wrangler-e2e.user.jsonc
node "$GITHUB_WORKSPACE/.github/scripts/wrangler-deploy.mjs" --config wrangler.jsonc --name "$INLINE_NAME" --var WORKERS_RESPONSE_STORE_E2E_EDGE_PURGE_MODE:disabled
- name: Run live Response Store E2E
working-directory: packages/workers-response-store
env:
LIVE_CACHE_BASE: https://${{ steps.names.outputs.inline_name }}.vinext.workers.dev
LIVE_RESPONSE_STORE_EDGE_PURGE_MODE: disabled
LIVE_RESPONSE_STORE_SERVICE_BASE: https://${{ steps.names.outputs.client_name }}.vinext.workers.dev
run: |
for url in "$LIVE_CACHE_BASE" "$LIVE_RESPONSE_STORE_SERVICE_BASE"; do
ready=false
consecutive=0
for _ in {1..60}; do
if node -e 'fetch(process.argv[1]).then(async response => process.exit(response.ok && (await response.text()).includes("\"status\":\"ready\"") ? 0 : 1)).catch(() => process.exit(1))' "$url"; then
consecutive=$((consecutive + 1))
if [[ "$consecutive" -eq 5 ]]; then
ready=true
break
fi
else
consecutive=0
fi
sleep 2
done
if [[ "$ready" != true ]]; then
echo "$url did not become ready"
exit 1
fi
done
vp run test:live
- name: Clean up live Response Store Workers
if: always() && github.event_name == 'pull_request'
continue-on-error: true
working-directory: packages/workers-response-store
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
CLIENT_NAME: ${{ steps.names.outputs.client_name }}
INLINE_NAME: ${{ steps.names.outputs.inline_name }}
run: |
set +e
for worker in "$CLIENT_NAME" "$INLINE_NAME"; do
vp exec wrangler delete "$worker" --force
done
deploy:
# Skip entirely for fork and Dependabot PRs — they don't have access to deploy secrets.
if: github.event_name == 'push' || (github.event.action != 'closed' && github.event.pull_request.head.repo.full_name == github.repository && github.event.pull_request.user.login != 'dependabot[bot]')
name: Deploy ${{ matrix.example.name }}
needs: response_store
runs-on: ubuntu-latest
permissions:
contents: read
deployments: write
env:
VINEXT_CACHE_BACKEND: ${{ matrix.example.cache_backend }}
VINEXT_KV_APP_PREFIX: ${{ matrix.example.cache_backend != 'response-store' && github.event_name == 'pull_request' && format('pr-{0}-{1}-{2}-{3}', github.event.pull_request.number, matrix.example.project, github.run_id, github.run_attempt) || matrix.example.project }}
strategy:
fail-fast: false
matrix:
example:
- name: app-router-cloudflare
project: app-router-cloudflare
working_directory: examples/app-router-cloudflare
wrangler_config: dist/server/wrangler.json
- name: pages-router-cloudflare
project: pages-router-cloudflare
working_directory: examples/pages-router-cloudflare
wrangler_config: dist/pages_router_cloudflare/wrangler.json
- name: app-router-playground
project: app-router-playground
working_directory: examples/app-router-playground
wrangler_config: dist/server/wrangler.json
- name: realworld-api-rest
project: realworld-api-rest
working_directory: examples/realworld-api-rest
wrangler_config: dist/realworld_api_rest/wrangler.json
- name: nextra-docs-template
project: nextra-docs-template
working_directory: examples/nextra-docs-template
wrangler_config: dist/server/wrangler.json
- name: benchmarks
project: benchmarks
working_directory: examples/benchmarks
wrangler_config: dist/server/wrangler.json
- name: hackernews
project: hackernews
working_directory: examples/hackernews
wrangler_config: dist/server/wrangler.json
- name: response-store-demo
project: response-store-demo
working_directory: examples/response-store-demo
wrangler_config: dist/server/wrangler.json
cache_backend: response-store
prewarm_e2e: true
warm_cdn_cache: true
- name: workers-cache
project: workers-cache
working_directory: examples/response-store-demo
wrangler_config: dist/server/wrangler.json
bootstrap_warm_cdn: true
cache_backend: workers-cache
prewarm_e2e: true
warm_cdn_cache: true
- name: kv
project: kv
worker_name: kv
working_directory: examples/response-store-demo
wrangler_config: dist/server/wrangler.json
bootstrap_warm_cdn: true
cache_backend: kv
prewarm_e2e: true
warm_cdn_cache: true
- name: static-export
project: static-export
working_directory: examples/static-export
wrangler_config: deploy/wrangler.jsonc
static_export: true
- name: web
project: vinext-web
working_directory: apps/web
wrangler_config: dist/server/wrangler.json
warm_cdn_cache: true
warm_cdn_target: https://vinext.dev
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: ./.github/actions/setup
- name: Build vinext plugin
run: vp run build
- name: Configure Response Store demo preview
if: matrix.example.name == 'response-store-demo' && github.event_name == 'pull_request'
working-directory: ${{ matrix.example.working_directory }}
env:
SERVICE_NAME: pr-${{ github.event.pull_request.number }}-response-store-demo-response-store
run: |
# Node reads the value from process.env.
# shellcheck disable=SC2016
node -e '
const fs = require("node:fs");
const appPath = "wrangler.jsonc";
const servicePath = "wrangler.response-store.jsonc";
const app = fs.readFileSync(appPath, "utf8").replace(
`"service": "response-store-demo-response-store"`,
`"service": "${process.env.SERVICE_NAME}"`,
);
const service = fs.readFileSync(servicePath, "utf8").replace(
`"name": "response-store-demo-response-store"`,
`"name": "${process.env.SERVICE_NAME}"`,
);
if (!app.includes(process.env.SERVICE_NAME) || !service.includes(process.env.SERVICE_NAME)) {
throw new Error("Could not configure the Response Store preview Worker name");
}
fs.writeFileSync(appPath, app);
fs.writeFileSync(servicePath, service);
'
- name: Deploy configured Response Store service
if: matrix.example.name == 'response-store-demo' || (matrix.example.name == 'web' && github.event_name == 'push' && github.ref == 'refs/heads/main')
working-directory: ${{ matrix.example.working_directory }}
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
run: node "$GITHUB_WORKSPACE/.github/scripts/wrangler-deploy.mjs" --config wrangler.response-store.jsonc
- name: Build example
if: matrix.example.static_export != true
run: vp build
working-directory: ${{ matrix.example.working_directory }}
- name: Build static export
if: matrix.example.static_export == true
run: vp build
working-directory: ${{ matrix.example.working_directory }}
- name: Verify static export artifact
if: matrix.example.static_export == true
run: node scripts/verify-output.mjs
working-directory: ${{ matrix.example.working_directory }}
- name: Apply D1 migrations (benchmarks only)
if: matrix.example.name == 'benchmarks' && github.event_name == 'push' && github.ref == 'refs/heads/main'
uses: cloudflare/wrangler-action@ebbaa1584979971c8614a24965b4405ff95890e0 # v4.0.0
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
workingDirectory: examples/benchmarks
command: d1 migrations apply vinext-benchmarks --remote
- name: Apply D1 migrations (web only)
if: matrix.example.name == 'web' && github.event_name == 'push' && github.ref == 'refs/heads/main'
uses: cloudflare/wrangler-action@ebbaa1584979971c8614a24965b4405ff95890e0 # v4.0.0
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
workingDirectory: apps/web
command: d1 migrations apply vinext-metrics --remote
- name: Deploy to Cloudflare Workers (Production)
if: github.event_name == 'push' && github.ref == 'refs/heads/main' && matrix.example.warm_cdn_cache != true
uses: cloudflare/wrangler-action@ebbaa1584979971c8614a24965b4405ff95890e0 # v4.0.0
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
workingDirectory: ${{ matrix.example.working_directory }}
command: deploy --config ${{ matrix.example.wrangler_config }}
- name: Bootstrap CDN warmup Worker (Production)
if: github.event_name == 'push' && github.ref == 'refs/heads/main' && matrix.example.bootstrap_warm_cdn == true
working-directory: ${{ matrix.example.working_directory }}
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
WORKER_NAME: ${{ matrix.example.worker_name }}
run: |
args=(--config "${{ matrix.example.wrangler_config }}")
if [[ -n "$WORKER_NAME" ]]; then
args+=(--name "$WORKER_NAME")
fi
if output=$(vp exec wrangler versions list "${args[@]}" 2>&1); then
printf '%s\n' "$output"
exit 0
fi
printf '%s\n' "$output" >&2
if [[ "$output" != *"does not exist"* && "$output" != *"does not yet exist"* ]]; then
exit 1
fi
vp exec wrangler deploy "${args[@]}"
- name: Deploy to Cloudflare Workers with CDN warmup (Production)
if: github.event_name == 'push' && github.ref == 'refs/heads/main' && matrix.example.warm_cdn_cache == true
working-directory: ${{ matrix.example.working_directory }}
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
WARM_CDN_TARGET: ${{ matrix.example.warm_cdn_target }}
WORKER_NAME: ${{ matrix.example.worker_name }}
run: |
args=(--skip-build --config "${{ matrix.example.wrangler_config }}" --experimental-warm-cdn-cache)
if [[ -n "$WORKER_NAME" ]]; then
args+=(--name "$WORKER_NAME")
fi
if [[ -n "$WARM_CDN_TARGET" ]]; then
args+=(--warm-cdn-target "$WARM_CDN_TARGET")
fi
vp exec vinext-cloudflare deploy "${args[@]}"
- name: Deploy Preview Version
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository && !matrix.example.worker_name
uses: cloudflare/wrangler-action@ebbaa1584979971c8614a24965b4405ff95890e0 # v4.0.0
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
workingDirectory: ${{ matrix.example.working_directory }}
command: versions upload --config ${{ matrix.example.wrangler_config }} --preview-alias pr-${{ github.event.pull_request.number }}
- name: Deploy Named Preview Worker
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository && matrix.example.worker_name
working-directory: ${{ matrix.example.working_directory }}
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
WORKER_NAME: pr-${{ github.event.pull_request.number }}-${{ matrix.example.worker_name }}
run: vp exec wrangler deploy --config "${{ matrix.example.wrangler_config }}" --name "$WORKER_NAME"
# `versions upload` does not apply non-versioned workers.dev/preview URL
# settings. A newly added assets-only Worker has no routes until this runs.
- name: Apply Static Export Preview Routes
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository && matrix.example.static_export == true
uses: cloudflare/wrangler-action@ebbaa1584979971c8614a24965b4405ff95890e0 # v4.0.0
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
workingDirectory: ${{ matrix.example.working_directory }}
command: triggers deploy --config ${{ matrix.example.wrangler_config }}
- name: Seed cache prewarm E2E Worker
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository && matrix.example.prewarm_e2e == true
working-directory: ${{ matrix.example.working_directory }}
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
WORKER_NAME: prewarm-${{ matrix.example.project }}-${{ github.run_id }}-${{ github.run_attempt }}
run: vp exec wrangler deploy --config ../../tests/fixtures/rsc-prewarm-seed/wrangler.jsonc --name "$WORKER_NAME"
- name: Deploy cache prewarm E2E Worker
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository && matrix.example.prewarm_e2e == true
working-directory: ${{ matrix.example.working_directory }}
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
VINEXT_CDN_WARM_DEBUG: "1"
WARM_CDN_CERTIFY: ${{ matrix.example.cache_backend != 'kv' }}
WORKER_NAME: prewarm-${{ matrix.example.project }}-${{ github.run_id }}-${{ github.run_attempt }}
run: |
args=(
--skip-build
--config "${{ matrix.example.wrangler_config }}"
--name "$WORKER_NAME"
--experimental-warm-cdn-cache
)
# The shared demo contains an intentionally late-dynamic page. The
# origin-managed KV adapter cannot classify that streamed MISS as a
# BYPASS, so its live E2E proves the prewarmed data hit directly.
if [[ "$WARM_CDN_CERTIFY" == "true" ]]; then
args+=(--warm-cdn-certify)
fi
vp exec vinext-cloudflare deploy "${args[@]}"
- name: Verify deployed cache prewarm
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository && matrix.example.prewarm_e2e == true
env:
PLAYWRIGHT_PROJECT: cloudflare-workers
VINEXT_E2E_CACHE_BACKEND: ${{ matrix.example.cache_backend }}
VINEXT_E2E_BASE_URL: https://prewarm-${{ matrix.example.project }}-${{ github.run_id }}-${{ github.run_attempt }}.vinext.workers.dev
run: vp exec playwright test tests/e2e/cloudflare-workers/cache-prewarm.spec.ts
- name: Delete cache prewarm E2E Worker
if: always() && github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository && matrix.example.prewarm_e2e == true
continue-on-error: true
working-directory: ${{ matrix.example.working_directory }}
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
WORKER_NAME: prewarm-${{ matrix.example.project }}-${{ github.run_id }}-${{ github.run_attempt }}
run: vp exec wrangler delete "$WORKER_NAME" --config ${{ matrix.example.wrangler_config }} --force
smoke-test:
name: Smoke Test Deployments
runs-on: ubuntu-latest
needs: deploy
permissions:
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Smoke test (production)
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
run: ./scripts/smoke-test.sh
- name: Smoke test (preview)
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository
run: ./scripts/smoke-test.sh --preview pr-${{ github.event.pull_request.number }}
deployed-e2e:
name: E2E Deployed Example
runs-on: ubuntu-latest
needs: deploy
if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository
permissions:
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: ./.github/actions/setup
- name: Cache Playwright browsers
id: playwright-cache
uses: ./.github/actions/playwright-cache
with:
browser: chromium
mode: cache
restore-keys: |
playwright-chromium-${{ runner.os }}-
- name: Install Playwright Chromium
if: steps.playwright-cache.outputs.cache-hit != 'true'
run: vp exec playwright install chromium
- name: Test deployed Web Worker
env:
PLAYWRIGHT_PROJECT: cloudflare-workers
VINEXT_E2E_BASE_URL: ${{ github.event_name == 'pull_request' && format('https://pr-{0}-app-router-cloudflare.vinext.workers.dev', github.event.pull_request.number) || 'https://app-router-cloudflare.vinext.workers.dev' }}
run: >-
vp exec playwright test
tests/e2e/cloudflare-workers/web-worker.spec.ts
tests/e2e/cloudflare-workers/optimistic-search-navigation.spec.ts
cleanup-response-store:
name: Clean up Workers Response Store services
if: always() && github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository && github.event.pull_request.user.login != 'dependabot[bot]'
needs: [response_store, deploy, smoke-test, deployed-e2e]
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: ./.github/actions/setup
- name: Delete cache Worker
continue-on-error: true
working-directory: packages/workers-response-store
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
SERVICE_NAME: ${{ needs.response_store.outputs.service_name }}
DEMO_SERVICE_NAME: pr-${{ github.event.pull_request.number }}-response-store-demo-response-store
PR_ACTION: ${{ github.event.action }}
run: |
if [[ "$PR_ACTION" == "closed" ]]; then
vp exec wrangler delete "$DEMO_SERVICE_NAME" --force
elif [[ -n "$SERVICE_NAME" ]]; then
vp exec wrangler delete "$SERVICE_NAME" --force
fi
- name: Delete named preview Workers
if: github.event.action == 'closed'
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
WORKER_NAME: pr-${{ github.event.pull_request.number }}-kv
run: vp exec wrangler delete "$WORKER_NAME" --force
comment:
name: Comment Preview URLs
runs-on: ubuntu-latest
needs: deploy
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository
permissions:
pull-requests: write
steps:
- name: Comment PR with preview URLs
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
const prNumber = context.issue.number;
const alias = `pr-${prNumber}`;
const examples = [
{ name: 'app-router-cloudflare', project: 'app-router-cloudflare' },
{ name: 'pages-router-cloudflare', project: 'pages-router-cloudflare' },
{ name: 'app-router-playground', project: 'app-router-playground', original: 'https://app-router.vercel.app' },
{ name: 'realworld-api-rest', project: 'realworld-api-rest' },
{ name: 'nextra-docs-template', project: 'nextra-docs-template' },
{ name: 'benchmarks', project: 'benchmarks' },
{ name: 'hackernews', project: 'hackernews', original: 'https://next-react-server-components.vercel.app' },
{ name: 'response-store-demo', project: 'response-store-demo' },
{ name: 'workers-cache', project: 'workers-cache' },
{ name: 'kv', project: 'kv' },
{ name: 'static-export', project: 'static-export' },
{ name: 'web', project: 'vinext-web', production: 'https://vinext.dev' },
];
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
});
const marker = '<!-- vinext-deploy-previews -->';
const existingComment = comments.find(c => c.body?.includes(marker));
const rows = examples
.map(e => {
const orig = e.original ? `[original](${e.original})` : '';
const production = e.production || `https://${e.project}.vinext.workers.dev`;
return `| ${e.name} | [preview](https://${alias}-${e.project}.vinext.workers.dev) | [production](${production}) | ${orig} |`;
})
.join('\n');
const body = [
marker,
'| Example | Preview | Production | Original |',
'|---------|---------|------------|----------|',
rows,
].join('\n');
if (existingComment) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: existingComment.id,
body,
});
} else {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
body,
});
}