diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 34b92a640..b35682ff2 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -27,6 +27,11 @@ on: required: false type: boolean default: false + publish_lazycodex: + description: "Publish the lazycodex npm alias" + required: false + type: boolean + default: true concurrency: ${{ github.workflow }}-${{ github.ref }} @@ -111,6 +116,7 @@ jobs: env: REPO: code-yeongyu/oh-my-openagent WORKFLOW_FILE: publish.yml + PUBLISH_LAZYCODEX: ${{ inputs.publish_lazycodex }} run: | OIDC_TOKEN=$(curl -sH "Authorization: bearer ${ACTIONS_ID_TOKEN_REQUEST_TOKEN}" \ "${ACTIONS_ID_TOKEN_REQUEST_URL}&audience=npm:registry.npmjs.org" \ @@ -122,7 +128,10 @@ jobs: fi PLATFORMS=(darwin-arm64 darwin-x64 darwin-x64-baseline linux-x64 linux-x64-baseline linux-arm64 linux-x64-musl linux-x64-musl-baseline linux-arm64-musl windows-x64 windows-x64-baseline) - ALL_PACKAGES=(oh-my-opencode oh-my-openagent lazycodex) + ALL_PACKAGES=(oh-my-opencode oh-my-openagent) + if [ "${PUBLISH_LAZYCODEX}" = "true" ]; then + ALL_PACKAGES+=(lazycodex) + fi for plat in "${PLATFORMS[@]}"; do ALL_PACKAGES+=("oh-my-opencode-${plat}") ALL_PACKAGES+=("oh-my-openagent-${plat}") @@ -302,6 +311,7 @@ jobs: - name: Check if lazycodex already published id: check-lazycodex + if: inputs.publish_lazycodex == true env: VERSION: ${{ needs.release-metadata.outputs.version }} run: | @@ -317,7 +327,7 @@ jobs: if: >- steps.check.outputs.skip != 'true' || steps.check-openagent.outputs.skip != 'true' || - steps.check-lazycodex.outputs.skip != 'true' + (inputs.publish_lazycodex == true && steps.check-lazycodex.outputs.skip != 'true') env: VERSION: ${{ needs.release-metadata.outputs.version }} run: | @@ -335,14 +345,14 @@ jobs: if: >- steps.check.outputs.skip != 'true' || steps.check-openagent.outputs.skip != 'true' || - steps.check-lazycodex.outputs.skip != 'true' + (inputs.publish_lazycodex == true && steps.check-lazycodex.outputs.skip != 'true') run: bun run build - name: Strip token auth from .npmrc to force OIDC if: >- steps.check.outputs.skip != 'true' || steps.check-openagent.outputs.skip != 'true' || - steps.check-lazycodex.outputs.skip != 'true' + (inputs.publish_lazycodex == true && steps.check-lazycodex.outputs.skip != 'true') run: | for f in .npmrc "$HOME/.npmrc"; do if [ -f "$f" ]; then @@ -394,7 +404,7 @@ jobs: git checkout -- package.json - name: Publish lazycodex - if: steps.check-lazycodex.outputs.skip != 'true' + if: inputs.publish_lazycodex == true && steps.check-lazycodex.outputs.skip != 'true' env: OMO_VERSION: ${{ needs.release-metadata.outputs.version }} DIST_TAG: ${{ needs.release-metadata.outputs.dist_tag }} @@ -417,7 +427,7 @@ jobs: fi - name: Restore package.json after lazycodex publish attempt - if: always() && steps.check-lazycodex.outputs.skip != 'true' + if: always() && inputs.publish_lazycodex == true && steps.check-lazycodex.outputs.skip != 'true' run: | git checkout -- package.json diff --git a/script/publish-workflow.test.ts b/script/publish-workflow.test.ts index 40ef3e912..a659196a3 100644 --- a/script/publish-workflow.test.ts +++ b/script/publish-workflow.test.ts @@ -257,6 +257,9 @@ describe("test workflows", () => { const flagDefaultsOff = workflow.includes("sync_lazycodex_marketplace:") && workflow.includes('description: "Sync the LazyCodex Codex marketplace repository"') && workflow.includes("default: false") + const publishAliasDefaultsOn = workflow.includes("publish_lazycodex:") && + workflow.includes('description: "Publish the lazycodex npm alias"') && + workflow.includes("default: true") const syncsLazycodexMarketplace = workflow.includes("bun run script/sync-lazycodex-marketplace.ts") const syncBuildsMcpDists = workflow.includes("bun run build:ast-grep-mcp") && @@ -276,7 +279,7 @@ describe("test workflows", () => { const alwaysChecksLazycodexNpm = workflow.includes("name: Check if lazycodex already published") && workflow.includes('https://registry.npmjs.org/lazycodex/${VERSION}') const publishesLazycodexNpm = publishLazycodexStep.includes("name: Publish lazycodex") && - publishLazycodexStep.includes("if: steps.check-lazycodex.outputs.skip != 'true'") && + publishLazycodexStep.includes("if: inputs.publish_lazycodex == true && steps.check-lazycodex.outputs.skip != 'true'") && publishLazycodexStep.includes("npm publish --access public --provenance --tag latest --loglevel verbose") && !publishLazycodexStep.includes("continue-on-error: true") const gatesLazycodexMarketplaceSync = workflow.includes("name: Sync LazyCodex Codex marketplace") && @@ -290,6 +293,7 @@ describe("test workflows", () => { // #then expect(keepsCodexPluginVersionIndependent, "LazyCodex plugin metadata must keep its own 0.1.0 version").toBe(true) expect(flagDefaultsOff, "LazyCodex marketplace sync must default to disabled").toBe(true) + expect(publishAliasDefaultsOn, "LazyCodex npm alias publish must stay enabled by default").toBe(true) expect(syncsLazycodexMarketplace, "release must sync the LazyCodex marketplace bundle").toBe(true) expect(syncBuildsMcpDists, "release must build bundled MCP dists before LazyCodex marketplace sync").toBe(true) expect(syncInstallsCodexPluginDeps, "release must install nested Codex plugin deps before building the aggregate plugin").toBe(true) @@ -301,6 +305,40 @@ describe("test workflows", () => { expect(requiresLazycodexSyncToken, "release must require a cross-repo token for LazyCodex push").toBe(true) }) + test("can skip LazyCodex npm alias publishing when npm holds the package name", () => { + // #given + const workflow = readFileSync(publishWorkflowPath, "utf8") + const preflightJob = sliceWorkflowSection(workflow, " preflight-trust:", " release-metadata:") + const updateVersionStep = sliceWorkflowSection( + workflow, + " - name: Update version", + " - name: Build main package", + ) + + // #when + const preflightMakesLazycodexConditional = + preflightJob.includes("PUBLISH_LAZYCODEX: ${{ inputs.publish_lazycodex }}") && + preflightJob.includes('if [ "${PUBLISH_LAZYCODEX}" = "true" ]; then') && + preflightJob.includes("ALL_PACKAGES+=(lazycodex)") + const checkStepIsConditional = workflow.includes("id: check-lazycodex") && + workflow.includes("if: inputs.publish_lazycodex == true") + const rebuildsOnlyWhenEnabledOrOtherPackagesNeedPublishing = + updateVersionStep.includes("(inputs.publish_lazycodex == true && steps.check-lazycodex.outputs.skip != 'true')") + const publishStepIsConditional = workflow.includes( + "if: inputs.publish_lazycodex == true && steps.check-lazycodex.outputs.skip != 'true'", + ) + const restoreStepIsConditional = workflow.includes( + "if: always() && inputs.publish_lazycodex == true && steps.check-lazycodex.outputs.skip != 'true'", + ) + + // #then + expect(preflightMakesLazycodexConditional, "trusted-publisher preflight must omit lazycodex when alias publishing is disabled").toBe(true) + expect(checkStepIsConditional, "lazycodex npm status check must be skipped when alias publishing is disabled").toBe(true) + expect(rebuildsOnlyWhenEnabledOrOtherPackagesNeedPublishing, "undefined lazycodex check outputs must not force rebuilds").toBe(true) + expect(publishStepIsConditional, "lazycodex publish must be guarded by publish_lazycodex").toBe(true) + expect(restoreStepIsConditional, "lazycodex restore must only run after a lazycodex publish attempt").toBe(true) + }) + test("keeps lazycodex platform dependencies aligned with shim resolution", () => { // #given const workflow = readFileSync(publishWorkflowPath, "utf8")