From 38c3a75b5b591460178cd03359f7905377ae7615 Mon Sep 17 00:00:00 2001 From: Adam Poit Date: Fri, 17 Jul 2026 23:04:20 -0700 Subject: [PATCH] Keep bundled workflows aligned with package version --- .github/workflows/ci.yml | 3 + .github/workflows/publish.yml | 3 + examples/promote-tested-sync.yml | 4 +- examples/sync-upstream.yml | 2 +- package-lock.json | 4 +- package.json | 7 +- scripts/sync-skill-assets.ts | 79 ++++++++++++++++--- .../assets/promote-tested-sync.yml | 4 +- .../assets/sync-upstream.yml | 2 +- 9 files changed, 83 insertions(+), 25 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index eace1d1..b512757 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,5 +21,8 @@ jobs: - name: Install dependencies run: npm ci + - name: Check generated artifacts + run: npm run artifacts:check + - name: Run tests run: npm test diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index d6e15d8..caa560a 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -24,6 +24,9 @@ jobs: - name: Install dependencies run: npm ci + - name: Check generated artifacts + run: npm run artifacts:check + - name: Run tests run: npm test diff --git a/examples/promote-tested-sync.yml b/examples/promote-tested-sync.yml index 3410f45..2f1982e 100644 --- a/examples/promote-tested-sync.yml +++ b/examples/promote-tested-sync.yml @@ -25,8 +25,6 @@ jobs: node-version: '22' - name: Run patchlane promote - run: npx patchlane@0.4.0 promote + run: npx patchlane@0.4.1 promote env: - BASE_BRANCH: main - SYNC_BRANCH: sync/integration EXPECTED_SYNC_SHA: ${{ github.event.workflow_run.head_sha }} diff --git a/examples/sync-upstream.yml b/examples/sync-upstream.yml index 2908bb5..0a042d2 100644 --- a/examples/sync-upstream.yml +++ b/examples/sync-upstream.yml @@ -38,7 +38,7 @@ jobs: node-version: '22' - name: Run patchlane sync - run: npx patchlane@0.4.0 sync + run: npx patchlane@0.4.1 sync env: UPSTREAM_SOURCE: ${{ inputs.source }} PATCH_REFS: ${{ inputs.patch_refs }} diff --git a/package-lock.json b/package-lock.json index cf3cb73..e896f08 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "patchlane", - "version": "0.4.0", + "version": "0.4.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "patchlane", - "version": "0.4.0", + "version": "0.4.1", "license": "MIT", "dependencies": { "cac": "^7.0.0", diff --git a/package.json b/package.json index bc6ccad..aa3c3ff 100644 --- a/package.json +++ b/package.json @@ -13,13 +13,16 @@ "node": ">=22" }, "scripts": { - "build": "node --experimental-strip-types ./scripts/sync-skill-assets.ts && tsc -p tsconfig.json", + "artifacts:sync": "node --experimental-strip-types ./scripts/sync-skill-assets.ts", + "artifacts:check": "node --experimental-strip-types ./scripts/sync-skill-assets.ts --check", + "build": "npm run artifacts:sync && tsc -p tsconfig.json", "changelog": "node --experimental-strip-types ./scripts/changelog.ts --print", "changelog:raw": "node --experimental-strip-types ./scripts/raw-changelog.ts", "release": "gh workflow run draft-release.yml", "test": "npm run build && tsc -p tsconfig.test.json && vitest run", "format": "prettier --write .", - "format:check": "prettier --check ." + "format:check": "prettier --check .", + "prepublishOnly": "npm run artifacts:check" }, "devDependencies": { "@types/node": "^25.6.0", diff --git a/scripts/sync-skill-assets.ts b/scripts/sync-skill-assets.ts index 2aed6e0..f11975c 100644 --- a/scripts/sync-skill-assets.ts +++ b/scripts/sync-skill-assets.ts @@ -1,18 +1,71 @@ -import { copyFileSync, mkdirSync } from 'node:fs'; +import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'; import path from 'node:path'; -import { fileURLToPath } from 'node:url'; +import { format } from 'prettier'; +import type { PatchlaneConfig } from '../src/config.ts'; +import { renderPromotionWorkflow, renderSyncWorkflow } from '../src/workflow-templates.ts'; -const rootDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); +const rootDir = path.resolve(import.meta.dirname, '..'); +const check = process.argv.includes('--check'); +const packagePath = path.join(rootDir, 'package.json'); +const packageJson = JSON.parse(readFileSync(packagePath, 'utf8')) as { version?: unknown }; +if (typeof packageJson.version !== 'string' || !packageJson.version) { + throw new Error('Patchlane package version is missing.'); +} + +const exampleConfig: PatchlaneConfig = { + upstreamOwner: 'example', + upstreamRepo: 'upstream', + source: 'release:latest', + baseBranch: 'main', + syncBranch: 'sync/integration', + patchRefs: ['patch/sync', 'patch/ci'], + ciWorkflow: 'Fork CI', +}; + +const prettierOptions = { + filepath: path.join(rootDir, 'examples/sync-upstream.yml'), + singleQuote: true, +}; +const syncWorkflow = await format(renderSyncWorkflow(exampleConfig, packageJson.version), prettierOptions); +const promotionWorkflow = await format(renderPromotionWorkflow(exampleConfig, packageJson.version), prettierOptions); +const forkCiWorkflow = readFileSync(path.join(rootDir, 'examples/fork-ci.yml'), 'utf8'); + +const generatedFiles = new Map([ + ['examples/sync-upstream.yml', syncWorkflow], + ['examples/promote-tested-sync.yml', promotionWorkflow], + ['skills/patchlane-fork-setup/assets/sync-upstream.yml', syncWorkflow], + ['skills/patchlane-fork-setup/assets/promote-tested-sync.yml', promotionWorkflow], + ['skills/patchlane-fork-setup/assets/fork-ci.yml', forkCiWorkflow], +]); -const fileMappings = [ - ['examples/sync-upstream.yml', 'skills/patchlane-fork-setup/assets/sync-upstream.yml'], - ['examples/fork-ci.yml', 'skills/patchlane-fork-setup/assets/fork-ci.yml'], - ['examples/promote-tested-sync.yml', 'skills/patchlane-fork-setup/assets/promote-tested-sync.yml'], -]; +const stalePaths: string[] = []; +for (const [relativePath, contents] of generatedFiles) { + const filePath = path.join(rootDir, relativePath); + if (existsSync(filePath) && readFileSync(filePath, 'utf8') === contents) continue; + if (check) { + stalePaths.push(relativePath); + } else { + mkdirSync(path.dirname(filePath), { recursive: true }); + writeFileSync(filePath, contents); + } +} + +const packageLockPath = path.join(rootDir, 'package-lock.json'); +const packageLock = JSON.parse(readFileSync(packageLockPath, 'utf8')) as { + version?: unknown; + packages?: { ''?: { version?: unknown } }; +}; +if (packageLock.version !== packageJson.version || packageLock.packages?.['']?.version !== packageJson.version) { + if (check) { + stalePaths.push('package-lock.json'); + } else { + packageLock.version = packageJson.version; + if (!packageLock.packages?.['']) throw new Error('Package lock root package is missing.'); + packageLock.packages[''].version = packageJson.version; + writeFileSync(packageLockPath, `${JSON.stringify(packageLock, null, 2)}\n`); + } +} -for (const [sourceRelativePath, destinationRelativePath] of fileMappings) { - const sourcePath = path.join(rootDir, sourceRelativePath); - const destinationPath = path.join(rootDir, destinationRelativePath); - mkdirSync(path.dirname(destinationPath), { recursive: true }); - copyFileSync(sourcePath, destinationPath); +if (stalePaths.length) { + throw new Error(`Generated artifacts are stale:\n${stalePaths.map((file) => `- ${file}`).join('\n')}`); } diff --git a/skills/patchlane-fork-setup/assets/promote-tested-sync.yml b/skills/patchlane-fork-setup/assets/promote-tested-sync.yml index 3410f45..2f1982e 100644 --- a/skills/patchlane-fork-setup/assets/promote-tested-sync.yml +++ b/skills/patchlane-fork-setup/assets/promote-tested-sync.yml @@ -25,8 +25,6 @@ jobs: node-version: '22' - name: Run patchlane promote - run: npx patchlane@0.4.0 promote + run: npx patchlane@0.4.1 promote env: - BASE_BRANCH: main - SYNC_BRANCH: sync/integration EXPECTED_SYNC_SHA: ${{ github.event.workflow_run.head_sha }} diff --git a/skills/patchlane-fork-setup/assets/sync-upstream.yml b/skills/patchlane-fork-setup/assets/sync-upstream.yml index 2908bb5..0a042d2 100644 --- a/skills/patchlane-fork-setup/assets/sync-upstream.yml +++ b/skills/patchlane-fork-setup/assets/sync-upstream.yml @@ -38,7 +38,7 @@ jobs: node-version: '22' - name: Run patchlane sync - run: npx patchlane@0.4.0 sync + run: npx patchlane@0.4.1 sync env: UPSTREAM_SOURCE: ${{ inputs.source }} PATCH_REFS: ${{ inputs.patch_refs }}