From 0ff85cda2e6d3b240f39f8a4fa4b15730fbb1bb5 Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Fri, 21 Aug 2026 02:29:15 -0700 Subject: [PATCH] fix(release): align npm authority selection --- .github/workflows/plugin-npm-release.yml | 5 ++- scripts/lib/plugin-npm-release.ts | 14 +++---- scripts/lib/plugin-publication-candidates.ts | 40 +++++++++++++++++-- scripts/plugin-npm-release-check.ts | 4 +- test/plugin-clawhub-release.test.ts | 3 +- test/plugin-npm-release.test.ts | 9 +++-- ...lugin-npm-extended-stable-workflow.test.ts | 20 ++++++---- 7 files changed, 68 insertions(+), 27 deletions(-) diff --git a/.github/workflows/plugin-npm-release.yml b/.github/workflows/plugin-npm-release.yml index 2356f8609da0..51168c8646a0 100644 --- a/.github/workflows/plugin-npm-release.yml +++ b/.github/workflows/plugin-npm-release.yml @@ -7,11 +7,12 @@ on: - main paths: - ".github/workflows/plugin-npm-release.yml" + - ".github/actions/setup-node-env/**" - "extensions/**" - "package.json" - "pnpm-lock.yaml" - - "packages/normalization-core/**" - - "packages/plugin-package-contract/src/index.ts" + - "packages/normalization-core/src/**" + - "packages/plugin-package-contract/src/**" - "scripts/generate-npm-package-lock.mjs" - "scripts/generate-npm-package-lock.mts" - "scripts/lib/npm-publish-plan.mjs" diff --git a/scripts/lib/plugin-npm-release.ts b/scripts/lib/plugin-npm-release.ts index b09798efaf76..9981e40c9b8e 100644 --- a/scripts/lib/plugin-npm-release.ts +++ b/scripts/lib/plugin-npm-release.ts @@ -6,8 +6,8 @@ import { join, resolve } from "node:path"; import { expectDefined } from "../../packages/normalization-core/src/expect.js"; import { collectExtensionPackageJsonCandidates, - hasPluginPublicationSharedAuthorityChanges, - PLUGIN_PUBLICATION_SHARED_AUTHORITY_PATHS, + hasPluginNpmReleaseAuthorityChanges, + PLUGIN_NPM_RELEASE_AUTHORITY_PATHS, } from "./plugin-publication-candidates.ts"; import { assertUniquePublishablePluginPackageSources, @@ -49,8 +49,8 @@ export type GitRangeSelection = { }; export type PluginNpmGitRangeSelection = { + authorityChanged: boolean; changedExtensionIds: string[]; - sharedAuthorityChanged: boolean; }; type ParsedPluginReleaseArgs = { @@ -338,11 +338,11 @@ export function collectPluginNpmGitRangeSelection(params: { const changedPaths = collectChangedPathsFromGitRange({ rootDir: params.rootDir, gitRange: params.gitRange, - pathspecs: ["extensions", ...PLUGIN_PUBLICATION_SHARED_AUTHORITY_PATHS], + pathspecs: ["extensions", ...PLUGIN_NPM_RELEASE_AUTHORITY_PATHS], }); return { + authorityChanged: hasPluginNpmReleaseAuthorityChanges(changedPaths), changedExtensionIds: collectChangedExtensionIdsFromPaths(changedPaths), - sharedAuthorityChanged: hasPluginPublicationSharedAuthorityChanges(changedPaths), }; } @@ -506,7 +506,7 @@ export function collectPluginReleasePlan(params?: { extensionIds: params?.selectionMode === "all-publishable" || !gitRangeSelection || - gitRangeSelection.sharedAuthorityChanged + gitRangeSelection.authorityChanged ? undefined : gitRangeSelection.changedExtensionIds, packageNames: params?.selection && params.selection.length > 0 ? params.selection : undefined, @@ -521,7 +521,7 @@ export function collectPluginReleasePlan(params?: { selection: params.selection, }) : gitRangeSelection - ? gitRangeSelection.sharedAuthorityChanged + ? gitRangeSelection.authorityChanged ? allPublishable : resolveChangedPublishablePluginPackages({ plugins: allPublishable, diff --git a/scripts/lib/plugin-publication-candidates.ts b/scripts/lib/plugin-publication-candidates.ts index c6851db57c5c..2473d4b41db3 100644 --- a/scripts/lib/plugin-publication-candidates.ts +++ b/scripts/lib/plugin-publication-candidates.ts @@ -10,22 +10,54 @@ import type { export const PLUGIN_PUBLICATION_SHARED_AUTHORITY_PATHS = [ "package.json", "pnpm-lock.yaml", - "packages/normalization-core/src/string-coerce.ts", - "packages/plugin-package-contract/src/index.ts", + "packages/normalization-core/src", + "packages/plugin-package-contract/src", "scripts/lib/npm-publish-plan.mjs", "scripts/lib/plugin-publication-candidates.ts", "scripts/lib/plugin-publication-collector.ts", "scripts/lib/release-version.mjs", ] as const; -export function hasPluginPublicationSharedAuthorityChanges(paths: readonly string[]): boolean { +export const PLUGIN_NPM_RELEASE_AUTHORITY_PATHS = [ + ...PLUGIN_PUBLICATION_SHARED_AUTHORITY_PATHS, + ".github/actions/setup-node-env", + ".github/workflows/plugin-npm-release.yml", + "scripts/generate-npm-package-lock.mjs", + "scripts/generate-npm-package-lock.mts", + "scripts/lib/actions-artifact-archive.mjs", + "scripts/lib/npm-json-output.mts", + "scripts/lib/plugin-npm-package-manifest.mjs", + "scripts/lib/plugin-npm-package-manifest.mts", + "scripts/lib/plugin-npm-release.ts", + "scripts/lib/tsx-cli-shim.mjs", + "scripts/plugin-npm-publish.sh", + "scripts/plugin-npm-release-check.ts", + "scripts/plugin-npm-release-plan.ts", + "scripts/plugin-publication-artifact.mjs", + "scripts/release-tooling-identity.d.mts", + "scripts/release-tooling-identity.mjs", + "scripts/verify-plugin-npm-published-runtime.mts", +] as const; + +function hasAuthorityPathChanges( + paths: readonly string[], + authorityPaths: readonly string[], +): boolean { return paths.some((path) => - PLUGIN_PUBLICATION_SHARED_AUTHORITY_PATHS.some( + authorityPaths.some( (authorityPath) => path === authorityPath || path.startsWith(`${authorityPath}/`), ), ); } +export function hasPluginPublicationSharedAuthorityChanges(paths: readonly string[]): boolean { + return hasAuthorityPathChanges(paths, PLUGIN_PUBLICATION_SHARED_AUTHORITY_PATHS); +} + +export function hasPluginNpmReleaseAuthorityChanges(paths: readonly string[]): boolean { + return hasAuthorityPathChanges(paths, PLUGIN_NPM_RELEASE_AUTHORITY_PATHS); +} + function readPluginPackageJson(absolutePath: string, repoPath: string): PluginPackageJson { let raw: string; try { diff --git a/scripts/plugin-npm-release-check.ts b/scripts/plugin-npm-release-check.ts index 3c06730616fb..5ed00f3adc9d 100644 --- a/scripts/plugin-npm-release-check.ts +++ b/scripts/plugin-npm-release-check.ts @@ -25,7 +25,7 @@ function runPluginNpmReleaseCheck(argv: string[]) { extensionIds: selectionMode === "all-publishable" || !gitRangeSelection || - gitRangeSelection.sharedAuthorityChanged + gitRangeSelection.authorityChanged ? undefined : gitRangeSelection.changedExtensionIds, packageNames: selection.length > 0 ? selection : undefined, @@ -40,7 +40,7 @@ function runPluginNpmReleaseCheck(argv: string[]) { selection, }) : gitRangeSelection - ? gitRangeSelection.sharedAuthorityChanged + ? gitRangeSelection.authorityChanged ? publishable : resolveChangedPublishablePluginPackages({ plugins: publishable, diff --git a/test/plugin-clawhub-release.test.ts b/test/plugin-clawhub-release.test.ts index 539346233f07..a53e8866f551 100644 --- a/test/plugin-clawhub-release.test.ts +++ b/test/plugin-clawhub-release.test.ts @@ -424,7 +424,8 @@ describe("resolveSelectedClawHubPublishablePluginPackages", () => { }); it.each([ - "packages/normalization-core/src/string-coerce.ts", + "packages/normalization-core/src/record-coerce.ts", + "packages/plugin-package-contract/src/schema.ts", "scripts/lib/plugin-publication-candidates.ts", "scripts/lib/plugin-publication-collector.ts", ])("selects all publishable plugins when %s changes", (changedPath) => { diff --git a/test/plugin-npm-release.test.ts b/test/plugin-npm-release.test.ts index 2fa2fdade000..dab700d979d5 100644 --- a/test/plugin-npm-release.test.ts +++ b/test/plugin-npm-release.test.ts @@ -824,10 +824,13 @@ describe("collectChangedExtensionIdsFromPaths", () => { describe("collectPluginNpmGitRangeSelection", () => { it.each([ - "packages/normalization-core/src/string-coerce.ts", + ".github/actions/setup-node-env/action.yml", + "packages/normalization-core/src/record-coerce.ts", + "packages/plugin-package-contract/src/schema.ts", "scripts/lib/plugin-publication-candidates.ts", "scripts/lib/plugin-publication-collector.ts", - ])("selects all publishable plugins for a shared-only %s change", (changedPath) => { + "scripts/plugin-npm-release-plan.ts", + ])("selects all publishable plugins for an authority-only %s change", (changedPath) => { const repoDir = makeTempRepoRoot(tempDirs, "openclaw-plugin-npm-range-"); const absolutePath = join(repoDir, changedPath); mkdirSync(dirname(absolutePath), { recursive: true }); @@ -878,8 +881,8 @@ describe("collectPluginNpmGitRangeSelection", () => { gitRange: { baseRef, headRef }, }), ).toEqual({ + authorityChanged: true, changedExtensionIds: [], - sharedAuthorityChanged: true, }); }); }); diff --git a/test/scripts/plugin-npm-extended-stable-workflow.test.ts b/test/scripts/plugin-npm-extended-stable-workflow.test.ts index 776eba6669f7..3b9dc07d46c5 100644 --- a/test/scripts/plugin-npm-extended-stable-workflow.test.ts +++ b/test/scripts/plugin-npm-extended-stable-workflow.test.ts @@ -1,7 +1,7 @@ import { readFileSync } from "node:fs"; import { describe, expect, it } from "vitest"; import { parse } from "yaml"; -import { PLUGIN_PUBLICATION_SHARED_AUTHORITY_PATHS } from "../../scripts/lib/plugin-publication-candidates.ts"; +import { PLUGIN_NPM_RELEASE_AUTHORITY_PATHS } from "../../scripts/lib/plugin-publication-candidates.ts"; const workflowPath = ".github/workflows/plugin-npm-release.yml"; const metaPackagePath = "extensions/meta/package.json"; @@ -64,14 +64,18 @@ function workflowPathPatternCovers(pattern: string, path: string): boolean { } describe("plugin npm extended-stable workflow", () => { - it("triggers for every shared plugin publication authority", () => { - const triggerPaths = workflow().on?.push?.paths ?? []; - for (const authorityPath of PLUGIN_PUBLICATION_SHARED_AUTHORITY_PATHS) { - expect( + it("keeps push triggers aligned with npm publication authorities", () => { + const triggerPaths = (workflow().on?.push?.paths ?? []).filter( + (path) => path !== "extensions/**", + ); + expect( + triggerPaths.map((path) => (path.endsWith("/**") ? path.slice(0, -3) : path)).toSorted(), + ).toEqual([...PLUGIN_NPM_RELEASE_AUTHORITY_PATHS].toSorted()); + expect( + PLUGIN_NPM_RELEASE_AUTHORITY_PATHS.every((authorityPath) => triggerPaths.some((pattern) => workflowPathPatternCovers(pattern, authorityPath)), - authorityPath, - ).toBe(true); - } + ), + ).toBe(true); }); it("exposes only the default behavior and closed extended-stable override", () => {