feat(aws-lambda): attach the arm64 video image to releases and pin it (#6070)

* 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.
This commit is contained in:
Miguel Ángel
2026-10-23 03:52:35 +00:00
committed by GitHub
parent f6cbf87cdd
commit 8d562a3d91
7 changed files with 186 additions and 30 deletions
@@ -3,8 +3,14 @@ import { createHash } from "node:crypto";
import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { adapterPackageRoot, prebuiltHandler } from "../src/sdk/prebuiltHandler.js";
import { pinHandlerZip } from "./pin-handler-zip.js";
import {
adapterPackageRoot,
prebuiltHandler,
prebuiltVideoImage,
VIDEO_IMAGE_ASSET,
VIDEO_IMAGE_MANIFEST_FILE,
} from "../src/sdk/prebuiltHandler.js";
import { pinHandlerZip, pinReleaseAsset } from "./pin-handler-zip.js";
const examples = join(import.meta.dir, "..", "..", "..", "examples", "aws-lambda");
const realTemplate = readFileSync(join(examples, "template.yaml"), "utf-8");
@@ -93,6 +99,31 @@ describe("pinHandlerZip", () => {
});
});
describe("pinReleaseAsset", () => {
it("pins the arm64 video image beside the handler zip without touching the zip's pin", () => {
const { root, pkgDir } = fixtureRepo();
writeFileSync(join(pkgDir, "dist", "handler.zip"), "zip");
const zipPin = pinHandlerZip(root);
const image = Buffer.from("oci layout tar");
writeFileSync(join(root, "dist", VIDEO_IMAGE_ASSET), image);
const sha256 = createHash("sha256").update(image).digest("hex");
const manifest = pinReleaseAsset(root, VIDEO_IMAGE_ASSET, VIDEO_IMAGE_MANIFEST_FILE);
expect(manifest).toEqual({
version: "9.8.7",
url: "https://github.com/heygen-com/hyperframes/releases/download/v9.8.7/hyperframes-lambda-video-arm64.oci.tar",
sha256,
bytes: image.length,
});
expect(readFileSync(join(root, "dist", "hyperframes-lambda-video-arm64.sha256"), "utf-8")).toBe(
`${sha256} hyperframes-lambda-video-arm64.oci.tar\n`,
);
expect(prebuiltVideoImage(pkgDir)).toEqual(manifest);
expect(prebuiltHandler(pkgDir)?.sha256).toBe(zipPin.sha256);
});
});
describe("prebuiltHandler", () => {
it("reads from its own package root", () => {
expect(adapterPackageRoot()).toBe(join(import.meta.dir, ".."));
@@ -102,4 +133,11 @@ describe("prebuiltHandler", () => {
const { pkgDir } = fixtureRepo();
expect(prebuiltHandler(pkgDir)).toBeUndefined();
});
it("has no video image when the package pins only the zip", () => {
const { root, pkgDir } = fixtureRepo();
writeFileSync(join(pkgDir, "dist", "handler.zip"), "zip");
pinHandlerZip(root);
expect(prebuiltVideoImage(pkgDir)).toBeUndefined();
});
});
+35 -18
View File
@@ -1,6 +1,7 @@
#!/usr/bin/env bun
// Release step: writes the handler ZIP's pin (asset URL + sha256) and the SAM template into the
// package. A retried release run reuses the ZIP already on the release.
// package, or with --video-image the arm64 video image's pin. A retried release run reuses the
// asset already on the release.
import { createHash } from "node:crypto";
import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
import { dirname, join, resolve } from "node:path";
@@ -8,17 +9,42 @@ import { fileURLToPath } from "node:url";
import {
HANDLER_MANIFEST_FILE,
HANDLER_ZIP_ASSET,
type HandlerZipManifest,
PREBUILT_DIR,
type ReleaseAssetManifest,
VIDEO_IMAGE_ASSET,
VIDEO_IMAGE_MANIFEST_FILE,
} from "../src/sdk/prebuiltHandler.js";
import { writePrebuiltTemplate } from "./prebuilt-template.mjs";
export function pinHandlerZip(repoRoot: string): HandlerZipManifest {
/** Hashes `dist/<asset>`, writes its `.sha256` beside it and its pin to `prebuilt/<manifestFile>`. */
export function pinReleaseAsset(
repoRoot: string,
asset: string,
manifestFile: string,
): ReleaseAssetManifest {
const pkgDir = join(repoRoot, "packages", "aws-lambda");
const pkg = JSON.parse(readFileSync(join(pkgDir, "package.json"), "utf-8")) as {
version: string;
repository: { url: string };
};
const bytes = readFileSync(join(repoRoot, "dist", asset));
const sha256 = createHash("sha256").update(bytes).digest("hex");
writeFileSync(join(repoRoot, "dist", `${asset.split(".")[0]}.sha256`), `${sha256} ${asset}\n`);
const manifest: ReleaseAssetManifest = {
version: pkg.version,
url: `${pkg.repository.url}/releases/download/v${pkg.version}/${asset}`,
sha256,
bytes: bytes.length,
};
const outDir = join(pkgDir, PREBUILT_DIR);
mkdirSync(outDir, { recursive: true });
writeFileSync(join(outDir, manifestFile), `${JSON.stringify(manifest, null, 2)}\n`);
return manifest;
}
export function pinHandlerZip(repoRoot: string): ReleaseAssetManifest {
const pkgDir = join(repoRoot, "packages", "aws-lambda");
const asset = join(repoRoot, "dist", HANDLER_ZIP_ASSET);
const built = join(pkgDir, "dist", "handler.zip");
if (existsSync(built)) {
@@ -26,23 +52,14 @@ export function pinHandlerZip(repoRoot: string): HandlerZipManifest {
renameSync(built, asset);
rmSync(`${built}.manifest.json`, { force: true });
}
const bytes = readFileSync(asset);
const sha256 = createHash("sha256").update(bytes).digest("hex");
writeFileSync(asset.replace(/\.zip$/, ".sha256"), `${sha256} ${HANDLER_ZIP_ASSET}\n`);
const outDir = join(pkgDir, PREBUILT_DIR);
writePrebuiltTemplate(repoRoot, outDir);
const manifest: HandlerZipManifest = {
version: pkg.version,
url: `${pkg.repository.url}/releases/download/v${pkg.version}/${HANDLER_ZIP_ASSET}`,
sha256,
bytes: bytes.length,
};
writeFileSync(join(outDir, HANDLER_MANIFEST_FILE), `${JSON.stringify(manifest, null, 2)}\n`);
return manifest;
writePrebuiltTemplate(repoRoot, join(pkgDir, PREBUILT_DIR));
return pinReleaseAsset(repoRoot, HANDLER_ZIP_ASSET, HANDLER_MANIFEST_FILE);
}
if (import.meta.main) {
const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), "../../..");
console.log(JSON.stringify(pinHandlerZip(repoRoot), null, 2));
const manifest = process.argv.includes("--video-image")
? pinReleaseAsset(repoRoot, VIDEO_IMAGE_ASSET, VIDEO_IMAGE_MANIFEST_FILE)
: pinHandlerZip(repoRoot);
console.log(JSON.stringify(manifest, null, 2));
}