ci(lazycodex): gate publish and install
This commit is contained in:
@@ -22,6 +22,11 @@ on:
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
publish_lazycodex:
|
||||
description: "Publish lazycodex npm alias and sync Codex marketplace"
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
concurrency: ${{ github.workflow }}-${{ github.ref }}
|
||||
|
||||
@@ -72,10 +77,11 @@ jobs:
|
||||
id-token: write
|
||||
contents: read
|
||||
steps:
|
||||
- name: Verify trusted publisher for all 25 packages
|
||||
- name: Verify trusted publisher for release packages
|
||||
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" \
|
||||
@@ -87,7 +93,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}")
|
||||
@@ -327,6 +336,7 @@ jobs:
|
||||
git checkout -- package.json
|
||||
|
||||
- name: Check if lazycodex already published
|
||||
if: inputs.publish_lazycodex == true
|
||||
id: check-lazycodex
|
||||
env:
|
||||
VERSION: ${{ steps.version.outputs.version }}
|
||||
@@ -340,7 +350,7 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Publish lazycodex
|
||||
if: steps.check-lazycodex.outputs.skip != 'true'
|
||||
if: inputs.publish_lazycodex == true && steps.check-lazycodex.outputs.skip != 'true'
|
||||
continue-on-error: true
|
||||
env:
|
||||
VERSION: ${{ steps.version.outputs.version }}
|
||||
@@ -368,7 +378,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
|
||||
|
||||
@@ -452,12 +462,13 @@ jobs:
|
||||
git push origin "v${VERSION}"
|
||||
|
||||
- name: Require LazyCodex sync token
|
||||
if: ${{ secrets.LAZYCODEX_SYNC_TOKEN == '' }}
|
||||
if: inputs.publish_lazycodex == true && secrets.LAZYCODEX_SYNC_TOKEN == ''
|
||||
run: |
|
||||
echo "::error::LAZYCODEX_SYNC_TOKEN is required to push the Codex marketplace bundle to code-yeongyu/lazycodex."
|
||||
exit 1
|
||||
|
||||
- name: Checkout LazyCodex marketplace
|
||||
if: inputs.publish_lazycodex == true
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: code-yeongyu/lazycodex
|
||||
@@ -466,6 +477,7 @@ jobs:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Sync LazyCodex Codex marketplace
|
||||
if: inputs.publish_lazycodex == true
|
||||
env:
|
||||
VERSION: ${{ needs.publish-main.outputs.version }}
|
||||
run: |
|
||||
|
||||
@@ -59,21 +59,31 @@ describe("test workflows", () => {
|
||||
expect(buildNeedsCodexMatrix, "Build must wait for Codex compatibility checks").toBe(true)
|
||||
})
|
||||
|
||||
test("syncs the LazyCodex Codex marketplace bundle during release", () => {
|
||||
test("keeps LazyCodex deployment behind an explicit publish flag", () => {
|
||||
// #given
|
||||
const workflow = readFileSync(new URL("../.github/workflows/publish.yml", import.meta.url), "utf8")
|
||||
|
||||
// #when
|
||||
const appliesCodexPluginVersion = workflow.includes("packages/omo-codex/plugin/.codex-plugin/plugin.json")
|
||||
const flagDefaultsOff = workflow.includes("publish_lazycodex:") &&
|
||||
workflow.includes('description: "Publish lazycodex npm alias and sync Codex marketplace"') &&
|
||||
workflow.includes("default: false")
|
||||
const syncsLazycodexMarketplace = workflow.includes("bun run script/sync-lazycodex-marketplace.ts")
|
||||
const pushesLazycodexMarketplace = workflow.includes("code-yeongyu/lazycodex")
|
||||
const gatesLazycodexNpmPublish = workflow.includes("name: Publish lazycodex") &&
|
||||
workflow.includes("if: inputs.publish_lazycodex == true && steps.check-lazycodex.outputs.skip != 'true'")
|
||||
const gatesLazycodexMarketplaceSync = workflow.includes("name: Sync LazyCodex Codex marketplace") &&
|
||||
workflow.includes("if: inputs.publish_lazycodex == true")
|
||||
const requiresLazycodexSyncToken = workflow.includes("secrets.LAZYCODEX_SYNC_TOKEN == ''") &&
|
||||
workflow.includes("token: ${{ secrets.LAZYCODEX_SYNC_TOKEN }}")
|
||||
|
||||
// #then
|
||||
expect(appliesCodexPluginVersion, "release must version the Codex plugin manifest before marketplace sync").toBe(true)
|
||||
expect(flagDefaultsOff, "LazyCodex deployment must default to disabled").toBe(true)
|
||||
expect(syncsLazycodexMarketplace, "release must sync the LazyCodex marketplace bundle").toBe(true)
|
||||
expect(pushesLazycodexMarketplace, "release must target the LazyCodex repository").toBe(true)
|
||||
expect(gatesLazycodexNpmPublish, "lazycodex npm publish must require publish_lazycodex=true").toBe(true)
|
||||
expect(gatesLazycodexMarketplaceSync, "LazyCodex marketplace push must require publish_lazycodex=true").toBe(true)
|
||||
expect(requiresLazycodexSyncToken, "release must require a cross-repo token for LazyCodex push").toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -60,13 +60,20 @@ function stubOpenCodeSuccess(): void {
|
||||
|
||||
describe("runCliInstaller platform branching", () => {
|
||||
const consoleLog = console.log
|
||||
const originalPublishLazycodex = process.env.OMO_PUBLISH_LAZYCODEX
|
||||
|
||||
beforeEach(() => {
|
||||
console.log = mock(() => {})
|
||||
process.env.OMO_PUBLISH_LAZYCODEX = "true"
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
console.log = consoleLog
|
||||
if (originalPublishLazycodex === undefined) {
|
||||
delete process.env.OMO_PUBLISH_LAZYCODEX
|
||||
} else {
|
||||
process.env.OMO_PUBLISH_LAZYCODEX = originalPublishLazycodex
|
||||
}
|
||||
mock.restore()
|
||||
})
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ describe("runCliInstaller", () => {
|
||||
const mockConsoleError = mock(() => {})
|
||||
const originalConsoleLog = console.log
|
||||
const originalConsoleError = console.error
|
||||
const originalPublishLazycodex = process.env.OMO_PUBLISH_LAZYCODEX
|
||||
|
||||
beforeEach(() => {
|
||||
console.log = mockConsoleLog
|
||||
@@ -20,6 +21,11 @@ describe("runCliInstaller", () => {
|
||||
afterEach(() => {
|
||||
console.log = originalConsoleLog
|
||||
console.error = originalConsoleError
|
||||
if (originalPublishLazycodex === undefined) {
|
||||
delete process.env.OMO_PUBLISH_LAZYCODEX
|
||||
} else {
|
||||
process.env.OMO_PUBLISH_LAZYCODEX = originalPublishLazycodex
|
||||
}
|
||||
mock.restore()
|
||||
})
|
||||
|
||||
@@ -128,6 +134,7 @@ describe("runCliInstaller", () => {
|
||||
|
||||
it("skips OpenCode checks and writes for platform=codex", async () => {
|
||||
// given
|
||||
process.env.OMO_PUBLISH_LAZYCODEX = "true"
|
||||
const detectSpy = spyOn(configManager, "detectCurrentConfig")
|
||||
const installedSpy = spyOn(configManager, "isOpenCodeInstalled")
|
||||
const versionSpy = spyOn(configManager, "getOpenCodeVersion")
|
||||
|
||||
@@ -6,6 +6,7 @@ import { doctor } from "./doctor"
|
||||
import { refreshModelCapabilities } from "./refresh-model-capabilities"
|
||||
import { createMcpOAuthCommand } from "./mcp-oauth"
|
||||
import { boulder } from "./boulder"
|
||||
import { isLazycodexPublishingEnabled } from "./lazycodex-feature-flag"
|
||||
import type { InstallArgs } from "./types"
|
||||
import type { RunOptions } from "./run"
|
||||
import type { GetLocalVersionOptions } from "./get-local-version/types"
|
||||
@@ -31,17 +32,22 @@ type InstallCommandOptions = {
|
||||
readonly skipAuth?: boolean
|
||||
}
|
||||
|
||||
type Environment = Readonly<Record<string, string | undefined>>
|
||||
|
||||
export function resolveInstallArgs(
|
||||
options: InstallCommandOptions,
|
||||
invocationName: string | undefined = process.env.OMO_INVOCATION_NAME,
|
||||
env: Environment = process.env,
|
||||
): InstallArgs {
|
||||
const defaultPlatform = invocationName === "lazycodex" && isLazycodexPublishingEnabled(env) ? "codex" : undefined
|
||||
|
||||
return {
|
||||
tui: options.tui !== false,
|
||||
claude: options.claude,
|
||||
openai: options.openai,
|
||||
gemini: options.gemini,
|
||||
copilot: options.copilot,
|
||||
platform: options.platform ?? (invocationName === "lazycodex" ? "codex" : undefined),
|
||||
platform: options.platform ?? defaultPlatform,
|
||||
opencodeZen: options.opencodeZen,
|
||||
zaiCodingPlan: options.zaiCodingPlan,
|
||||
kimiForCoding: options.kimiForCoding,
|
||||
|
||||
@@ -7,18 +7,46 @@ import { argsToConfig } from "../install-validators"
|
||||
|
||||
describe("lazycodex install routing", () => {
|
||||
const originalInvocationName = process.env.OMO_INVOCATION_NAME
|
||||
const originalPublishLazycodex = process.env.OMO_PUBLISH_LAZYCODEX
|
||||
|
||||
afterEach(() => {
|
||||
if (originalInvocationName === undefined) {
|
||||
delete process.env.OMO_INVOCATION_NAME
|
||||
return
|
||||
} else {
|
||||
process.env.OMO_INVOCATION_NAME = originalInvocationName
|
||||
}
|
||||
|
||||
if (originalPublishLazycodex === undefined) {
|
||||
delete process.env.OMO_PUBLISH_LAZYCODEX
|
||||
} else {
|
||||
process.env.OMO_PUBLISH_LAZYCODEX = originalPublishLazycodex
|
||||
}
|
||||
process.env.OMO_INVOCATION_NAME = originalInvocationName
|
||||
})
|
||||
|
||||
test("defaults platform to codex when invoked as lazycodex without --platform", () => {
|
||||
test("leaves lazycodex invocation unresolved when lazycodex publishing is disabled", () => {
|
||||
// given
|
||||
process.env.OMO_INVOCATION_NAME = "lazycodex"
|
||||
delete process.env.OMO_PUBLISH_LAZYCODEX
|
||||
|
||||
// when
|
||||
const args = resolveInstallArgs({
|
||||
tui: false,
|
||||
claude: "no",
|
||||
gemini: "no",
|
||||
copilot: "no",
|
||||
})
|
||||
const config = argsToConfig(args)
|
||||
|
||||
// then
|
||||
expect(args.platform).toBeUndefined()
|
||||
expect(config.hasCodex).toBe(false)
|
||||
expect(config.hasOpenCode).toBe(true)
|
||||
})
|
||||
|
||||
test("defaults platform to codex when invoked as lazycodex with lazycodex publishing enabled", () => {
|
||||
// given
|
||||
process.env.OMO_INVOCATION_NAME = "lazycodex"
|
||||
process.env.OMO_PUBLISH_LAZYCODEX = "true"
|
||||
|
||||
// when
|
||||
const args = resolveInstallArgs({
|
||||
@@ -35,9 +63,10 @@ describe("lazycodex install routing", () => {
|
||||
expect(config.hasOpenCode).toBe(false)
|
||||
})
|
||||
|
||||
test("respects explicit --platform=both when invoked as lazycodex", () => {
|
||||
test("respects explicit --platform=both when lazycodex publishing is enabled", () => {
|
||||
// given
|
||||
process.env.OMO_INVOCATION_NAME = "lazycodex"
|
||||
process.env.OMO_PUBLISH_LAZYCODEX = "true"
|
||||
|
||||
// when
|
||||
const args = resolveInstallArgs({
|
||||
|
||||
@@ -48,13 +48,24 @@ describe("install platform resolution", () => {
|
||||
expect(args.platform).toBe("opencode")
|
||||
})
|
||||
|
||||
test("defaults lazycodex install to codex platform", () => {
|
||||
test("leaves lazycodex install unresolved when lazycodex publishing is disabled", () => {
|
||||
// given
|
||||
const invocationName = "lazycodex"
|
||||
|
||||
// when
|
||||
const args = resolveInstallArgs({ tui: true }, invocationName)
|
||||
|
||||
// then
|
||||
expect(args.platform).toBeUndefined()
|
||||
})
|
||||
|
||||
test("defaults lazycodex install to codex platform when lazycodex publishing is enabled", () => {
|
||||
// given
|
||||
const invocationName = "lazycodex"
|
||||
|
||||
// when
|
||||
const args = resolveInstallArgs({ tui: true }, invocationName, { OMO_PUBLISH_LAZYCODEX: "true" })
|
||||
|
||||
// then
|
||||
expect(args.platform).toBe("codex")
|
||||
})
|
||||
|
||||
@@ -117,24 +117,52 @@ describe("validateNonTuiArgs", () => {
|
||||
expect(result.errors).toContain("--copilot is required (values: no, yes)")
|
||||
})
|
||||
|
||||
test("allows codex-only non-TUI installs without OpenCode provider flags", () => {
|
||||
test("rejects codex-only non-TUI installs when lazycodex publishing is disabled", () => {
|
||||
// #given
|
||||
const args: InstallArgs = { tui: false, platform: "codex" }
|
||||
|
||||
// #when
|
||||
const result = validateNonTuiArgs(args)
|
||||
|
||||
// #then
|
||||
expect(result.valid).toBe(false)
|
||||
expect(result.errors).toContain(
|
||||
"Codex platform install is disabled. Set OMO_PUBLISH_LAZYCODEX=true to enable LazyCodex publish/install.",
|
||||
)
|
||||
})
|
||||
|
||||
test("allows codex-only non-TUI installs with lazycodex publishing enabled", () => {
|
||||
// #given
|
||||
const args: InstallArgs = { tui: false, platform: "codex" }
|
||||
|
||||
// #when
|
||||
const result = validateNonTuiArgs(args, { OMO_PUBLISH_LAZYCODEX: "true" })
|
||||
|
||||
// #then
|
||||
expect(result.valid).toBe(true)
|
||||
expect(result.errors).toEqual([])
|
||||
})
|
||||
|
||||
test("rejects platform=both when lazycodex publishing is disabled", () => {
|
||||
// #given
|
||||
const args = createArgs({ platform: "both" })
|
||||
|
||||
// #when
|
||||
const result = validateNonTuiArgs(args)
|
||||
|
||||
// #then
|
||||
expect(result.valid).toBe(false)
|
||||
expect(result.errors).toContain(
|
||||
"Codex platform install is disabled. Set OMO_PUBLISH_LAZYCODEX=true to enable LazyCodex publish/install.",
|
||||
)
|
||||
})
|
||||
|
||||
test("rejects OpenCode flags for codex-only non-TUI installs", () => {
|
||||
// #given
|
||||
const args = createArgs({ platform: "codex", claude: "yes" })
|
||||
|
||||
// #when
|
||||
const result = validateNonTuiArgs(args)
|
||||
const result = validateNonTuiArgs(args, { OMO_PUBLISH_LAZYCODEX: "true" })
|
||||
|
||||
// #then
|
||||
expect(result.valid).toBe(false)
|
||||
|
||||
@@ -7,6 +7,11 @@ import type {
|
||||
InstallConfig,
|
||||
InstallPlatform,
|
||||
} from "./types"
|
||||
import {
|
||||
LAZYCODEX_DISABLED_MESSAGE,
|
||||
isLazycodexPublishingEnabled,
|
||||
platformRequiresLazycodex,
|
||||
} from "./lazycodex-feature-flag"
|
||||
|
||||
export const SYMBOLS = {
|
||||
check: color.green("[OK]"),
|
||||
@@ -19,6 +24,7 @@ export const SYMBOLS = {
|
||||
}
|
||||
|
||||
const ANSI_COLOR_PATTERN = new RegExp("\u001b\\[[0-9;]*m", "g")
|
||||
type Environment = Readonly<Record<string, string | undefined>>
|
||||
|
||||
function formatProvider(name: string, enabled: boolean, detail?: string): string {
|
||||
const status = enabled ? SYMBOLS.check : color.dim("○")
|
||||
@@ -116,12 +122,19 @@ export function printBox(content: string, title?: string): void {
|
||||
console.log()
|
||||
}
|
||||
|
||||
export function validateNonTuiArgs(args: InstallArgs): { valid: boolean; errors: string[] } {
|
||||
export function validateNonTuiArgs(
|
||||
args: InstallArgs,
|
||||
env: Environment = process.env,
|
||||
): { valid: boolean; errors: string[] } {
|
||||
const errors: string[] = []
|
||||
const platform = resolvePlatform(args)
|
||||
const hasOpenCode = platform === "opencode" || platform === "both"
|
||||
const hasCodexOnly = platform === "codex"
|
||||
|
||||
if (platformRequiresLazycodex(platform) && !isLazycodexPublishingEnabled(env)) {
|
||||
errors.push(LAZYCODEX_DISABLED_MESSAGE)
|
||||
}
|
||||
|
||||
if (hasOpenCode && args.claude === undefined) {
|
||||
errors.push("--claude is required (values: no, yes, max20)")
|
||||
} else if (args.claude !== undefined && !["no", "yes", "max20"].includes(args.claude)) {
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import type { InstallPlatform } from "./types"
|
||||
|
||||
export const LAZYCODEX_PUBLISH_FLAG = "OMO_PUBLISH_LAZYCODEX"
|
||||
export const LAZYCODEX_DISABLED_MESSAGE =
|
||||
"Codex platform install is disabled. Set OMO_PUBLISH_LAZYCODEX=true to enable LazyCodex publish/install."
|
||||
|
||||
type Environment = Readonly<Record<string, string | undefined>>
|
||||
|
||||
export function isLazycodexPublishingEnabled(env: Environment = process.env): boolean {
|
||||
return env[LAZYCODEX_PUBLISH_FLAG] === "true"
|
||||
}
|
||||
|
||||
export function platformRequiresLazycodex(platform: InstallPlatform | undefined): boolean {
|
||||
return platform === "codex" || platform === "both"
|
||||
}
|
||||
@@ -46,12 +46,12 @@ describe("promptInstallPlatform", () => {
|
||||
mock.restore()
|
||||
})
|
||||
|
||||
test("offers OpenCode, Codex, and Both choices with OpenCode as the default", async () => {
|
||||
test("offers OpenCode, Codex, and Both choices when lazycodex publishing is enabled", async () => {
|
||||
// given
|
||||
const selectSpy = spyOn(p, "select").mockResolvedValue("opencode")
|
||||
|
||||
// when
|
||||
const value = await prompts.promptInstallPlatform()
|
||||
const value = await prompts.promptInstallPlatform("opencode", true)
|
||||
|
||||
// then
|
||||
expect(value).toBe("opencode")
|
||||
@@ -65,6 +65,22 @@ describe("promptInstallPlatform", () => {
|
||||
],
|
||||
})
|
||||
})
|
||||
|
||||
test("hides Codex platform choices when lazycodex publishing is disabled", async () => {
|
||||
// given
|
||||
const selectSpy = spyOn(p, "select").mockResolvedValue("opencode")
|
||||
|
||||
// when
|
||||
const value = await prompts.promptInstallPlatform("codex", false)
|
||||
|
||||
// then
|
||||
expect(value).toBe("opencode")
|
||||
expect(selectSpy).toHaveBeenCalledTimes(1)
|
||||
expect(selectSpy.mock.calls[0]?.[0]).toMatchObject({
|
||||
initialValue: "opencode",
|
||||
options: [{ value: "opencode" }],
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("promptInstallConfig platform branching", () => {
|
||||
|
||||
@@ -7,6 +7,7 @@ import type {
|
||||
InstallPlatform,
|
||||
} from "./types"
|
||||
import { detectedToInitialValues } from "./install-validators"
|
||||
import { isLazycodexPublishingEnabled } from "./lazycodex-feature-flag"
|
||||
|
||||
async function selectOrCancel<TValue extends Readonly<string | boolean | number>>(params: {
|
||||
message: string
|
||||
@@ -29,15 +30,24 @@ async function selectOrCancel<TValue extends Readonly<string | boolean | number>
|
||||
|
||||
export async function promptInstallPlatform(
|
||||
initialValue: InstallPlatform = "opencode",
|
||||
lazycodexEnabled = isLazycodexPublishingEnabled(),
|
||||
): Promise<InstallPlatform | null> {
|
||||
return selectOrCancel<InstallPlatform>({
|
||||
message: "Which platform do you want to install?",
|
||||
options: [
|
||||
{ value: "opencode", label: "OpenCode", hint: "Install OpenCode plugin only" },
|
||||
const options: Option<InstallPlatform>[] = [
|
||||
{ value: "opencode", label: "OpenCode", hint: "Install OpenCode plugin only" },
|
||||
]
|
||||
if (lazycodexEnabled) {
|
||||
options.push(
|
||||
{ value: "codex", label: "Codex", hint: "Install Codex harness adapter only" },
|
||||
{ value: "both", label: "Both", hint: "Install OpenCode plugin and Codex adapter" },
|
||||
],
|
||||
initialValue,
|
||||
)
|
||||
}
|
||||
|
||||
const safeInitialValue = lazycodexEnabled || initialValue === "opencode" ? initialValue : "opencode"
|
||||
|
||||
return selectOrCancel<InstallPlatform>({
|
||||
message: "Which platform do you want to install?",
|
||||
options,
|
||||
initialValue: safeInitialValue,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ function createMockSpinner(): ReturnType<typeof p.spinner> {
|
||||
describe("runTuiInstaller", () => {
|
||||
const originalIsStdinTty = process.stdin.isTTY
|
||||
const originalIsStdoutTty = process.stdout.isTTY
|
||||
const originalPublishLazycodex = process.env.OMO_PUBLISH_LAZYCODEX
|
||||
|
||||
beforeEach(() => {
|
||||
Object.defineProperty(process.stdin, "isTTY", { configurable: true, value: true })
|
||||
@@ -28,6 +29,11 @@ describe("runTuiInstaller", () => {
|
||||
afterEach(() => {
|
||||
Object.defineProperty(process.stdin, "isTTY", { configurable: true, value: originalIsStdinTty })
|
||||
Object.defineProperty(process.stdout, "isTTY", { configurable: true, value: originalIsStdoutTty })
|
||||
if (originalPublishLazycodex === undefined) {
|
||||
delete process.env.OMO_PUBLISH_LAZYCODEX
|
||||
} else {
|
||||
process.env.OMO_PUBLISH_LAZYCODEX = originalPublishLazycodex
|
||||
}
|
||||
})
|
||||
|
||||
it("blocks installation when OpenCode is below the minimum version", async () => {
|
||||
@@ -76,6 +82,29 @@ describe("runTuiInstaller", () => {
|
||||
outroSpy.mockRestore()
|
||||
})
|
||||
|
||||
it("blocks codex platform when lazycodex publishing is disabled", async () => {
|
||||
// given
|
||||
delete process.env.OMO_PUBLISH_LAZYCODEX
|
||||
const platformSpy = spyOn(tuiInstallPrompts, "promptInstallPlatform").mockResolvedValue("codex")
|
||||
const promptConfigSpy = spyOn(tuiInstallPrompts, "promptInstallConfig")
|
||||
const logErrorSpy = spyOn(p.log, "error").mockImplementation(() => undefined)
|
||||
const outroSpy = spyOn(p, "outro").mockImplementation(() => undefined)
|
||||
|
||||
// when
|
||||
const result = await runTuiInstaller({ tui: true, platform: "codex" }, "3.16.0")
|
||||
|
||||
// then
|
||||
expect(result).toBe(1)
|
||||
expect(platformSpy).toHaveBeenCalled()
|
||||
expect(promptConfigSpy).not.toHaveBeenCalled()
|
||||
expect(logErrorSpy).toHaveBeenCalled()
|
||||
|
||||
platformSpy.mockRestore()
|
||||
promptConfigSpy.mockRestore()
|
||||
logErrorSpy.mockRestore()
|
||||
outroSpy.mockRestore()
|
||||
})
|
||||
|
||||
it("proceeds when OpenCode meets the minimum version", async () => {
|
||||
// given
|
||||
const restoreSpies = [
|
||||
@@ -143,6 +172,7 @@ describe("runTuiInstaller", () => {
|
||||
|
||||
it("skips OpenCode checks and writes when platform is codex", async () => {
|
||||
// given
|
||||
process.env.OMO_PUBLISH_LAZYCODEX = "true"
|
||||
const restoreSpies = [
|
||||
spyOn(p, "spinner").mockReturnValue(createMockSpinner()),
|
||||
spyOn(p, "intro").mockImplementation(() => undefined),
|
||||
|
||||
@@ -13,6 +13,11 @@ import { detectedToInitialValues, formatConfigSummary, SYMBOLS } from "./install
|
||||
import { getUnsupportedOpenCodeVersionMessage } from "./minimum-opencode-version"
|
||||
import { promptInstallConfig, promptInstallPlatform } from "./tui-install-prompts"
|
||||
import { runCodexInstaller } from "./install-codex"
|
||||
import {
|
||||
LAZYCODEX_DISABLED_MESSAGE,
|
||||
isLazycodexPublishingEnabled,
|
||||
platformRequiresLazycodex,
|
||||
} from "./lazycodex-feature-flag"
|
||||
|
||||
export async function runTuiInstaller(args: InstallArgs, version: string): Promise<number> {
|
||||
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
||||
@@ -22,6 +27,11 @@ export async function runTuiInstaller(args: InstallArgs, version: string): Promi
|
||||
|
||||
const selectedPlatform = await promptInstallPlatform(args.platform ?? "opencode")
|
||||
if (!selectedPlatform) return 1
|
||||
if (platformRequiresLazycodex(selectedPlatform) && !isLazycodexPublishingEnabled()) {
|
||||
p.log.error(LAZYCODEX_DISABLED_MESSAGE)
|
||||
p.outro(color.red("Installation blocked."))
|
||||
return 1
|
||||
}
|
||||
|
||||
const hasOpenCode = selectedPlatform === "opencode" || selectedPlatform === "both"
|
||||
const detected = hasOpenCode
|
||||
|
||||
Reference in New Issue
Block a user