Merge pull request #13 from heygen-com/03-23-ci_add_tag-triggered_npm_publish_workflow_with_oidc_provenance

ci: add CI pipeline, npm publish workflow, and release tooling
This commit is contained in:
James Russo
2026-03-22 20:23:46 -07:00
committed by GitHub
6 changed files with 312 additions and 5 deletions
+115
View File
@@ -0,0 +1,115 @@
name: CI
on:
pull_request:
push:
branches: [main]
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
name: Build
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: 10
- uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm build
typecheck:
name: Typecheck
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: 10
- uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm build
- run: pnpm -r typecheck
test-core:
name: "Test: core"
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: 10
- uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm --filter @hyperframes/core test -- --coverage
test-engine:
name: "Test: engine"
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: 10
- uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm --filter @hyperframes/engine test
test-runtime-contract:
name: "Test: runtime contract"
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: 10
- uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm --filter @hyperframes/core test:hyperframe-runtime-ci
semantic-pr-title:
name: Semantic PR title
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: amannn/action-semantic-pull-request@v5
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
types: |
feat
fix
docs
style
refactor
perf
test
build
ci
chore
revert
+53
View File
@@ -0,0 +1,53 @@
name: Publish to npm
on:
push:
tags:
- "v*"
jobs:
publish:
name: Publish
runs-on: ubuntu-latest
timeout-minutes: 10
environment: npm-publish
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: 10
- uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
registry-url: "https://registry.npmjs.org"
- run: pnpm install --frozen-lockfile
- run: pnpm build
- name: Publish @hyperframes/core
run: pnpm --filter @hyperframes/core publish --access public --provenance --no-git-checks
env:
NPM_CONFIG_PROVENANCE: "true"
- name: Publish @hyperframes/engine
run: pnpm --filter @hyperframes/engine publish --access public --provenance --no-git-checks
env:
NPM_CONFIG_PROVENANCE: "true"
- name: Publish @hyperframes/producer
run: pnpm --filter @hyperframes/producer publish --access public --provenance --no-git-checks
env:
NPM_CONFIG_PROVENANCE: "true"
- name: Publish @hyperframes/studio
run: pnpm --filter @hyperframes/studio publish --access public --provenance --no-git-checks
env:
NPM_CONFIG_PROVENANCE: "true"
- name: Publish hyperframes (CLI)
run: pnpm --filter hyperframes publish --access public --provenance --no-git-checks
env:
NPM_CONFIG_PROVENANCE: "true"
+61 -1
View File
@@ -9,7 +9,67 @@ Thanks for your interest in contributing to Hyperframes! This guide will help yo
3. Install dependencies: `pnpm install`
4. Create a branch: `git checkout -b my-feature`
<!-- TODO: Fill in development setup details once packages are ported -->
## Development
```bash
pnpm install # Install all dependencies
pnpm dev # Run the studio (composition editor)
pnpm build # Build all packages
pnpm -r typecheck # Type-check all packages
```
### Running Tests
```bash
pnpm --filter @hyperframes/core test # Core unit tests (vitest)
pnpm --filter @hyperframes/engine test # Engine unit tests (vitest)
pnpm --filter @hyperframes/core test:hyperframe-runtime-ci # Runtime contract tests
```
## Pull Requests
- Use [conventional commit](https://www.conventionalcommits.org/) format for PR titles (e.g., `feat: add timeline export`, `fix: resolve seek overflow`)
- CI must pass before merge (build, typecheck, tests, semantic PR title)
- PRs require at least 1 approval
## Packages
| Package | Description |
|---|---|
| `@hyperframes/core` | Types, HTML generation, runtime, linter |
| `@hyperframes/engine` | Seekable page-to-video capture engine |
| `@hyperframes/producer` | Full rendering pipeline (capture + encode) |
| `@hyperframes/studio` | Composition editor UI |
| `hyperframes` | CLI for creating, previewing, and rendering |
## Releasing (Maintainers)
All packages use **fixed versioning** — every release bumps all packages to the same version.
### Steps
```bash
# 1. Bump version, commit, and tag
pnpm set-version 0.1.1 --tag
# 2. Push to trigger the publish workflow
git push origin main --tags
```
The `v*` tag triggers CI, which validates (build + typecheck + tests) then publishes all packages to npm with provenance attestation.
### Without `--tag` (manual control)
```bash
# 1. Bump versions only (no commit/tag)
pnpm set-version 0.1.1
# 2. Review changes, commit yourself
git add -A
git commit -m "chore: release v0.1.1"
git tag v0.1.1
git push origin main --tags
```
## Reporting Issues
+2 -1
View File
@@ -8,7 +8,8 @@
"build:producer": "pnpm --filter @hyperframes/producer build",
"studio": "pnpm --filter @hyperframes/studio dev",
"build:hyperframes-runtime": "pnpm --filter @hyperframes/core build:hyperframes-runtime",
"build:hyperframes-runtime:modular": "pnpm --filter @hyperframes/core build:hyperframes-runtime:modular"
"build:hyperframes-runtime:modular": "pnpm --filter @hyperframes/core build:hyperframes-runtime:modular",
"set-version": "tsx scripts/set-version.ts"
},
"devDependencies": {
"@types/node": "^25.0.10",
+3 -3
View File
@@ -128,7 +128,7 @@ importers:
specifier: ^1.13.0
version: 1.19.11(hono@4.12.8)
'@hyperframes/core':
specifier: workspace:*
specifier: workspace:^
version: link:../core
hono:
specifier: ^4.6.0
@@ -192,10 +192,10 @@ importers:
specifier: ^1.13.0
version: 1.19.11(hono@4.12.8)
'@hyperframes/core':
specifier: workspace:*
specifier: workspace:^
version: link:../core
'@hyperframes/engine':
specifier: workspace:*
specifier: workspace:^
version: link:../engine
hono:
specifier: ^4.6.0
+78
View File
@@ -0,0 +1,78 @@
#!/usr/bin/env tsx
/**
* Set the version across all publishable packages in the monorepo.
*
* Usage:
* pnpm set-version 0.1.1
* pnpm set-version 0.1.1 --tag # also creates a git commit and tag
*
* All packages share a single version number (fixed versioning).
*/
import { readFileSync, writeFileSync } from "fs";
import { join } from "path";
import { execSync } from "child_process";
const PACKAGES = [
"packages/core",
"packages/engine",
"packages/producer",
"packages/studio",
"packages/cli",
];
const ROOT = join(import.meta.dirname, "..");
function main() {
const args = process.argv.slice(2);
const version = args.find((a) => !a.startsWith("--"));
const shouldTag = args.includes("--tag");
if (!version) {
console.error("Usage: pnpm set-version <version> [--tag]");
console.error("Example: pnpm set-version 0.1.1 --tag");
process.exit(1);
}
if (!/^\d+\.\d+\.\d+(-[\w.]+)?$/.test(version)) {
console.error(`Invalid semver: ${version}`);
process.exit(1);
}
// Update each package.json
for (const pkg of PACKAGES) {
const pkgPath = join(ROOT, pkg, "package.json");
const content = JSON.parse(readFileSync(pkgPath, "utf-8"));
const oldVersion = content.version;
content.version = version;
writeFileSync(pkgPath, JSON.stringify(content, null, 2) + "\n");
console.log(` ${content.name}: ${oldVersion} -> ${version}`);
}
console.log(`\nSet ${PACKAGES.length} packages to v${version}`);
if (shouldTag) {
// Verify working tree is clean (aside from the version bumps we just made)
const status = execSync("git status --porcelain", {
cwd: ROOT,
encoding: "utf-8",
}).trim();
const unexpected = status
.split("\n")
.filter((line) => line && !PACKAGES.some((pkg) => line.includes(pkg)));
if (unexpected.length > 0) {
console.error("\nUnexpected uncommitted changes:");
unexpected.forEach((line) => console.error(` ${line}`));
console.error("Commit or stash these before tagging.");
process.exit(1);
}
execSync(`git add ${PACKAGES.map((p) => join(p, "package.json")).join(" ")}`, { cwd: ROOT, stdio: "inherit" });
execSync(`git commit -m "chore: release v${version}"`, { cwd: ROOT, stdio: "inherit" });
execSync(`git tag v${version}`, { cwd: ROOT, stdio: "inherit" });
console.log(`\nCreated commit and tag v${version}`);
console.log(`Run 'git push origin main --tags' to trigger the publish workflow.`);
}
}
main();