mirror of
https://github.com/deepseek-ai/deepseek-harness.git
synced 2026-08-29 04:26:38 +00:00
ci: add build-preview workflow
This commit is contained in:
@@ -0,0 +1,166 @@
|
||||
name: Build PR preview
|
||||
|
||||
# Every push to a pull request publishes that pull request's preview to
|
||||
# Cloudflare Pages under its own branch alias, behind Cloudflare Access. The
|
||||
# upload carries build products only: the workflow never grants the deployment
|
||||
# platform access to this repository's sources.
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
|
||||
# Within one pull request the newest build wins. Across pull requests there is
|
||||
# nothing to serialize: each uploads to its own branch alias, so two deployments
|
||||
# never contend for the same URL.
|
||||
concurrency:
|
||||
group: build-preview-cloudflare-${{ github.event.pull_request.number }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
|
||||
env:
|
||||
PRIMARY_NODE_VERSION: '24'
|
||||
# Cloudflare Pages project receiving the upload. Its preview deployments are
|
||||
# the surface the Access application protects; the project's production branch
|
||||
# is deliberately a name no deployment uses, so no unprotected URL exists.
|
||||
CF_PROJECT: dsh-build-preview
|
||||
# CI runs must never report to the production telemetry endpoint baked into
|
||||
# apps/cli/cordis.yml (AppCLIEntry disables the row when set).
|
||||
DSH_TELEMETRY_DISABLED: '1'
|
||||
|
||||
jobs:
|
||||
preview:
|
||||
runs-on: dsh-ubuntu-24-04-16core
|
||||
name: cloudflare pages preview
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- uses: pnpm/action-setup@v4
|
||||
with:
|
||||
dest: ${{ runner.temp }}/setup-pnpm
|
||||
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: ${{ env.PRIMARY_NODE_VERSION }}
|
||||
|
||||
- name: Configure pnpm store path
|
||||
id: pnpm-store
|
||||
run: |
|
||||
store_root="$HOME/.local/share/pnpm/store"
|
||||
echo "PNPM_CONFIG_STORE_DIR=$store_root" >> "$GITHUB_ENV"
|
||||
store_path=$(PNPM_CONFIG_STORE_DIR="$store_root" pnpm store path --silent)
|
||||
echo "path=$store_path" >> "$GITHUB_OUTPUT"
|
||||
|
||||
# Read-only: the preview lane consumes the default-branch cache without
|
||||
# putting cache upload on its own path.
|
||||
- uses: actions/cache/restore@v4
|
||||
with:
|
||||
path: ${{ steps.pnpm-store.outputs.path }}
|
||||
key: ${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-pnpm-${{ hashFiles('pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-pnpm-
|
||||
|
||||
- name: Install (immutable)
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
# apps/web consumes workspace packages as built lib products, and
|
||||
# build:preview packs the image through the packer's installed bin
|
||||
# (lib/bin.js), so neither half exists before the full build runs.
|
||||
- name: Build workspace
|
||||
run: pnpm run build
|
||||
|
||||
- name: Build the preview page and pack the VFS image
|
||||
env:
|
||||
DSH_CLIENT_TITLE: DSH preview pr-${{ github.event.pull_request.number }}
|
||||
run: pnpm --filter @deepseek-ai/dsh-web-frontend run build:preview
|
||||
|
||||
# Sourcemaps carry complete sources and stay off the deployment platform.
|
||||
# index.html is the served page, which cannot boot without a host
|
||||
# injecting window.__DSH_BOOT__; replacing it with the worker page makes
|
||||
# the deployment root the usable entry instead of a page that never boots.
|
||||
- name: Shape the upload
|
||||
run: |
|
||||
find apps/web/dist -name '*.map' -delete
|
||||
cp apps/web/dist/preview.html apps/web/dist/index.html
|
||||
|
||||
- name: Upload to Cloudflare Pages
|
||||
env:
|
||||
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
run: |
|
||||
npx --yes wrangler@4 pages deploy apps/web/dist \
|
||||
--project-name "$CF_PROJECT" \
|
||||
--branch "pr-${{ github.event.pull_request.number }}" \
|
||||
--commit-dirty=true
|
||||
|
||||
# The image is what a worker boot fails on first and least visibly, so the
|
||||
# run only passes once the protected URL serves it as gzip bytes. Three
|
||||
# facts are asserted, each with its own failure meaning:
|
||||
# 200 Access admitted the request; a 302 means the
|
||||
# Access policy is missing its Service Auth rule
|
||||
# for this token
|
||||
# no content-encoding the platform did not claim transport
|
||||
# compression, which would make the browser
|
||||
# decode the body and leave the worker's
|
||||
# DecompressionStream inflating a plain tar
|
||||
# gzip magic 1f 8b the bytes really are the gzip member the
|
||||
# packer wrote
|
||||
# Accept-Encoding is sent because a browser sends it; the assertion is
|
||||
# about what the platform does with a body that is already compressed.
|
||||
- name: Verify the protected deployment serves the image
|
||||
env:
|
||||
CF_ACCESS_CLIENT_ID: ${{ secrets.CF_ACCESS_CLIENT_ID }}
|
||||
CF_ACCESS_CLIENT_SECRET: ${{ secrets.CF_ACCESS_CLIENT_SECRET }}
|
||||
run: |
|
||||
url="https://pr-${{ github.event.pull_request.number }}.${CF_PROJECT}.pages.dev"
|
||||
image="$url/preview/vfs-image.tar.gz"
|
||||
code=000
|
||||
for attempt in 1 2 3 4 5; do
|
||||
code=$(curl -sS -o image.bin -D headers.txt -w '%{http_code}' \
|
||||
-H 'Accept-Encoding: gzip' \
|
||||
-H "CF-Access-Client-Id: $CF_ACCESS_CLIENT_ID" \
|
||||
-H "CF-Access-Client-Secret: $CF_ACCESS_CLIENT_SECRET" \
|
||||
"$image" || echo 000)
|
||||
echo "attempt $attempt: HTTP $code"
|
||||
if [ "$code" = "200" ]; then break; fi
|
||||
sleep 10
|
||||
done
|
||||
if [ "$code" != "200" ]; then
|
||||
echo "the protected image URL answered $code, not 200"
|
||||
head -20 headers.txt
|
||||
exit 1
|
||||
fi
|
||||
if grep -qi '^content-encoding:' headers.txt; then
|
||||
echo "the platform declared transport compression on an already-compressed image:"
|
||||
grep -i '^content-encoding:' headers.txt
|
||||
exit 1
|
||||
fi
|
||||
magic=$(head -c 2 image.bin | od -An -tx1 | tr -d ' \n')
|
||||
if [ "$magic" != "1f8b" ]; then
|
||||
echo "image does not start with the gzip magic number: $magic"
|
||||
exit 1
|
||||
fi
|
||||
echo "image served as $(wc -c < image.bin) gzip bytes"
|
||||
|
||||
# The alias URL follows from the pull request number, so it is stable
|
||||
# across redeploys and worth stating once. The marker makes the comment
|
||||
# idempotent: a pull request opened before this workflow existed never
|
||||
# sees an `opened` event, and every later push must not restate the URL.
|
||||
- name: Comment the preview URL
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
PR: ${{ github.event.pull_request.number }}
|
||||
run: |
|
||||
marker='<!-- dsh-preview-url -->'
|
||||
existing=$(gh pr view "$PR" --json comments \
|
||||
--jq "[.comments[] | select(.body | contains(\"$marker\")) | .url] | first // empty")
|
||||
if [ -n "$existing" ]; then
|
||||
echo "preview URL already commented: $existing"
|
||||
exit 0
|
||||
fi
|
||||
gh pr comment "$PR" --body \
|
||||
"$marker \n [Preview for #$PR](https://pr-$PR.${CF_PROJECT}.pages.dev) (requires Cloudflare Access sign-in)"
|
||||
Reference in New Issue
Block a user