mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-11-07 15:27:14 +00:00
* feat(cli): lambda deploy works from the npm install, no repo checkout Each release builds the Lambda handler zip on Linux, attaches it to the GitHub release, and pins its URL and sha256 (plus the SAM template) in @hyperframes/aws-lambda before npm publish. Outside a checkout, lambda deploy downloads that zip, refuses it on a sha256 mismatch, and deploys it with the packaged template. With neither a checkout nor a pinned zip it fails up front with one message naming every way out. * fix(cli): lambda deploy test mocks the pinned-handler reader, comments fit the checks * test(cli): sam tests deploy from a SAM directory, not the repo root * chore(aws-lambda): raise the package size budget for the pinned handler template Measured with pnpm@10.17.1 pack on Linux. Release state (prebuilt/ written by pin-handler-zip): 77,307 packed / 285,654 unpacked. CI state (no prebuilt/): 70,104 / 253,390. Packed is the check's 5% headroom over the release size. Unpacked is 292,000 (2.2% over release) because a 5% budget would put the CI pack under the 85% ratchet. * test(cli): anchor the docs link check in the lambda deploy fallback test * fix(cli): lambda deploy logs the verified handler sha256 and keeps a presigned query out of errors * fix(cli): a redirect-limit download error keeps a signed query out of its message * test(cli): the redirect-limit leak test types its rejection * docs(aws-lambda): header comments cut to what the code cannot say * chore(aws-lambda): size budget fits both the CI and the release package
253 lines
10 KiB
YAML
253 lines
10 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:
|
|
publish:
|
|
name: Publish
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 20
|
|
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
|
|
|
|
- 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: 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
|