From 10a6b5e11ac2d2c1d0686742a972948ec2061b03 Mon Sep 17 00:00:00 2001 From: Marc Mintel Date: Mon, 2 Feb 2026 01:02:14 +0100 Subject: [PATCH] feat: Implement tag-based version synchronization and direct publishing for releases. --- .gitea/workflows/release.yml | 15 ++++++++-- package.json | 3 ++ pnpm-lock.yaml | 56 ++++++++++++++++++++++++------------ scripts/sync-versions.ts | 53 ++++++++++++++++++++++++++++++++++ 4 files changed, 105 insertions(+), 22 deletions(-) create mode 100644 scripts/sync-versions.ts diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index 8f4a26b..2b61720 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -4,6 +4,8 @@ on: push: branches: - main + tags: + - 'v*' jobs: release: @@ -31,6 +33,13 @@ jobs: - name: Install dependencies run: pnpm install --frozen-lockfile - - name: Create Release Pull Request or Publish - id: changesets - run: pnpm release + - name: Release Packages + run: | + if [[ "${{ github.ref_type }}" == "tag" ]]; then + echo "🏷️ Tag detected, performing sync release..." + pnpm sync-versions + pnpm release:tag + else + echo "🚀 Push detected, looking for changesets..." + pnpm release + fi diff --git a/package.json b/package.json index 3eb2144..2c6abdf 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,9 @@ "test": "pnpm -r test", "changeset": "changeset", "version-packages": "changeset version", + "sync-versions": "tsx scripts/sync-versions.ts", "release": "pnpm build && changeset publish", + "release:tag": "pnpm build && pnpm -r publish --no-git-checks --access public", "prepare": "husky" }, "devDependencies": { @@ -32,6 +34,7 @@ "jsdom": "^27.4.0", "lint-staged": "^16.2.7", "prettier": "^3.8.1", + "tsx": "^4.21.0", "typescript": "^5.0.0", "typescript-eslint": "^8.54.0", "vitest": "^4.0.18" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 46e3de9..617c741 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -37,7 +37,7 @@ importers: version: 19.2.3(@types/react@19.2.10) '@vitejs/plugin-react': specifier: ^5.1.2 - version: 5.1.2(vite@7.3.1(@types/node@20.19.30)(jiti@2.6.1)(terser@5.46.0)(yaml@2.8.2)) + version: 5.1.2(vite@7.3.1(@types/node@20.19.30)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)) eslint: specifier: ^9.39.2 version: 9.39.2(jiti@2.6.1) @@ -65,6 +65,9 @@ importers: prettier: specifier: ^3.8.1 version: 3.8.1 + tsx: + specifier: ^4.21.0 + version: 4.21.0 typescript: specifier: ^5.0.0 version: 5.9.3 @@ -73,7 +76,7 @@ importers: version: 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) vitest: specifier: ^4.0.18 - version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@20.19.30)(happy-dom@20.4.0)(jiti@2.6.1)(jsdom@27.4.0)(terser@5.46.0)(yaml@2.8.2) + version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@20.19.30)(happy-dom@20.4.0)(jiti@2.6.1)(jsdom@27.4.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) apps/sample-website: dependencies: @@ -138,7 +141,7 @@ importers: version: 2.4.9 tsup: specifier: ^8.0.0 - version: 8.5.1(jiti@2.6.1)(postcss@8.5.6)(typescript@5.9.3)(yaml@2.8.2) + version: 8.5.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2) typescript: specifier: ^5.0.0 version: 5.9.3 @@ -205,7 +208,7 @@ importers: version: 8.5.6 tailwindcss: specifier: ^3.4.17 - version: 3.4.19(yaml@2.8.2) + version: 3.4.19(tsx@4.21.0)(yaml@2.8.2) typescript: specifier: ^5.0.0 version: 5.9.3 @@ -266,7 +269,7 @@ importers: version: 9.39.2(jiti@2.6.1) tsup: specifier: ^8.0.0 - version: 8.5.1(jiti@2.6.1)(postcss@8.5.6)(typescript@5.9.3)(yaml@2.8.2) + version: 8.5.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2) typescript: specifier: ^5.0.0 version: 5.9.3 @@ -4094,6 +4097,11 @@ packages: typescript: optional: true + tsx@4.21.0: + resolution: {integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==} + engines: {node: '>=18.0.0'} + hasBin: true + type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} @@ -6045,7 +6053,7 @@ snapshots: '@unrs/resolver-binding-win32-x64-msvc@1.11.1': optional: true - '@vitejs/plugin-react@5.1.2(vite@7.3.1(@types/node@20.19.30)(jiti@2.6.1)(terser@5.46.0)(yaml@2.8.2))': + '@vitejs/plugin-react@5.1.2(vite@7.3.1(@types/node@20.19.30)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2))': dependencies: '@babel/core': 7.28.6 '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.6) @@ -6053,7 +6061,7 @@ snapshots: '@rolldown/pluginutils': 1.0.0-beta.53 '@types/babel__core': 7.20.5 react-refresh: 0.18.0 - vite: 7.3.1(@types/node@20.19.30)(jiti@2.6.1)(terser@5.46.0)(yaml@2.8.2) + vite: 7.3.1(@types/node@20.19.30)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) transitivePeerDependencies: - supports-color @@ -6066,13 +6074,13 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.0.3 - '@vitest/mocker@4.0.18(vite@7.3.1(@types/node@20.19.30)(jiti@2.6.1)(terser@5.46.0)(yaml@2.8.2))': + '@vitest/mocker@4.0.18(vite@7.3.1(@types/node@20.19.30)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2))': dependencies: '@vitest/spy': 4.0.18 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.3.1(@types/node@20.19.30)(jiti@2.6.1)(terser@5.46.0)(yaml@2.8.2) + vite: 7.3.1(@types/node@20.19.30)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) '@vitest/pretty-format@4.0.18': dependencies: @@ -7994,20 +8002,22 @@ snapshots: camelcase-css: 2.0.1 postcss: 8.5.6 - postcss-load-config@6.0.1(jiti@1.21.7)(postcss@8.5.6)(yaml@2.8.2): + postcss-load-config@6.0.1(jiti@1.21.7)(postcss@8.5.6)(tsx@4.21.0)(yaml@2.8.2): dependencies: lilconfig: 3.1.3 optionalDependencies: jiti: 1.21.7 postcss: 8.5.6 + tsx: 4.21.0 yaml: 2.8.2 - postcss-load-config@6.0.1(jiti@2.6.1)(postcss@8.5.6)(yaml@2.8.2): + postcss-load-config@6.0.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(yaml@2.8.2): dependencies: lilconfig: 3.1.3 optionalDependencies: jiti: 2.6.1 postcss: 8.5.6 + tsx: 4.21.0 yaml: 2.8.2 postcss-nested@6.2.0(postcss@8.5.6): @@ -8520,7 +8530,7 @@ snapshots: tailwind-merge@2.6.1: {} - tailwindcss@3.4.19(yaml@2.8.2): + tailwindcss@3.4.19(tsx@4.21.0)(yaml@2.8.2): dependencies: '@alloc/quick-lru': 5.2.0 arg: 5.0.2 @@ -8539,7 +8549,7 @@ snapshots: postcss: 8.5.6 postcss-import: 15.1.0(postcss@8.5.6) postcss-js: 4.1.0(postcss@8.5.6) - postcss-load-config: 6.0.1(jiti@1.21.7)(postcss@8.5.6)(yaml@2.8.2) + postcss-load-config: 6.0.1(jiti@1.21.7)(postcss@8.5.6)(tsx@4.21.0)(yaml@2.8.2) postcss-nested: 6.2.0(postcss@8.5.6) postcss-selector-parser: 6.1.2 resolve: 1.22.11 @@ -8626,7 +8636,7 @@ snapshots: tslib@2.8.1: {} - tsup@8.5.1(jiti@2.6.1)(postcss@8.5.6)(typescript@5.9.3)(yaml@2.8.2): + tsup@8.5.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2): dependencies: bundle-require: 5.1.0(esbuild@0.27.2) cac: 6.7.14 @@ -8637,7 +8647,7 @@ snapshots: fix-dts-default-cjs-exports: 1.0.1 joycon: 3.1.1 picocolors: 1.1.1 - postcss-load-config: 6.0.1(jiti@2.6.1)(postcss@8.5.6)(yaml@2.8.2) + postcss-load-config: 6.0.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(yaml@2.8.2) resolve-from: 5.0.0 rollup: 4.57.1 source-map: 0.7.6 @@ -8654,6 +8664,13 @@ snapshots: - tsx - yaml + tsx@4.21.0: + dependencies: + esbuild: 0.27.2 + get-tsconfig: 4.13.1 + optionalDependencies: + fsevents: 2.3.3 + type-check@0.4.0: dependencies: prelude-ls: 1.2.1 @@ -8772,7 +8789,7 @@ snapshots: uuid@9.0.1: {} - vite@7.3.1(@types/node@20.19.30)(jiti@2.6.1)(terser@5.46.0)(yaml@2.8.2): + vite@7.3.1(@types/node@20.19.30)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2): dependencies: esbuild: 0.27.2 fdir: 6.5.0(picomatch@4.0.3) @@ -8785,12 +8802,13 @@ snapshots: fsevents: 2.3.3 jiti: 2.6.1 terser: 5.46.0 + tsx: 4.21.0 yaml: 2.8.2 - vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@20.19.30)(happy-dom@20.4.0)(jiti@2.6.1)(jsdom@27.4.0)(terser@5.46.0)(yaml@2.8.2): + vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@20.19.30)(happy-dom@20.4.0)(jiti@2.6.1)(jsdom@27.4.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2): dependencies: '@vitest/expect': 4.0.18 - '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@20.19.30)(jiti@2.6.1)(terser@5.46.0)(yaml@2.8.2)) + '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@20.19.30)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)) '@vitest/pretty-format': 4.0.18 '@vitest/runner': 4.0.18 '@vitest/snapshot': 4.0.18 @@ -8807,7 +8825,7 @@ snapshots: tinyexec: 1.0.2 tinyglobby: 0.2.15 tinyrainbow: 3.0.3 - vite: 7.3.1(@types/node@20.19.30)(jiti@2.6.1)(terser@5.46.0)(yaml@2.8.2) + vite: 7.3.1(@types/node@20.19.30)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) why-is-node-running: 2.3.0 optionalDependencies: '@opentelemetry/api': 1.9.0 diff --git a/scripts/sync-versions.ts b/scripts/sync-versions.ts new file mode 100644 index 0000000..0f2e8f9 --- /dev/null +++ b/scripts/sync-versions.ts @@ -0,0 +1,53 @@ +import * as fs from "fs"; +import * as path from "path"; +import { execSync } from "child_process"; + +const tag = process.env.GITHUB_REF_NAME || process.env.TAG; + +if (!tag || !tag.startsWith("v")) { + console.error("❌ No valid tag found (must start with v, e.g., v1.0.0)"); + process.exit(1); +} + +const version = tag.replace(/^v/, ""); +console.log(`🚀 Syncing all packages to version: ${version}`); + +const rootPkg = JSON.parse(fs.readFileSync("package.json", "utf-8")); +const packagesDir = "packages"; +const appsDir = "apps"; + +function updatePkg(pkgPath: string) { + if (!fs.existsSync(pkgPath)) return; + const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8")); + pkg.version = version; + + // Also update workspace dependencies if they match our packages + if (pkg.dependencies) { + for (const dep in pkg.dependencies) { + if (pkg.dependencies[dep].startsWith("workspace:")) { + pkg.dependencies[dep] = "workspace:*"; // Keep workspace protocol + } + } + } + + fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n"); + console.log(`✅ Updated ${pkg.name} to ${version}`); +} + +// Update root +rootPkg.version = version; +fs.writeFileSync("package.json", JSON.stringify(rootPkg, null, 2) + "\n"); + +// Update all packages +const packages = fs.readdirSync(packagesDir); +for (const p of packages) { + updatePkg(path.join(packagesDir, p, "package.json")); +} + +// Update all apps +const apps = fs.readdirSync(appsDir); +for (const a of apps) { + updatePkg(path.join(appsDir, a, "package.json")); +} + +console.log("✨ All versions synced!");