mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-14 18:01:20 +08:00
de4062a933
* fix: create temp dirs with mkdtemp, not a name built from Date.now() Closes nine open `js/insecure-temporary-file` alerts — the technically correct ones. An audit of all 29 open alerts for that rule split them three ways: - 19 false positives: the write lands inside a directory the caller already made with `mkdtempSync`, and CodeQL's dataflow reaches `tmpdir()` without seeing the mkdtemp in between. - 1 mitigated: `fontCompression.ts` writes with `flag: "wx"` and only takes the tmpdir branch inside Lambda, where /tmp is single-tenant. - 9 real, and these are them. A name built from `Date.now()` under the shared temp dir, followed by `mkdirSync`, is guessable to the millisecond AND leaves a window between choosing the name and creating it, so on a shared machine another user can pre-create or symlink the path first. `mkdtempSync` closes both halves: it picks the random suffix and creates the directory 0700 in one syscall. Same shape, one line shorter, and the alerts go away rather than being dismissed. Six sites in `normalize.test.ts` (its `mkdirSync` import goes with them), one in `generate-catalog-previews.ts` — that single construction accounted for three alerts, since the other two were writes into the directory it made. No shared helper. `mkdtempSync` is already the stdlib primitive for exactly this, and the two callers live in different packages, so a wrapper would need a home in core to serve one CLI test and one build script — more indirection than the line it saves. Deliberately not touching the other 20: excluding the rule repo-wide would hide this class of bug from future code, which is the reason these are fixed rather than silenced. * fix: track the wav temp dir for cleanup and finish the mkdtemp sweep The wav helper pushed the file path into `dirs`, so `afterEach` removed `tone.wav` and left the directory it had just made — four per suite run. Push the directory and derive the file path from it. Measured: the old code leaks 4 directories per run, the new code leaks 0. Three sites still built a predictable name and then created it. CodeQL never flagged them — its dataflow reaches the template preview writes through a `readdir` walk and does not connect them back to the `tmpdir()` root — so the alert list was narrower than the pattern, and closing only the alerts would turn the rule green while the shape survived where nothing would re-flag it. `generate-template-previews.ts` is the near-twin of the file this change started from, and the other two are producer dev entry points. All three use the path only through the variable, so the random suffix changes nothing. Catalog previews now call the existing `createCatalogPreviewTempDir` instead of repeating its body. That test was in no runner, so it pinned uniqueness and mode 0700 on a function nothing called; adding it to `test:scripts` alongside a real caller makes it load-bearing. The rationale for the primitive moves to the helper, which is now the only place it lives. * ci: re-run catalog previews when the temp-dir module changes Routing the renderer through `createCatalogPreviewTempDir` made that module part of its runtime path, and the workflow already states the rule for the sibling case: a module the renderer imports has to appear in the trigger, or a change to it alone never re-runs the job that exercises it. Add it to the `paths:` filter and to the renderer canary, so a PR touching only the temp-dir allocation still renders both shape canaries. Verified against this branch's own range: the previous argument list does not report the file, so a helper-only PR was invisible to both checks.
128 lines
4.7 KiB
YAML
128 lines
4.7 KiB
YAML
name: Catalog Previews
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
# Suppress hyperframes CLI telemetry from HeyGen's own CI runs.
|
|
# External users' CI continues to emit telemetry unless they set this themselves.
|
|
env:
|
|
HYPERFRAMES_NO_TELEMETRY: "1"
|
|
|
|
on:
|
|
pull_request:
|
|
branches: [main]
|
|
paths:
|
|
- "registry/blocks/**"
|
|
- "registry/components/**"
|
|
- "scripts/generate-catalog-previews.ts"
|
|
# Modules the renderer imports. Without them a change to path-traversal
|
|
# defence or temp-directory allocation alone never re-runs the job that
|
|
# exercises it.
|
|
- "scripts/registry-target-paths.mjs"
|
|
- "scripts/catalog-preview-temp.ts"
|
|
- ".github/workflows/catalog-previews.yml"
|
|
|
|
concurrency:
|
|
group: catalog-previews-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
jobs:
|
|
preflight:
|
|
name: Preflight (lint + format)
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 5
|
|
steps:
|
|
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
|
- uses: ./.github/actions/preflight
|
|
|
|
render-previews:
|
|
name: Render catalog previews
|
|
needs: preflight
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 30
|
|
steps:
|
|
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
|
|
|
|
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
|
with:
|
|
node-version: 22
|
|
|
|
- run: bun install --frozen-lockfile
|
|
|
|
- run: bun run build
|
|
|
|
# Chrome headless shell for rendering
|
|
- uses: browser-actions/setup-chrome@c785b87e244131f27c9f19c1a33e2ead956ab7ce # v1
|
|
with:
|
|
chrome-version: stable
|
|
|
|
# The renderer shells out to ffmpeg for both halves of a preview: the
|
|
# poster transcode and the web encode of the mp4. Neither ran here before
|
|
# (`--skip-video` skipped the encode, and the poster copy was a plain
|
|
# file copy), so the job never needed it and ubuntu-latest does not ship
|
|
# it.
|
|
- name: Install ffmpeg
|
|
run: sudo apt-get update -qq && sudo apt-get install -y --no-install-recommends ffmpeg
|
|
|
|
- name: Render changed block/component previews
|
|
env:
|
|
BASE_SHA: ${{ github.event.pull_request.base.sha }}
|
|
run: |
|
|
# Find which blocks/components changed in this PR. The trailing slash
|
|
# in the grep matters: an item is a directory, and a file sitting
|
|
# directly under registry/components (CATALOG.md) does not match the
|
|
# sed, so without it the whole path survives and is passed to the
|
|
# renderer as if it were an item name.
|
|
CHANGED_ITEMS=$(git diff --name-only --diff-filter=ACMR "$BASE_SHA"...HEAD -- registry/blocks/ registry/components/ \
|
|
| grep -E '^registry/(blocks|components)/[^/]+/' \
|
|
| sed 's|^registry/[^/]*/\([^/]*\)/.*|\1|' \
|
|
| sort -u)
|
|
|
|
# A renderer change reaches every item, so it cannot be trusted to a
|
|
# PR that happens to also touch a block. Two canaries cover the two
|
|
# shapes the renderer has to tell apart: a block whose scene lives in
|
|
# a <template> (mounted through a wrapper) and one that registers its
|
|
# timeline at body level (rendered directly). Getting that wrong is
|
|
# silent — the wrong-shaped block renders blank, not red.
|
|
RENDERER_CHANGED=$(git diff --name-only --diff-filter=ACMR "$BASE_SHA"...HEAD \
|
|
-- scripts/generate-catalog-previews.ts scripts/registry-target-paths.mjs \
|
|
scripts/catalog-preview-temp.ts)
|
|
if [ -n "$RENDERER_CHANGED" ]; then
|
|
CHANGED_ITEMS=$(printf '%s\n' $CHANGED_ITEMS \
|
|
code-snippet-visual-studio-dark code-snippet-apple-terminal-pro | sort -u)
|
|
fi
|
|
|
|
if [ -z "$CHANGED_ITEMS" ]; then
|
|
echo "No block/component changes detected."
|
|
exit 0
|
|
fi
|
|
|
|
echo "Changed items: $CHANGED_ITEMS"
|
|
FAILED=0
|
|
|
|
for item in $CHANGED_ITEMS; do
|
|
echo "Rendering preview for: $item"
|
|
if ! timeout 120 npx tsx scripts/generate-catalog-previews.ts --only "$item" --skip-video; then
|
|
echo "::warning::Failed to render preview for $item"
|
|
FAILED=$((FAILED + 1))
|
|
fi
|
|
done
|
|
|
|
if [ "$FAILED" -gt 0 ]; then
|
|
echo "::warning::$FAILED item(s) failed to render"
|
|
exit 1
|
|
fi
|
|
|
|
- name: Upload preview artifacts
|
|
if: always()
|
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
|
with:
|
|
name: catalog-previews
|
|
path: docs/images/catalog/
|
|
if-no-files-found: ignore
|
|
retention-days: 7
|