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
80 changes: 80 additions & 0 deletions .github/scripts/update-template-versions.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Syncs template dependency versions to the versions declared in the openui
// monorepo checkout (OPENUI_DIR) — the repo is the source of truth for what
// was published (npm registry metadata can lag right after a publish).
// Regenerates template lockfiles; the workflow opens the diff as a PR.
import { execSync } from "node:child_process";
import fs from "node:fs";
import path from "node:path";

// True when a > b, comparing plain x.y.z triples.
function isNewer(a, b) {
const pa = a.split(".").map(Number);
const pb = b.split(".").map(Number);
for (let i = 0; i < 3; i++) {
if ((pa[i] ?? 0) !== (pb[i] ?? 0)) return (pa[i] ?? 0) > (pb[i] ?? 0);
}
return false;
}

const openuiDir = process.env.OPENUI_DIR;
if (!openuiDir) {
console.error("OPENUI_DIR env var is required (path to a thesysdev/openui checkout).");
process.exit(1);
}

// map of every @openuidev/* workspace package.
const workspaceVersions = new Map();
for (const dir of fs.readdirSync(path.join(openuiDir, "packages"))) {
const pkgPath = path.join(openuiDir, "packages", dir, "package.json");
if (!fs.existsSync(pkgPath)) continue;
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
if (!pkg.name?.startsWith("@openuidev/") || !pkg.version || pkg.private) continue;
workspaceVersions.set(pkg.name, pkg.version);
}

// index.json is this repo's source of truth for which templates exist.
const index = JSON.parse(fs.readFileSync("index.json", "utf8"));

const changes = [];
for (const { name: template } of index.templates) {
const pkgPath = path.join(template, "package.json");
if (!fs.existsSync(pkgPath)) continue;
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));

let templateChanged = false;
for (const section of ["dependencies", "devDependencies"]) {
const deps = pkg[section];
if (!deps) continue;
for (const [name, range] of Object.entries(deps)) {
const target = workspaceVersions.get(name);
if (!target) continue;
// Only plain versions and simple ^/~ ranges; leave link:/file:/etc alone.
if (!/^[\^~]?\d/.test(range)) continue;
const prefix = /^[\^~]/.test(range) ? range[0] : "";
const current = range.replace(/^[\^~]/, "");
if (current === target) continue;
// Never downgrade: a template can be deliberately ahead of the
// workspace (hotfix landed here first).
if (!isNewer(target, current)) continue;
deps[name] = `${prefix}${target}`;
templateChanged = true;
changes.push(`${template}: ${name} ${range} -> ${deps[name]}`);
}
}
if (!templateChanged) continue;

fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
if (fs.existsSync(path.join(template, "package-lock.json"))) {
execSync("npm install --package-lock-only --ignore-scripts --no-audit --no-fund", {
cwd: template,
stdio: "inherit",
});
}
}

if (changes.length === 0) {
console.log("Templates already match the openui workspace versions.");
} else {
console.log("Updated:");
for (const change of changes) console.log(` ${change}`);
}
51 changes: 51 additions & 0 deletions .github/workflows/update-template-versions.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
name: Update Template Versions

# Event-based sync: openui's publish workflow sends a repository_dispatch
# here after a successful package publish. The openui checkout is pinned to
# the publish commit (client_payload.sha) so versions match exactly what
# shipped. The PR is opened in this repo with the default token.
on:
repository_dispatch:
types: [package-published]
workflow_dispatch:

permissions:
contents: write
pull-requests: write

jobs:
bump-template-versions:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6

# openui is public — a read-only checkout needs no token.
# .openui-checkout is gitignored so create-pull-request ignores it.
- uses: actions/checkout@v6
with:
repository: thesysdev/openui
ref: ${{ github.event.client_payload.sha || 'main' }}
path: .openui-checkout

- uses: actions/setup-node@v6
with:
node-version: 22

- name: Sync template versions to openui workspace versions
env:
OPENUI_DIR: .openui-checkout
run: node .github/scripts/update-template-versions.mjs

- name: Open PR with the bumps
uses: peter-evans/create-pull-request@v7
with:
branch: bot/update-template-versions
title: "chore: bump template versions to latest published packages"
commit-message: "chore: bump template versions to latest published packages"
body: |
Automated follow-up to a package publish in thesysdev/openui.

- Syncs `@openuidev/*` versions in the templates to the published workspace versions
- Regenerates template `package-lock.json` files
- Merging this ships the new versions to `openui create` (templates are fetched from main)
delete-branch: true
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,6 @@ build/

next-env.d.ts
*.tsbuildinfo
*/**/pnpm-lock.yaml
*/**/pnpm-lock.yaml
# Nested openui checkout used by the version-sync workflow
.openui-checkout/