Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 115 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -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 changes: 53 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -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"
62 changes: 61 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
6 changes: 3 additions & 3 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

78 changes: 78 additions & 0 deletions scripts/set-version.ts
Original file line number Diff line number Diff line change
@@ -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();
Loading