mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-10-31 02:45:40 +00:00
* feat(aws-lambda): attach the arm64 video image to releases and pin it The release builds the arm64 video function image from the handler ZIP it attaches, attaches it as an OCI layout tarball with its sha256, and pins it in prebuilt/video-image.json, read through prebuiltVideoImage(). A retried release reuses the image already attached. * fix(aws-lambda): keep the release pin script out of the published package pin-handler-zip.ts is run from a checkout by publish.yml; nothing in dist or the CLI imports it. Leaving it out keeps aws-lambda under its size budget with the video image reader added.
325 lines
13 KiB
YAML
325 lines
13 KiB
YAML
name: Publish to npm
|
|
|
|
permissions: {}
|
|
|
|
on:
|
|
push:
|
|
tags:
|
|
# Stable tags are created only by a merged, reviewed release PR. Direct
|
|
# tag pushes are reserved for prerelease channels.
|
|
- "v*-*"
|
|
pull_request:
|
|
types: [closed]
|
|
branches: [main]
|
|
|
|
jobs:
|
|
lambda-arm64-ffmpeg:
|
|
name: Lambda arm64 ffmpeg
|
|
runs-on: ubuntu-24.04-arm
|
|
# A cache miss compiles ffmpeg natively, 15-20 minutes.
|
|
timeout-minutes: 60
|
|
permissions:
|
|
contents: read
|
|
env:
|
|
EXPECTED_RELEASE_SHA: >-
|
|
${{ github.event_name == 'pull_request'
|
|
&& github.event.pull_request.merge_commit_sha
|
|
|| github.sha }}
|
|
if: >-
|
|
github.event_name == 'push' ||
|
|
(github.event.pull_request.merged == true &&
|
|
startsWith(github.event.pull_request.head.ref, 'release/v'))
|
|
steps:
|
|
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
|
with:
|
|
ref: ${{ env.EXPECTED_RELEASE_SHA }}
|
|
# lambda-arm64-image.yml saves this key on main; a release run's own saves reach only its re-runs.
|
|
- id: cache
|
|
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
|
|
with:
|
|
path: out/
|
|
key: hf-media-linux-aarch64-${{ hashFiles('packages/aws-lambda/image/**') }}
|
|
- if: steps.cache.outputs.cache-hit != 'true'
|
|
run: >-
|
|
docker buildx build --progress=plain --platform linux/arm64 --target artifact
|
|
--output type=local,dest=out packages/aws-lambda/image
|
|
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
|
with:
|
|
name: hf-media-linux-aarch64
|
|
path: out/hf-media-linux-aarch64.tar.gz
|
|
if-no-files-found: error
|
|
|
|
publish:
|
|
name: Publish
|
|
needs: lambda-arm64-ffmpeg
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 30
|
|
environment: npm-publish
|
|
permissions:
|
|
contents: write
|
|
id-token: write
|
|
env:
|
|
EXPECTED_RELEASE_SHA: >-
|
|
${{ github.event_name == 'pull_request'
|
|
&& github.event.pull_request.merge_commit_sha
|
|
|| github.sha }}
|
|
# Stable releases come only from reviewed release PRs. Tag pushes are
|
|
# prerelease-only because the trigger pattern requires a hyphenated version.
|
|
if: >-
|
|
github.event_name == 'push' ||
|
|
(github.event.pull_request.merged == true &&
|
|
startsWith(github.event.pull_request.head.ref, 'release/v'))
|
|
steps:
|
|
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
|
with:
|
|
fetch-depth: 0
|
|
# Pin every release to an immutable ref. In particular, a merged
|
|
# release PR event exposes main as github.ref, which may advance
|
|
# before this job starts; use the event's exact merge commit instead.
|
|
ref: ${{ env.EXPECTED_RELEASE_SHA }}
|
|
|
|
- name: Verify immutable release checkout
|
|
run: |
|
|
ACTUAL_SHA="$(git rev-parse HEAD)"
|
|
EXPECTED_COMMIT_SHA="$(git rev-parse "${EXPECTED_RELEASE_SHA}^{commit}")"
|
|
if [ "$ACTUAL_SHA" != "$EXPECTED_COMMIT_SHA" ]; then
|
|
echo "::error::Expected release commit $EXPECTED_COMMIT_SHA, checked out $ACTUAL_SHA"
|
|
exit 1
|
|
fi
|
|
|
|
- name: Resolve version
|
|
id: version
|
|
env:
|
|
EVENT_NAME: ${{ github.event_name }}
|
|
PR_HEAD_REF: ${{ github.event.pull_request.head.ref }}
|
|
run: |
|
|
if [ "$EVENT_NAME" = "push" ]; then
|
|
VERSION="${GITHUB_REF_NAME#v}"
|
|
else
|
|
BRANCH="${PR_HEAD_REF}"
|
|
VERSION="${BRANCH#release/v}"
|
|
fi
|
|
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
|
|
|
|
# Detect pre-release tag (e.g. 0.1.16-alpha.1 → alpha, 0.1.16-beta.2 → beta)
|
|
if [[ "$VERSION" =~ -([a-zA-Z]+) ]]; then
|
|
DIST_TAG="${BASH_REMATCH[1]}"
|
|
echo "prerelease=true" >> "$GITHUB_OUTPUT"
|
|
else
|
|
DIST_TAG="latest"
|
|
echo "prerelease=false" >> "$GITHUB_OUTPUT"
|
|
fi
|
|
echo "dist_tag=${DIST_TAG}" >> "$GITHUB_OUTPUT"
|
|
echo "Resolved version=${VERSION} dist_tag=${DIST_TAG}"
|
|
|
|
- name: Validate release channel
|
|
env:
|
|
VERSION: ${{ steps.version.outputs.version }}
|
|
DIST_TAG: ${{ steps.version.outputs.dist_tag }}
|
|
EVENT_NAME: ${{ github.event_name }}
|
|
PR_HEAD_REF: ${{ github.event.pull_request.head.ref }}
|
|
run: node scripts/validate-release-channel.mjs
|
|
|
|
- name: Create release tag
|
|
if: github.event_name == 'pull_request'
|
|
env:
|
|
VERSION: ${{ steps.version.outputs.version }}
|
|
run: |
|
|
TAG="v$VERSION"
|
|
EXPECTED_TAG_SHA="$(git rev-parse HEAD)"
|
|
|
|
verify_remote_tag() {
|
|
git fetch --force --no-tags origin "+refs/tags/$TAG:refs/tags/$TAG"
|
|
ACTUAL_TAG_SHA="$(git rev-parse "refs/tags/$TAG^{commit}")"
|
|
if [ "$ACTUAL_TAG_SHA" != "$EXPECTED_TAG_SHA" ]; then
|
|
echo "::error::Release tag $TAG points to $ACTUAL_TAG_SHA, expected $EXPECTED_TAG_SHA"
|
|
exit 1
|
|
fi
|
|
echo "Release tag $TAG already exists at the expected commit — skipping"
|
|
}
|
|
|
|
if [ -n "$(git ls-remote --refs origin "refs/tags/$TAG")" ]; then
|
|
verify_remote_tag
|
|
else
|
|
git tag --no-sign "$TAG" "$EXPECTED_TAG_SHA"
|
|
if ! git push origin "refs/tags/$TAG"; then
|
|
# A concurrent retry may have created the tag after ls-remote.
|
|
git tag -d "$TAG"
|
|
verify_remote_tag
|
|
fi
|
|
fi
|
|
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
|
|
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
|
with:
|
|
node-version: 24
|
|
registry-url: "https://registry.npmjs.org"
|
|
- run: corepack enable
|
|
- run: corepack prepare pnpm@10.17.1 --activate
|
|
- run: bun install --frozen-lockfile
|
|
- run: bun run build
|
|
|
|
# Before `npm publish`, so @hyperframes/aws-lambda pins this exact zip.
|
|
- name: Build Lambda handler zip
|
|
id: lambda_zip
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
VERSION: ${{ steps.version.outputs.version }}
|
|
REPO: ${{ github.repository }}
|
|
run: |
|
|
# A retried run reuses the zip already on the release, so the pin still matches it.
|
|
if gh release download "v${VERSION}" --repo "$REPO" --pattern hyperframes-lambda-handler.zip --dir dist; then
|
|
echo "attached=true" >> "$GITHUB_OUTPUT"
|
|
else
|
|
bun run --cwd packages/aws-lambda build:zip
|
|
fi
|
|
bun packages/aws-lambda/scripts/pin-handler-zip.ts
|
|
|
|
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
|
with:
|
|
name: hf-media-linux-aarch64
|
|
path: dist/hf-media
|
|
|
|
# Built from the zip pinned above. The video stage only copies files, so no emulation is needed.
|
|
- name: Build Lambda video image
|
|
id: lambda_video_image
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
VERSION: ${{ steps.version.outputs.version }}
|
|
REPO: ${{ github.repository }}
|
|
run: |
|
|
# A retried run reuses the image already on the release, so the pin still matches it.
|
|
if gh release download "v${VERSION}" --repo "$REPO" --pattern hyperframes-lambda-video-arm64.oci.tar --dir dist; then
|
|
echo "attached=true" >> "$GITHUB_OUTPUT"
|
|
else
|
|
bun packages/aws-lambda/scripts/stage-arm64-image.ts --zip=dist/hyperframes-lambda-handler.zip \
|
|
--ffmpeg=dist/hf-media/hf-media-linux-aarch64.tar.gz --out=dist/arm64-image
|
|
# OCI export needs a docker-container builder; no attestations, so index.json lists one image.
|
|
docker buildx create --use
|
|
docker buildx build --progress=plain --platform linux/arm64 --target video \
|
|
--provenance=false --sbom=false --build-context task=dist/arm64-image/task \
|
|
--output type=oci,dest=dist/hyperframes-lambda-video-arm64.oci.tar packages/aws-lambda/image
|
|
fi
|
|
bun packages/aws-lambda/scripts/pin-handler-zip.ts --video-image
|
|
|
|
- run: bun run verify:packed-manifests
|
|
|
|
- name: Create GitHub Release
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
VERSION: ${{ steps.version.outputs.version }}
|
|
PRERELEASE: ${{ steps.version.outputs.prerelease }}
|
|
REPO: ${{ github.repository }}
|
|
run: |
|
|
NOTES_FILE="releases/v${VERSION}.md"
|
|
|
|
# Skip if release already exists (idempotent re-runs)
|
|
if gh release view "v${VERSION}" --repo "$REPO" >/dev/null 2>&1; then
|
|
echo "Release v${VERSION} already exists — skipping"
|
|
else
|
|
FLAGS=(--repo "$REPO" --title "v${VERSION}")
|
|
if [ -f "$NOTES_FILE" ]; then
|
|
FLAGS+=(--notes-file "$NOTES_FILE")
|
|
else
|
|
echo "No reviewed release notes found at $NOTES_FILE — using GitHub generated notes"
|
|
FLAGS+=(--generate-notes)
|
|
fi
|
|
if [ "$PRERELEASE" = "true" ]; then
|
|
FLAGS+=(--prerelease)
|
|
fi
|
|
gh release create "v${VERSION}" "${FLAGS[@]}"
|
|
fi
|
|
|
|
- name: Attach Lambda handler zip to release
|
|
if: steps.lambda_zip.outputs.attached != 'true'
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
VERSION: ${{ steps.version.outputs.version }}
|
|
REPO: ${{ github.repository }}
|
|
run: gh release upload "v${VERSION}" dist/hyperframes-lambda-handler.zip dist/hyperframes-lambda-handler.sha256 --repo "$REPO"
|
|
|
|
- name: Attach Lambda video image to release
|
|
if: steps.lambda_video_image.outputs.attached != 'true'
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
VERSION: ${{ steps.version.outputs.version }}
|
|
REPO: ${{ github.repository }}
|
|
run: gh release upload "v${VERSION}" dist/hyperframes-lambda-video-arm64.oci.tar dist/hyperframes-lambda-video-arm64.sha256 --repo "$REPO"
|
|
|
|
- name: Publish packages
|
|
env:
|
|
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
VERSION: ${{ steps.version.outputs.version }}
|
|
DIST_TAG: ${{ steps.version.outputs.dist_tag }}
|
|
run: |
|
|
FAILED=0
|
|
|
|
publish_pkg() {
|
|
local filter="$1"
|
|
local name="$2"
|
|
|
|
# Check if this version is already published
|
|
if npm view "${name}@${VERSION}" version >/dev/null 2>&1; then
|
|
echo "⏭️ ${name}@${VERSION} already published — skipping"
|
|
return 0
|
|
fi
|
|
|
|
echo "📦 Publishing ${name}@${VERSION}..."
|
|
if pnpm --filter "$filter" publish --access public --no-git-checks --tag "$DIST_TAG"; then
|
|
echo "✅ ${name}@${VERSION} published"
|
|
else
|
|
echo "❌ ${name}@${VERSION} failed to publish"
|
|
FAILED=1
|
|
fi
|
|
}
|
|
|
|
publish_pkg "@hyperframes/parsers" "@hyperframes/parsers"
|
|
publish_pkg "@hyperframes/lint" "@hyperframes/lint"
|
|
publish_pkg "@hyperframes/studio-server" "@hyperframes/studio-server"
|
|
publish_pkg "@hyperframes/core" "@hyperframes/core"
|
|
publish_pkg "@hyperframes/inspector" "@hyperframes/inspector"
|
|
publish_pkg "@hyperframes/sdk" "@hyperframes/sdk"
|
|
publish_pkg "@hyperframes/engine" "@hyperframes/engine"
|
|
publish_pkg "@hyperframes/player" "@hyperframes/player"
|
|
publish_pkg "@hyperframes/producer" "@hyperframes/producer"
|
|
publish_pkg "@hyperframes/shader-transitions" "@hyperframes/shader-transitions"
|
|
publish_pkg "@hyperframes/studio" "@hyperframes/studio"
|
|
publish_pkg "@hyperframes/aws-lambda" "@hyperframes/aws-lambda"
|
|
publish_pkg "@hyperframes/gcp-cloud-run" "@hyperframes/gcp-cloud-run"
|
|
|
|
# CLI is @hyperframes/cli in the monorepo but published as unscoped "hyperframes" on npm.
|
|
# Rewrite the name in package.json before publishing, then use npm publish directly
|
|
# since pnpm --filter won't match the rewritten name.
|
|
if npm view "hyperframes@${VERSION}" version >/dev/null 2>&1; then
|
|
echo "⏭️ hyperframes@${VERSION} already published — skipping"
|
|
else
|
|
node -e "
|
|
const fs = require('fs');
|
|
const p = 'packages/cli/package.json';
|
|
const pkg = JSON.parse(fs.readFileSync(p, 'utf8'));
|
|
pkg.name = 'hyperframes';
|
|
fs.writeFileSync(p, JSON.stringify(pkg, null, 2) + '\n');
|
|
"
|
|
echo "📦 Publishing hyperframes@${VERSION}..."
|
|
if (cd packages/cli && npm publish --access public --tag "$DIST_TAG"); then
|
|
echo "✅ hyperframes@${VERSION} published"
|
|
else
|
|
echo "❌ hyperframes@${VERSION} failed to publish"
|
|
FAILED=1
|
|
fi
|
|
fi
|
|
|
|
if [ "$FAILED" -ne 0 ]; then
|
|
echo "::error::One or more packages failed to publish"
|
|
exit 1
|
|
fi
|
|
|
|
- name: Build portable agent plugin
|
|
run: node scripts/package-agent-plugin.mjs
|
|
|
|
- name: Attach portable plugin to release
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
VERSION: ${{ steps.version.outputs.version }}
|
|
REPO: ${{ github.repository }}
|
|
run: gh release upload "v${VERSION}" dist/hyperframes-agent-plugin.zip dist/hyperframes-agent-plugin.sha256 dist/hyperframes-agent-plugin.json --repo "$REPO" --clobber
|