From c9d574888b3628824f2433210f992f8093ad0deb Mon Sep 17 00:00:00 2001 From: Angelo Ashmore Date: Thu, 16 Jul 2026 19:13:13 +0000 Subject: [PATCH 1/7] feat: add prismic env commands backed by .env.local Add `prismic env` commands to manage a project's active environment. The active environment is stored in .env.local under a framework-specific variable name (e.g. NEXT_PUBLIC_PRISMIC_ENVIRONMENT), so the running site reads it automatically. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/adapters/index.ts | 29 +++++++++++++++++++- src/adapters/nextjs.ts | 2 ++ src/adapters/nuxt.ts | 2 ++ src/adapters/sveltekit.ts | 2 ++ src/commands/env-active.ts | 15 +++++++++++ src/commands/env-list.ts | 45 +++++++++++++++++++++++++++++++ src/commands/env-set.ts | 35 ++++++++++++++++++++++++ src/commands/env-unset.ts | 15 +++++++++++ src/commands/env.ts | 28 ++++++++++++++++++++ src/lib/file.ts | 47 +++++++++++++++++++++++++++++++++ src/lib/prismic/environments.ts | 18 +++++++++++++ test/env-active.test.ts | 24 +++++++++++++++++ test/env-list.test.ts | 14 ++++++++++ test/env-set.test.ts | 36 +++++++++++++++++++++++++ test/env-unset.test.ts | 22 +++++++++++++++ test/env.test.ts | 13 +++++++++ 16 files changed, 346 insertions(+), 1 deletion(-) create mode 100644 src/commands/env-active.ts create mode 100644 src/commands/env-list.ts create mode 100644 src/commands/env-set.ts create mode 100644 src/commands/env-unset.ts create mode 100644 src/commands/env.ts create mode 100644 test/env-active.test.ts create mode 100644 test/env-list.test.ts create mode 100644 test/env-set.test.ts create mode 100644 test/env-unset.test.ts create mode 100644 test/env.test.ts diff --git a/src/adapters/index.ts b/src/adapters/index.ts index 4e3fc7df..02020226 100644 --- a/src/adapters/index.ts +++ b/src/adapters/index.ts @@ -6,7 +6,13 @@ import { pathToFileURL } from "node:url"; import { generateTypes } from "prismic-ts-codegen"; import { glob } from "tinyglobby"; -import { readJsonFile, writeFileRecursive } from "../lib/file"; +import { + readEnvFile, + readJsonFile, + setEnvFileVar, + unsetEnvFileVar, + writeFileRecursive, +} from "../lib/file"; import { stringify } from "../lib/json"; import { readPackageJson } from "../lib/packageJson"; import { appendTrailingSlash } from "../lib/url"; @@ -45,6 +51,8 @@ export class NoSupportedFrameworkError extends Error { export abstract class Adapter { abstract readonly id: string; + abstract readonly environmentEnvVarName: string; + abstract onProjectInitialized(): Promise | void; abstract onSliceCreated(model: SharedSlice, library: URL): Promise | void; abstract onSliceUpdated(model: SharedSlice): Promise | void; @@ -205,4 +213,23 @@ export abstract class Adapter { await writeFileRecursive(output, types); return output; } + + async getEnvironment(): Promise { + const projectRoot = await findProjectRoot(); + const envLocalPath = new URL(".env.local", projectRoot); + const envLocalVars = await readEnvFile(envLocalPath).catch(() => {}); + return envLocalVars?.[this.environmentEnvVarName] || undefined; + } + + async setEnvironment(environment: string): Promise { + const projectRoot = await findProjectRoot(); + const envLocalPath = new URL(".env.local", projectRoot); + await setEnvFileVar(envLocalPath, this.environmentEnvVarName, environment); + } + + async unsetEnvironment(): Promise { + const projectRoot = await findProjectRoot(); + const envLocalPath = new URL(".env.local", projectRoot); + await unsetEnvFileVar(envLocalPath, this.environmentEnvVarName); + } } diff --git a/src/adapters/nextjs.ts b/src/adapters/nextjs.ts index dc61b923..b4fab6da 100644 --- a/src/adapters/nextjs.ts +++ b/src/adapters/nextjs.ts @@ -32,6 +32,8 @@ import { export class NextJsAdapter extends Adapter { readonly id = "next"; + readonly environmentEnvVarName = "NEXT_PUBLIC_PRISMIC_ENVIRONMENT"; + async setupProject(): Promise { await addDependencies({ "@prismicio/client": `^${await getNpmPackageVersion("@prismicio/client")}`, diff --git a/src/adapters/nuxt.ts b/src/adapters/nuxt.ts index a2f5fede..abe54638 100644 --- a/src/adapters/nuxt.ts +++ b/src/adapters/nuxt.ts @@ -27,6 +27,8 @@ const NUXT_PRISMIC = "@nuxtjs/prismic"; export class NuxtAdapter extends Adapter { readonly id = "nuxt"; + readonly environmentEnvVarName = "NUXT_PUBLIC_PRISMIC_ENVIRONMENT"; + async setupProject(): Promise { await addDependencies({ "@prismicio/client": `^${await getNpmPackageVersion("@prismicio/client")}`, diff --git a/src/adapters/sveltekit.ts b/src/adapters/sveltekit.ts index 55ca1685..ebc58a46 100644 --- a/src/adapters/sveltekit.ts +++ b/src/adapters/sveltekit.ts @@ -34,6 +34,8 @@ import { export class SvelteKitAdapter extends Adapter { readonly id = "sveltekit"; + readonly environmentEnvVarName = "PUBLIC_PRISMIC_ENVIRONMENT"; + async setupProject(): Promise { await addDependencies({ "@prismicio/client": `^${await getNpmPackageVersion("@prismicio/client")}`, diff --git a/src/commands/env-active.ts b/src/commands/env-active.ts new file mode 100644 index 00000000..e3f1b8a9 --- /dev/null +++ b/src/commands/env-active.ts @@ -0,0 +1,15 @@ +import { getAdapter } from "../adapters"; +import { createCommand, type CommandConfig } from "../lib/command"; +import { getRepositoryName } from "../project"; + +const config = { + name: "prismic env active", + description: "Print the active environment.", +} satisfies CommandConfig; + +export default createCommand(config, async () => { + const repo = await getRepositoryName(); + const adapter = await getAdapter(); + const environment = await adapter.getEnvironment(); + console.info(environment ?? repo); +}); diff --git a/src/commands/env-list.ts b/src/commands/env-list.ts new file mode 100644 index 00000000..b9337065 --- /dev/null +++ b/src/commands/env-list.ts @@ -0,0 +1,45 @@ +import { getAdapter } from "../adapters"; +import { getCredentials } from "../auth"; +import { createCommand, type CommandConfig } from "../lib/command"; +import { stringify } from "../lib/json"; +import { getUserEnvironments } from "../lib/prismic/environments"; +import { formatTable } from "../lib/string"; +import { getRepositoryName } from "../project"; + +const config = { + name: "prismic env list", + description: "List the environments available for the project.", + options: { + json: { type: "boolean", description: "Output as JSON" }, + repo: { type: "string", short: "r", description: "Repository domain" }, + }, +} satisfies CommandConfig; + +export default createCommand(config, async ({ values }) => { + const { json, repo = await getRepositoryName() } = values; + + const { token, host } = await getCredentials(); + const environments = await getUserEnvironments({ repo, token, host }); + + const adapter = await getAdapter(); + const activeEnvironment = (await adapter.getEnvironment()) ?? (await getRepositoryName()); + + if (json) { + const results = environments.map((environment) => ({ + domain: environment.domain, + kind: environment.kind, + active: environment.domain === activeEnvironment, + })); + console.info(stringify(results)); + return; + } + + const rows = environments.map((environment) => { + return [ + environment.domain, + environment.kind, + environment.domain === activeEnvironment ? "*" : "", + ]; + }); + console.info(formatTable(rows, { headers: ["DOMAIN", "KIND", "ACTIVE"] })); +}); diff --git a/src/commands/env-set.ts b/src/commands/env-set.ts new file mode 100644 index 00000000..cc1d0f2d --- /dev/null +++ b/src/commands/env-set.ts @@ -0,0 +1,35 @@ +import { getAdapter } from "../adapters"; +import { getCredentials } from "../auth"; +import { createCommand, type CommandConfig } from "../lib/command"; +import { getUserEnvironments, InvalidEnvironmentError } from "../lib/prismic/environments"; +import { getRepositoryName } from "../project"; + +const config = { + name: "prismic env set", + description: "Set the active environment.", + positionals: { + environment: { description: "Environment domain", required: true }, + }, +} satisfies CommandConfig; + +export default createCommand(config, async ({ positionals }) => { + const [environment] = positionals; + + const repo = await getRepositoryName(); + const { token, host } = await getCredentials(); + const environments = await getUserEnvironments({ repo, token, host }); + + const validEnvironment = environments.some((env) => env.domain === environment); + if (!validEnvironment) throw new InvalidEnvironmentError(environment, environments, repo); + + const adapter = await getAdapter(); + + if (environment === repo) { + await adapter.unsetEnvironment(); + console.info(`Reset to the production environment "${repo}".`); + return; + } + + await adapter.setEnvironment(environment); + console.info(`Set the active environment to "${environment}".`); +}); diff --git a/src/commands/env-unset.ts b/src/commands/env-unset.ts new file mode 100644 index 00000000..7954acbe --- /dev/null +++ b/src/commands/env-unset.ts @@ -0,0 +1,15 @@ +import { getAdapter } from "../adapters"; +import { createCommand, type CommandConfig } from "../lib/command"; +import { getRepositoryName } from "../project"; + +const config = { + name: "prismic env unset", + description: "Reset the active environment to the production environment.", +} satisfies CommandConfig; + +export default createCommand(config, async () => { + const repo = await getRepositoryName(); + const adapter = await getAdapter(); + await adapter.unsetEnvironment(); + console.info(`Reset to the production environment "${repo}".`); +}); diff --git a/src/commands/env.ts b/src/commands/env.ts new file mode 100644 index 00000000..d2e75f4c --- /dev/null +++ b/src/commands/env.ts @@ -0,0 +1,28 @@ +import { createCommandRouter } from "../lib/command"; +import envActive from "./env-active"; +import envList from "./env-list"; +import envSet from "./env-set"; +import envUnset from "./env-unset"; + +export default createCommandRouter({ + name: "prismic env", + description: "Manage the active environment for a Prismic project.", + commands: { + set: { + handler: envSet, + description: "Set the active environment", + }, + unset: { + handler: envUnset, + description: "Reset to the production environment", + }, + active: { + handler: envActive, + description: "Print the active environment", + }, + list: { + handler: envList, + description: "List environments", + }, + }, +}); diff --git a/src/lib/file.ts b/src/lib/file.ts index bf644731..c0e3a066 100644 --- a/src/lib/file.ts +++ b/src/lib/file.ts @@ -1,5 +1,6 @@ import { access, mkdir, readFile, writeFile } from "node:fs/promises"; import { pathToFileURL } from "node:url"; +import { parseEnv } from "node:util"; import * as z from "zod/mini"; import { appendTrailingSlash, getExtension } from "./url"; @@ -99,3 +100,49 @@ export async function readURLFile(url: URL): Promise { throw new Error(`Unsupported file protocol: ${url.protocol}`); } + +export async function readEnvFile>>( + path: URL, + options: { schema?: z.ZodMiniType } = {}, +): Promise { + const { schema } = options; + const contents = await readFile(path, "utf8"); + const parsed = parseEnv(contents); + if (schema) return z.parse(schema, parsed); + return parsed as T; +} + +export async function setEnvFileVar(path: URL, key: string, value: string): Promise { + const hasFile = await exists(path); + let contents = ""; + if (hasFile) { + contents = await readFile(path, "utf8"); + parseEnv(contents); // Verify the format + } + + const pattern = new RegExp(`^${key}=.*$`, "m"); + const line = `${key}=${value}`; + const hasEnvironmentVar = pattern.test(contents); + + if (hasEnvironmentVar) { + contents = contents.replace(pattern, line); + } else { + if (contents && !contents.endsWith("\n")) contents += "\n"; + contents += `${line}\n`; + } + + await writeFile(path, contents); +} + +export async function unsetEnvFileVar(path: URL, key: string): Promise { + const hasFile = await exists(path); + if (!hasFile) return; + + let contents = await readFile(path, "utf8"); + parseEnv(contents); // Verify the format + + const pattern = new RegExp(`^${key}=.*$\n?`, "m"); + contents = contents.replace(pattern, ""); + + await writeFile(path, contents); +} diff --git a/src/lib/prismic/environments.ts b/src/lib/prismic/environments.ts index a0db3ee7..32d85d14 100644 --- a/src/lib/prismic/environments.ts +++ b/src/lib/prismic/environments.ts @@ -2,6 +2,24 @@ import { dedent } from "../string"; import { type Environment, getEnvironments } from "./clients/core"; import { getProfile } from "./clients/user"; +export async function getUserEnvironments(config: { + repo: string; + token: string | undefined; + host: string; +}): Promise { + const { repo, token, host } = config; + const [profile, environments] = await Promise.all([ + getProfile({ token, host }), + getEnvironments({ repo, token, host }), + ]); + const userEnvironments = environments.filter( + (environment) => + (environment.kind === "prod" || environment.kind === "stage") && + environment.users.some((user) => user.id === profile.shortId), + ); + return userEnvironments; +} + export async function resolveEnvironment( env: string, config: { repo: string; token: string | undefined; host: string }, diff --git a/test/env-active.test.ts b/test/env-active.test.ts new file mode 100644 index 00000000..9b0915ad --- /dev/null +++ b/test/env-active.test.ts @@ -0,0 +1,24 @@ +import { writeFile } from "node:fs/promises"; + +import { it } from "./it"; + +it("supports --help", async ({ expect, prismic }) => { + const { stdout, exitCode } = await prismic("env", ["active", "--help"]); + expect(exitCode).toBe(0); + expect(stdout).toContain("prismic env active [options]"); +}); + +it("prints production when no environment is active", async ({ expect, prismic, repo }) => { + const { stdout, exitCode } = await prismic("env", ["active"]); + expect(exitCode).toBe(0); + expect(stdout).toBe(repo); +}); + +it("reads the active environment from .env.local", async ({ expect, prismic, project }) => { + const path = new URL(".env.local", project); + await writeFile(path, "NEXT_PUBLIC_PRISMIC_ENVIRONMENT=my-repo-staging"); + + const { stdout, exitCode } = await prismic("env", ["active"]); + expect(exitCode).toBe(0); + expect(stdout).toBe("my-repo-staging"); +}); diff --git a/test/env-list.test.ts b/test/env-list.test.ts new file mode 100644 index 00000000..39d9cee4 --- /dev/null +++ b/test/env-list.test.ts @@ -0,0 +1,14 @@ +import { it } from "./it"; + +it("supports --help", async ({ expect, prismic }) => { + const { stdout, exitCode } = await prismic("env", ["list", "--help"]); + expect(exitCode).toBe(0); + expect(stdout).toContain("prismic env list [options]"); +}); + +it("lists environments including production", async ({ expect, prismic, repo }) => { + const { stdout, exitCode } = await prismic("env", ["list"]); + expect(exitCode).toBe(0); + expect(stdout).toContain(repo); + expect(stdout).toContain("prod"); +}); diff --git a/test/env-set.test.ts b/test/env-set.test.ts new file mode 100644 index 00000000..0ad445e9 --- /dev/null +++ b/test/env-set.test.ts @@ -0,0 +1,36 @@ +import { readFile, writeFile } from "node:fs/promises"; + +import { it } from "./it"; + +it("supports --help", async ({ expect, prismic }) => { + const { stdout, exitCode } = await prismic("env", ["set", "--help"]); + expect(exitCode).toBe(0); + expect(stdout).toContain("prismic env set [options]"); +}); + +// TODO: Test setting a non-production environment once the e2e setup can create +// one. It should write the environment's domain to .env.local as the active +// repository. The test repository only has production, so it can't be covered yet. + +it("setting production clears the active environment", async ({ + expect, + prismic, + project, + repo, +}) => { + const path = new URL(".env.local", project); + await writeFile(path, "NEXT_PUBLIC_PRISMIC_ENVIRONMENT=my-repo-staging"); + + const { stdout, exitCode } = await prismic("env", ["set", repo]); + expect(exitCode).toBe(0); + expect(stdout).toContain("production"); + + const after = await readFile(path, "utf8"); + expect(after).not.toContain("NEXT_PUBLIC_PRISMIC_ENVIRONMENT"); + expect(after).not.toContain("my-stage-env"); +}); + +it("rejects an unknown environment", async ({ expect, prismic }) => { + const { exitCode } = await prismic("env", ["set", "does-not-exist"]); + expect(exitCode).toBe(1); +}); diff --git a/test/env-unset.test.ts b/test/env-unset.test.ts new file mode 100644 index 00000000..dee9f9ae --- /dev/null +++ b/test/env-unset.test.ts @@ -0,0 +1,22 @@ +import { readFile, writeFile } from "node:fs/promises"; + +import { it } from "./it"; + +it("supports --help", async ({ expect, prismic }) => { + const { stdout, exitCode } = await prismic("env", ["unset", "--help"]); + expect(exitCode).toBe(0); + expect(stdout).toContain("prismic env unset [options]"); +}); + +it("resets to the production environment", async ({ expect, prismic, repo, project }) => { + const path = new URL(".env.local", project); + await writeFile(path, "NEXT_PUBLIC_PRISMIC_ENVIRONMENT=my-repo-staging"); + + const { stdout, exitCode } = await prismic("env", ["unset"]); + expect(exitCode).toBe(0); + expect(stdout).toContain(`Reset to the production environment "${repo}".`); + + const after = await readFile(path, "utf8"); + expect(after).not.toContain("NEXT_PUBLIC_PRISMIC_ENVIRONMENT"); + expect(after).not.toContain("my-stage-env"); +}); diff --git a/test/env.test.ts b/test/env.test.ts new file mode 100644 index 00000000..1af8902e --- /dev/null +++ b/test/env.test.ts @@ -0,0 +1,13 @@ +import { it } from "./it"; + +it("prints help by default", async ({ expect, prismic }) => { + const { stdout, exitCode } = await prismic("env"); + expect(exitCode).toBe(0); + expect(stdout).toContain("prismic env [options]"); +}); + +it("supports --help", async ({ expect, prismic }) => { + const { stdout, exitCode } = await prismic("env", ["--help"]); + expect(exitCode).toBe(0); + expect(stdout).toContain("prismic env [options]"); +}); From a2221504c20120c4c954167bf5028ac3c414a442 Mon Sep 17 00:00:00 2001 From: Angelo Ashmore Date: Thu, 16 Jul 2026 19:31:06 +0000 Subject: [PATCH 2/7] feat: wire env command and read active environment in clients Register `prismic env` in the command router and update the generated prismic.io client templates to resolve repositoryName from the active environment variable, falling back to the configured repository name. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/adapters/nextjs.templates.ts | 6 ++++-- src/adapters/sveltekit.templates.ts | 8 ++++++-- src/commands/index.ts | 5 +++++ test/env-active.test.ts | 14 +++++++------- test/env-set.test.ts | 2 +- 5 files changed, 23 insertions(+), 12 deletions(-) diff --git a/src/adapters/nextjs.templates.ts b/src/adapters/nextjs.templates.ts index fce35bca..3cac94b1 100644 --- a/src/adapters/nextjs.templates.ts +++ b/src/adapters/nextjs.templates.ts @@ -357,7 +357,8 @@ export function prismicIOFileTemplate(args: { /** * The project's Prismic repository name. */ - export const repositoryName = prismicConfig.repositoryName; + export const repositoryName = + process.env.NEXT_PUBLIC_PRISMIC_ENVIRONMENT ?? prismicConfig.repositoryName; ${createClientContents} `; @@ -369,7 +370,8 @@ export function prismicIOFileTemplate(args: { /** * The project's Prismic repository name. */ - export const repositoryName = prismicConfig.repositoryName; + export const repositoryName = + process.env.NEXT_PUBLIC_PRISMIC_ENVIRONMENT ?? prismicConfig.repositoryName; ${createClientContents} `; diff --git a/src/adapters/sveltekit.templates.ts b/src/adapters/sveltekit.templates.ts index 9a07509a..32699181 100644 --- a/src/adapters/sveltekit.templates.ts +++ b/src/adapters/sveltekit.templates.ts @@ -11,12 +11,14 @@ export function prismicIOFileTemplate(args: { typescript: boolean }): string { return dedent` import { createClient as baseCreateClient } from "@prismicio/client"; import { type CreateClientConfig, enableAutoPreviews } from '@prismicio/svelte/kit'; + import { env } from '$env/dynamic/public'; import prismicConfig from "../../prismic.config.json"; /** * The project's Prismic repository name. */ - export const repositoryName = prismicConfig.repositoryName; + export const repositoryName = + env.PUBLIC_PRISMIC_ENVIRONMENT ?? prismicConfig.repositoryName; /** * Creates a Prismic client for the project's repository. The client is used to @@ -40,12 +42,14 @@ export function prismicIOFileTemplate(args: { typescript: boolean }): string { return dedent` import { createClient as baseCreateClient } from "@prismicio/client"; import { enableAutoPreviews } from '@prismicio/svelte/kit'; + import { env } from '$env/dynamic/public'; import prismicConfig from "../../prismic.config.json"; /** * The project's Prismic repository name. */ - export const repositoryName = prismicConfig.repositoryName; + export const repositoryName = + env.PUBLIC_PRISMIC_ENVIRONMENT ?? prismicConfig.repositoryName; /** * Creates a Prismic client for the project's repository. The client is used to diff --git a/src/commands/index.ts b/src/commands/index.ts index cccbbdf2..78a952c7 100644 --- a/src/commands/index.ts +++ b/src/commands/index.ts @@ -1,5 +1,6 @@ import { createCommandRouter } from "../lib/command"; import docs from "./docs"; +import env from "./env"; import field from "./field"; import gen from "./gen"; import init from "./init"; @@ -56,6 +57,10 @@ export default createCommandRouter({ handler: status, description: "Show local vs remote model differences", }, + env: { + handler: env, + description: "Manage the active environment", + }, locale: { handler: locale, description: "Manage locales", diff --git a/test/env-active.test.ts b/test/env-active.test.ts index 9b0915ad..653de683 100644 --- a/test/env-active.test.ts +++ b/test/env-active.test.ts @@ -8,17 +8,17 @@ it("supports --help", async ({ expect, prismic }) => { expect(stdout).toContain("prismic env active [options]"); }); -it("prints production when no environment is active", async ({ expect, prismic, repo }) => { +it("prints the active environment", async ({ expect, prismic, project }) => { + const path = new URL(".env.local", project); + await writeFile(path, "NEXT_PUBLIC_PRISMIC_ENVIRONMENT=my-repo-staging"); + const { stdout, exitCode } = await prismic("env", ["active"]); expect(exitCode).toBe(0); - expect(stdout).toBe(repo); + expect(stdout).toContain("my-repo-staging"); }); -it("reads the active environment from .env.local", async ({ expect, prismic, project }) => { - const path = new URL(".env.local", project); - await writeFile(path, "NEXT_PUBLIC_PRISMIC_ENVIRONMENT=my-repo-staging"); - +it("prints production when no environment is active", async ({ expect, prismic, repo }) => { const { stdout, exitCode } = await prismic("env", ["active"]); expect(exitCode).toBe(0); - expect(stdout).toBe("my-repo-staging"); + expect(stdout).toContain(repo); }); diff --git a/test/env-set.test.ts b/test/env-set.test.ts index 0ad445e9..44bc2324 100644 --- a/test/env-set.test.ts +++ b/test/env-set.test.ts @@ -12,7 +12,7 @@ it("supports --help", async ({ expect, prismic }) => { // one. It should write the environment's domain to .env.local as the active // repository. The test repository only has production, so it can't be covered yet. -it("setting production clears the active environment", async ({ +it("clears the active environment when setting to production", async ({ expect, prismic, project, From c74abb36f2c14d91af24066e84b8bb12965aa73c Mon Sep 17 00:00:00 2001 From: Angelo Ashmore Date: Thu, 16 Jul 2026 22:20:41 +0000 Subject: [PATCH 3/7] fix: address env review feedback - getEnvironment surfaces corrupt .env.local instead of treating it as unset - env list marks the active environment only for the project's own repo - setEnvFileVar/unsetEnvFileVar update every occurrence of a key Co-Authored-By: Claude Opus 4.8 (1M context) --- src/adapters/index.ts | 6 ++++-- src/commands/env-list.ts | 8 ++++++-- src/lib/file.ts | 4 ++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/adapters/index.ts b/src/adapters/index.ts index 02020226..20ccae0d 100644 --- a/src/adapters/index.ts +++ b/src/adapters/index.ts @@ -7,6 +7,7 @@ import { generateTypes } from "prismic-ts-codegen"; import { glob } from "tinyglobby"; import { + exists, readEnvFile, readJsonFile, setEnvFileVar, @@ -217,8 +218,9 @@ export abstract class Adapter { async getEnvironment(): Promise { const projectRoot = await findProjectRoot(); const envLocalPath = new URL(".env.local", projectRoot); - const envLocalVars = await readEnvFile(envLocalPath).catch(() => {}); - return envLocalVars?.[this.environmentEnvVarName] || undefined; + if (!(await exists(envLocalPath))) return undefined; + const envLocalVars = await readEnvFile(envLocalPath); + return envLocalVars[this.environmentEnvVarName] || undefined; } async setEnvironment(environment: string): Promise { diff --git a/src/commands/env-list.ts b/src/commands/env-list.ts index b9337065..71f30527 100644 --- a/src/commands/env-list.ts +++ b/src/commands/env-list.ts @@ -16,13 +16,17 @@ const config = { } satisfies CommandConfig; export default createCommand(config, async ({ values }) => { - const { json, repo = await getRepositoryName() } = values; + const projectRepo = await getRepositoryName(); + const { json, repo = projectRepo } = values; const { token, host } = await getCredentials(); const environments = await getUserEnvironments({ repo, token, host }); + // The active environment is a project setting, so it only applies to the + // project's own repository, not one passed via --repo. const adapter = await getAdapter(); - const activeEnvironment = (await adapter.getEnvironment()) ?? (await getRepositoryName()); + const activeEnvironment = + repo === projectRepo ? ((await adapter.getEnvironment()) ?? projectRepo) : undefined; if (json) { const results = environments.map((environment) => ({ diff --git a/src/lib/file.ts b/src/lib/file.ts index c0e3a066..d7a6bb9f 100644 --- a/src/lib/file.ts +++ b/src/lib/file.ts @@ -120,7 +120,7 @@ export async function setEnvFileVar(path: URL, key: string, value: string): Prom parseEnv(contents); // Verify the format } - const pattern = new RegExp(`^${key}=.*$`, "m"); + const pattern = new RegExp(`^${key}=.*$`, "mg"); const line = `${key}=${value}`; const hasEnvironmentVar = pattern.test(contents); @@ -141,7 +141,7 @@ export async function unsetEnvFileVar(path: URL, key: string): Promise { let contents = await readFile(path, "utf8"); parseEnv(contents); // Verify the format - const pattern = new RegExp(`^${key}=.*$\n?`, "m"); + const pattern = new RegExp(`^${key}=.*$\n?`, "mg"); contents = contents.replace(pattern, ""); await writeFile(path, contents); From 2e3217efca4de08d5a38db43341c528623d667ce Mon Sep 17 00:00:00 2001 From: Angelo Ashmore Date: Fri, 24 Jul 2026 20:03:34 +0000 Subject: [PATCH 4/7] feat: hide env commands from help and mark them experimental Co-Authored-By: Claude Fable 5 --- src/commands/env-active.ts | 6 +++++- src/commands/env-list.ts | 6 +++++- src/commands/env-set.ts | 6 +++++- src/commands/env-unset.ts | 6 +++++- src/commands/env.ts | 6 +++++- src/commands/index.ts | 1 + src/lib/command.ts | 8 +++++--- 7 files changed, 31 insertions(+), 8 deletions(-) diff --git a/src/commands/env-active.ts b/src/commands/env-active.ts index e3f1b8a9..3040fdec 100644 --- a/src/commands/env-active.ts +++ b/src/commands/env-active.ts @@ -4,7 +4,11 @@ import { getRepositoryName } from "../project"; const config = { name: "prismic env active", - description: "Print the active environment.", + description: ` + Print the active environment. + + @experimental - This command may change or be removed in any release and does not follow semver. + `, } satisfies CommandConfig; export default createCommand(config, async () => { diff --git a/src/commands/env-list.ts b/src/commands/env-list.ts index 71f30527..62bdb687 100644 --- a/src/commands/env-list.ts +++ b/src/commands/env-list.ts @@ -8,7 +8,11 @@ import { getRepositoryName } from "../project"; const config = { name: "prismic env list", - description: "List the environments available for the project.", + description: ` + List the environments available for the project. + + @experimental - This command may change or be removed in any release and does not follow semver. + `, options: { json: { type: "boolean", description: "Output as JSON" }, repo: { type: "string", short: "r", description: "Repository domain" }, diff --git a/src/commands/env-set.ts b/src/commands/env-set.ts index cc1d0f2d..6da8edf9 100644 --- a/src/commands/env-set.ts +++ b/src/commands/env-set.ts @@ -6,7 +6,11 @@ import { getRepositoryName } from "../project"; const config = { name: "prismic env set", - description: "Set the active environment.", + description: ` + Set the active environment. + + @experimental - This command may change or be removed in any release and does not follow semver. + `, positionals: { environment: { description: "Environment domain", required: true }, }, diff --git a/src/commands/env-unset.ts b/src/commands/env-unset.ts index 7954acbe..92e0959e 100644 --- a/src/commands/env-unset.ts +++ b/src/commands/env-unset.ts @@ -4,7 +4,11 @@ import { getRepositoryName } from "../project"; const config = { name: "prismic env unset", - description: "Reset the active environment to the production environment.", + description: ` + Reset the active environment to the production environment. + + @experimental - This command may change or be removed in any release and does not follow semver. + `, } satisfies CommandConfig; export default createCommand(config, async () => { diff --git a/src/commands/env.ts b/src/commands/env.ts index d2e75f4c..a79ac777 100644 --- a/src/commands/env.ts +++ b/src/commands/env.ts @@ -6,7 +6,11 @@ import envUnset from "./env-unset"; export default createCommandRouter({ name: "prismic env", - description: "Manage the active environment for a Prismic project.", + description: ` + Manage the active environment for a Prismic project. + + @experimental - This command may change or be removed in any release and does not follow semver. + `, commands: { set: { handler: envSet, diff --git a/src/commands/index.ts b/src/commands/index.ts index 78a952c7..e715d228 100644 --- a/src/commands/index.ts +++ b/src/commands/index.ts @@ -60,6 +60,7 @@ export default createCommandRouter({ env: { handler: env, description: "Manage the active environment", + hidden: true, }, locale: { handler: locale, diff --git a/src/lib/command.ts b/src/lib/command.ts index 3cd2d476..cb513237 100644 --- a/src/lib/command.ts +++ b/src/lib/command.ts @@ -169,7 +169,7 @@ type CreateCommandRouterConfig = { sections?: Record; commands: Record; }; -type RouterCommand = { handler: () => Promise; description: string }; +type RouterCommand = { handler: () => Promise; description: string; hidden?: boolean }; export function createCommandRouter(config: CreateCommandRouterConfig): () => Promise { const depth = config.name.split(" ").length; @@ -203,7 +203,7 @@ export function createCommandRouter(config: CreateCommandRouterConfig): () => Pr function buildRouterHelp(config: CreateCommandRouterConfig): string { const { name, description, sections, commands } = config; - const lines = [description]; + const lines = [dedent(description)]; lines.push(""); lines.push("USAGE"); @@ -211,7 +211,9 @@ function buildRouterHelp(config: CreateCommandRouterConfig): string { lines.push(""); lines.push("COMMANDS"); - const commandRows = Object.entries(commands).map(([name, cmd]) => [` ${name}`, cmd.description]); + const commandRows = Object.entries(commands) + .filter(([, cmd]) => !cmd.hidden) + .map(([name, cmd]) => [` ${name}`, cmd.description]); lines.push(formatTable(commandRows)); lines.push(""); From 81875ef9c1db3fc6998ed1bc3f4c1ad0141dfc98 Mon Sep 17 00:00:00 2001 From: Angelo Ashmore Date: Fri, 24 Jul 2026 20:08:11 +0000 Subject: [PATCH 5/7] refactor: remove env file format checks that can never throw Node's util.parseEnv is a best-effort parser with no error path for string input, so the "verify the format" calls were dead code. Co-Authored-By: Claude Fable 5 --- src/lib/file.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/lib/file.ts b/src/lib/file.ts index d7a6bb9f..009e1e86 100644 --- a/src/lib/file.ts +++ b/src/lib/file.ts @@ -115,10 +115,7 @@ export async function readEnvFile>>( export async function setEnvFileVar(path: URL, key: string, value: string): Promise { const hasFile = await exists(path); let contents = ""; - if (hasFile) { - contents = await readFile(path, "utf8"); - parseEnv(contents); // Verify the format - } + if (hasFile) contents = await readFile(path, "utf8"); const pattern = new RegExp(`^${key}=.*$`, "mg"); const line = `${key}=${value}`; @@ -139,7 +136,6 @@ export async function unsetEnvFileVar(path: URL, key: string): Promise { if (!hasFile) return; let contents = await readFile(path, "utf8"); - parseEnv(contents); // Verify the format const pattern = new RegExp(`^${key}=.*$\n?`, "mg"); contents = contents.replace(pattern, ""); From ea91d5b2b223dcdb68a8a0d9a4f2ecfbd3bc85f1 Mon Sep 17 00:00:00 2001 From: Angelo Ashmore Date: Fri, 24 Jul 2026 11:17:20 -1000 Subject: [PATCH 6/7] feat: use active environment across commands (#228) * refactor: consume active environment across commands Migrate the existing commands (locale, preview, token, webhook, pull, push, status, sync) to resolve the target repository from the persisted active environment. Deprecate the per-command --env flag, making it an alias for --repo, and remove resolveEnvironment along with its API-side validation. Co-Authored-By: Claude Opus 4.8 (1M context) * fix: label status Environment line from the resolved repo Show an explicit --repo target on the status Environment line, not just --env or the active environment. Co-Authored-By: Claude Opus 4.8 (1M context) * feat: hide deprecated options from help and warn on use Co-Authored-By: Claude Fable 5 * fix: record onboarding steps on the production repository Co-Authored-By: Claude Fable 5 * fix: resolve the repository lazily so --repo works without a framework Co-Authored-By: Claude Fable 5 --------- Co-authored-by: Claude Opus 4.8 (1M context) --- src/adapters/index.ts | 7 ++++++- src/commands/env-list.ts | 10 +++++----- src/commands/locale-add.ts | 15 +++++++++------ src/commands/locale-list.ts | 15 +++++++++------ src/commands/locale-remove.ts | 15 +++++++++------ src/commands/locale-set-master.ts | 15 +++++++++------ src/commands/preview-add.ts | 15 +++++++++------ src/commands/preview-list.ts | 15 +++++++++------ src/commands/preview-remove.ts | 15 +++++++++------ src/commands/preview-set-simulator.ts | 15 +++++++++------ src/commands/pull.ts | 25 ++++++++++++++++--------- src/commands/push.ts | 25 ++++++++++++++++--------- src/commands/status.ts | 27 +++++++++++++++------------ src/commands/sync.ts | 26 +++++++++++++++----------- src/commands/token-create.ts | 15 +++++++++------ src/commands/token-delete.ts | 15 +++++++++------ src/commands/token-list.ts | 15 +++++++++------ src/commands/webhook-create.ts | 21 +++++++++++++++------ src/commands/webhook-disable.ts | 15 +++++++++------ src/commands/webhook-enable.ts | 15 +++++++++------ src/commands/webhook-list.ts | 15 +++++++++------ src/commands/webhook-remove.ts | 15 +++++++++------ src/commands/webhook-set-triggers.ts | 15 +++++++++------ src/commands/webhook-view.ts | 15 +++++++++------ src/lib/command.ts | 5 +++++ src/lib/prismic/environments.ts | 22 ---------------------- test/pull.test.ts | 6 ------ test/push.test.ts | 6 ------ test/status.test.ts | 14 -------------- 29 files changed, 246 insertions(+), 203 deletions(-) diff --git a/src/adapters/index.ts b/src/adapters/index.ts index 20ccae0d..44d487bc 100644 --- a/src/adapters/index.ts +++ b/src/adapters/index.ts @@ -17,7 +17,7 @@ import { import { stringify } from "../lib/json"; import { readPackageJson } from "../lib/packageJson"; import { appendTrailingSlash } from "../lib/url"; -import { addRoute, removeRoute, updateRoute } from "../project"; +import { addRoute, getRepositoryName, removeRoute, updateRoute } from "../project"; import { findProjectRoot, getLibraries } from "../project"; const TYPES_FILENAME = "prismicio-types.d.ts"; @@ -49,6 +49,11 @@ export class NoSupportedFrameworkError extends Error { "No supported framework found. Run this command in a Next.js, Nuxt, or SvelteKit project."; } +export async function getActiveRepositoryName(): Promise { + const adapter = await getAdapter(); + return (await adapter.getEnvironment()) ?? (await getRepositoryName()); +} + export abstract class Adapter { abstract readonly id: string; diff --git a/src/commands/env-list.ts b/src/commands/env-list.ts index 62bdb687..53eb73fc 100644 --- a/src/commands/env-list.ts +++ b/src/commands/env-list.ts @@ -26,11 +26,11 @@ export default createCommand(config, async ({ values }) => { const { token, host } = await getCredentials(); const environments = await getUserEnvironments({ repo, token, host }); - // The active environment is a project setting, so it only applies to the - // project's own repository, not one passed via --repo. - const adapter = await getAdapter(); - const activeEnvironment = - repo === projectRepo ? ((await adapter.getEnvironment()) ?? projectRepo) : undefined; + let activeEnvironment: string | undefined; + if (repo === projectRepo) { + const adapter = await getAdapter(); + activeEnvironment = (await adapter.getEnvironment()) ?? projectRepo; + } if (json) { const results = environments.map((environment) => ({ diff --git a/src/commands/locale-add.ts b/src/commands/locale-add.ts index 476b82df..cc3ff4f8 100644 --- a/src/commands/locale-add.ts +++ b/src/commands/locale-add.ts @@ -1,8 +1,7 @@ +import { getActiveRepositoryName } from "../adapters"; import { getCredentials } from "../auth"; import { createCommand, type CommandConfig } from "../lib/command"; import { upsertLocale } from "../lib/prismic/clients/locale"; -import { resolveEnvironment } from "../lib/prismic/environments"; -import { getRepositoryName } from "../project"; const config = { name: "prismic locale add", @@ -18,17 +17,21 @@ const config = { options: { master: { type: "boolean", description: "Set as the master locale" }, name: { type: "string", short: "n", description: "Custom display name (for custom locales)" }, - repo: { type: "string", short: "r", description: "Repository domain" }, - env: { type: "string", short: "e", description: "Environment domain" }, + repo: { type: "string", short: "r", description: "Repository or environment domain" }, + env: { + type: "string", + short: "e", + description: "Alias for --repo", + deprecated: "Use `prismic env` or --repo instead.", + }, }, } satisfies CommandConfig; export default createCommand(config, async ({ positionals, values }) => { const [code] = positionals; - const { repo: parentRepo = await getRepositoryName(), env, master = false, name } = values; + const { env, repo = env ?? (await getActiveRepositoryName()), master = false, name } = values; const { token, host } = await getCredentials(); - const repo = env ? await resolveEnvironment(env, { repo: parentRepo, token, host }) : parentRepo; await upsertLocale({ id: code, isMaster: master, customName: name }, { repo, token, host }); diff --git a/src/commands/locale-list.ts b/src/commands/locale-list.ts index a2305f7e..14bf8cba 100644 --- a/src/commands/locale-list.ts +++ b/src/commands/locale-list.ts @@ -1,10 +1,9 @@ +import { getActiveRepositoryName } from "../adapters"; import { getCredentials } from "../auth"; import { createCommand, type CommandConfig } from "../lib/command"; import { stringify } from "../lib/json"; import { getLocales } from "../lib/prismic/clients/locale"; -import { resolveEnvironment } from "../lib/prismic/environments"; import { formatTable } from "../lib/string"; -import { getRepositoryName } from "../project"; const config = { name: "prismic locale list", @@ -16,16 +15,20 @@ const config = { `, options: { json: { type: "boolean", description: "Output as JSON" }, - repo: { type: "string", short: "r", description: "Repository domain" }, - env: { type: "string", short: "e", description: "Environment domain" }, + repo: { type: "string", short: "r", description: "Repository or environment domain" }, + env: { + type: "string", + short: "e", + description: "Alias for --repo", + deprecated: "Use `prismic env` or --repo instead.", + }, }, } satisfies CommandConfig; export default createCommand(config, async ({ values }) => { - const { repo: parentRepo = await getRepositoryName(), env, json } = values; + const { env, repo = env ?? (await getActiveRepositoryName()), json } = values; const { token, host } = await getCredentials(); - const repo = env ? await resolveEnvironment(env, { repo: parentRepo, token, host }) : parentRepo; const locales = await getLocales({ repo, token, host }); diff --git a/src/commands/locale-remove.ts b/src/commands/locale-remove.ts index 99b53efb..66da2df5 100644 --- a/src/commands/locale-remove.ts +++ b/src/commands/locale-remove.ts @@ -1,8 +1,7 @@ +import { getActiveRepositoryName } from "../adapters"; import { getCredentials } from "../auth"; import { createCommand, type CommandConfig } from "../lib/command"; import { removeLocale } from "../lib/prismic/clients/locale"; -import { resolveEnvironment } from "../lib/prismic/environments"; -import { getRepositoryName } from "../project"; const config = { name: "prismic locale remove", @@ -16,17 +15,21 @@ const config = { code: { description: "Locale code (e.g. en-us, fr-fr)", required: true }, }, options: { - repo: { type: "string", short: "r", description: "Repository domain" }, - env: { type: "string", short: "e", description: "Environment domain" }, + repo: { type: "string", short: "r", description: "Repository or environment domain" }, + env: { + type: "string", + short: "e", + description: "Alias for --repo", + deprecated: "Use `prismic env` or --repo instead.", + }, }, } satisfies CommandConfig; export default createCommand(config, async ({ positionals, values }) => { const [code] = positionals; - const { repo: parentRepo = await getRepositoryName(), env } = values; + const { env, repo = env ?? (await getActiveRepositoryName()) } = values; const { token, host } = await getCredentials(); - const repo = env ? await resolveEnvironment(env, { repo: parentRepo, token, host }) : parentRepo; await removeLocale(code, { repo, token, host }); diff --git a/src/commands/locale-set-master.ts b/src/commands/locale-set-master.ts index 9bcf6072..029c1990 100644 --- a/src/commands/locale-set-master.ts +++ b/src/commands/locale-set-master.ts @@ -1,8 +1,7 @@ +import { getActiveRepositoryName } from "../adapters"; import { getCredentials } from "../auth"; import { CommandError, createCommand, type CommandConfig } from "../lib/command"; import { getLocales, upsertLocale } from "../lib/prismic/clients/locale"; -import { resolveEnvironment } from "../lib/prismic/environments"; -import { getRepositoryName } from "../project"; const config = { name: "prismic locale set-master", @@ -16,17 +15,21 @@ const config = { code: { description: "Locale code (e.g. en-us, fr-fr)", required: true }, }, options: { - repo: { type: "string", short: "r", description: "Repository domain" }, - env: { type: "string", short: "e", description: "Environment domain" }, + repo: { type: "string", short: "r", description: "Repository or environment domain" }, + env: { + type: "string", + short: "e", + description: "Alias for --repo", + deprecated: "Use `prismic env` or --repo instead.", + }, }, } satisfies CommandConfig; export default createCommand(config, async ({ positionals, values }) => { const [code] = positionals; - const { repo: parentRepo = await getRepositoryName(), env } = values; + const { env, repo = env ?? (await getActiveRepositoryName()) } = values; const { token, host } = await getCredentials(); - const repo = env ? await resolveEnvironment(env, { repo: parentRepo, token, host }) : parentRepo; const locales = await getLocales({ repo, token, host }); diff --git a/src/commands/preview-add.ts b/src/commands/preview-add.ts index c9dfcc14..da639975 100644 --- a/src/commands/preview-add.ts +++ b/src/commands/preview-add.ts @@ -1,8 +1,7 @@ +import { getActiveRepositoryName } from "../adapters"; import { getCredentials } from "../auth"; import { CommandError, createCommand, type CommandConfig } from "../lib/command"; import { addPreview } from "../lib/prismic/clients/core"; -import { resolveEnvironment } from "../lib/prismic/environments"; -import { getRepositoryName } from "../project"; const config = { name: "prismic preview add", @@ -17,14 +16,19 @@ const config = { }, options: { name: { type: "string", short: "n", description: "Display name (defaults to hostname)" }, - repo: { type: "string", short: "r", description: "Repository domain" }, - env: { type: "string", short: "e", description: "Environment domain" }, + repo: { type: "string", short: "r", description: "Repository or environment domain" }, + env: { + type: "string", + short: "e", + description: "Alias for --repo", + deprecated: "Use `prismic env` or --repo instead.", + }, }, } satisfies CommandConfig; export default createCommand(config, async ({ positionals, values }) => { const [previewUrl] = positionals; - const { repo: parentRepo = await getRepositoryName(), env, name } = values; + const { env, repo = env ?? (await getActiveRepositoryName()), name } = values; let parsed: URL; try { @@ -38,7 +42,6 @@ export default createCommand(config, async ({ positionals, values }) => { const resolverPath = parsed.pathname === "/" ? undefined : parsed.pathname; const { token, host } = await getCredentials(); - const repo = env ? await resolveEnvironment(env, { repo: parentRepo, token, host }) : parentRepo; await addPreview({ name: displayName, websiteURL, resolverPath }, { repo, token, host }); diff --git a/src/commands/preview-list.ts b/src/commands/preview-list.ts index d6fd9050..fba51177 100644 --- a/src/commands/preview-list.ts +++ b/src/commands/preview-list.ts @@ -1,10 +1,9 @@ +import { getActiveRepositoryName } from "../adapters"; import { getCredentials } from "../auth"; import { createCommand, type CommandConfig } from "../lib/command"; import { stringify } from "../lib/json"; import { getPreviews, getSimulatorUrl } from "../lib/prismic/clients/core"; -import { resolveEnvironment } from "../lib/prismic/environments"; import { formatTable } from "../lib/string"; -import { getRepositoryName } from "../project"; const config = { name: "prismic preview list", @@ -16,16 +15,20 @@ const config = { `, options: { json: { type: "boolean", description: "Output as JSON" }, - repo: { type: "string", short: "r", description: "Repository domain" }, - env: { type: "string", short: "e", description: "Environment domain" }, + repo: { type: "string", short: "r", description: "Repository or environment domain" }, + env: { + type: "string", + short: "e", + description: "Alias for --repo", + deprecated: "Use `prismic env` or --repo instead.", + }, }, } satisfies CommandConfig; export default createCommand(config, async ({ values }) => { - const { repo: parentRepo = await getRepositoryName(), env, json } = values; + const { env, repo = env ?? (await getActiveRepositoryName()), json } = values; const { token, host } = await getCredentials(); - const repo = env ? await resolveEnvironment(env, { repo: parentRepo, token, host }) : parentRepo; const [previews, simulatorUrl] = await Promise.all([ getPreviews({ repo, token, host }), diff --git a/src/commands/preview-remove.ts b/src/commands/preview-remove.ts index 676eb0ea..93ef5b9c 100644 --- a/src/commands/preview-remove.ts +++ b/src/commands/preview-remove.ts @@ -1,8 +1,7 @@ +import { getActiveRepositoryName } from "../adapters"; import { getCredentials } from "../auth"; import { CommandError, createCommand, type CommandConfig } from "../lib/command"; import { getPreviews, removePreview } from "../lib/prismic/clients/core"; -import { resolveEnvironment } from "../lib/prismic/environments"; -import { getRepositoryName } from "../project"; const config = { name: "prismic preview remove", @@ -16,17 +15,21 @@ const config = { url: { description: "Preview URL to remove", required: true }, }, options: { - repo: { type: "string", short: "r", description: "Repository domain" }, - env: { type: "string", short: "e", description: "Environment domain" }, + repo: { type: "string", short: "r", description: "Repository or environment domain" }, + env: { + type: "string", + short: "e", + description: "Alias for --repo", + deprecated: "Use `prismic env` or --repo instead.", + }, }, } satisfies CommandConfig; export default createCommand(config, async ({ positionals, values }) => { const [previewUrl] = positionals; - const { repo: parentRepo = await getRepositoryName(), env } = values; + const { env, repo = env ?? (await getActiveRepositoryName()) } = values; const { token, host } = await getCredentials(); - const repo = env ? await resolveEnvironment(env, { repo: parentRepo, token, host }) : parentRepo; const previews = await getPreviews({ repo, token, host }); diff --git a/src/commands/preview-set-simulator.ts b/src/commands/preview-set-simulator.ts index afeab587..b5f1843d 100644 --- a/src/commands/preview-set-simulator.ts +++ b/src/commands/preview-set-simulator.ts @@ -1,8 +1,7 @@ +import { getActiveRepositoryName } from "../adapters"; import { getCredentials } from "../auth"; import { CommandError, createCommand, type CommandConfig } from "../lib/command"; import { setSimulatorUrl } from "../lib/prismic/clients/core"; -import { resolveEnvironment } from "../lib/prismic/environments"; -import { getRepositoryName } from "../project"; const config = { name: "prismic preview set-simulator", @@ -22,14 +21,19 @@ const config = { }, }, options: { - repo: { type: "string", short: "r", description: "Repository domain" }, - env: { type: "string", short: "e", description: "Environment domain" }, + repo: { type: "string", short: "r", description: "Repository or environment domain" }, + env: { + type: "string", + short: "e", + description: "Alias for --repo", + deprecated: "Use `prismic env` or --repo instead.", + }, }, } satisfies CommandConfig; export default createCommand(config, async ({ positionals, values }) => { const [urlArg] = positionals; - const { repo: parentRepo = await getRepositoryName(), env } = values; + const { env, repo = env ?? (await getActiveRepositoryName()) } = values; let parsed: URL; try { @@ -44,7 +48,6 @@ export default createCommand(config, async ({ positionals, values }) => { const simulatorUrl = parsed.toString(); const { token, host } = await getCredentials(); - const repo = env ? await resolveEnvironment(env, { repo: parentRepo, token, host }) : parentRepo; await setSimulatorUrl(simulatorUrl, { repo, token, host }); diff --git a/src/commands/pull.ts b/src/commands/pull.ts index 59c07ef5..4afad55b 100644 --- a/src/commands/pull.ts +++ b/src/commands/pull.ts @@ -5,7 +5,6 @@ import { diffArrays } from "../lib/diff"; import { getDirtyPaths, getGitRoot } from "../lib/git"; import { getCustomTypes, getSlices } from "../lib/prismic/clients/custom-types"; import { completeOnboardingStepsSilently } from "../lib/prismic/clients/repository"; -import { resolveEnvironment } from "../lib/prismic/environments"; import { canonicalizeModel } from "../lib/prismic/models"; import { isDescendant, relativePathname } from "../lib/url"; import { findProjectRoot, getRepositoryName } from "../project"; @@ -20,21 +19,29 @@ const config = { `, options: { force: { type: "boolean", short: "f", description: "Overwrite local changes" }, - repo: { type: "string", short: "r", description: "Repository domain" }, - env: { type: "string", short: "e", description: "Environment domain" }, + repo: { type: "string", short: "r", description: "Repository or environment domain" }, + env: { + type: "string", + short: "e", + description: "Alias for --repo", + deprecated: "Use `prismic env` or --repo instead.", + }, }, } satisfies CommandConfig; export default createCommand(config, async ({ values }) => { - const { force = false, repo: parentRepo = await getRepositoryName(), env } = values; + const adapter = await getAdapter(); + + const { + force = false, + env, + repo = env ?? (await adapter.getEnvironment()) ?? (await getRepositoryName()), + } = values; const { token, host } = await getCredentials(); - const adapter = await getAdapter(); const projectRoot = await findProjectRoot(); - const repo = env ? await resolveEnvironment(env, { repo: parentRepo, token, host }) : parentRepo; - - console.info(`Pulling from repository: ${parentRepo}${env ? ` (env: ${env})` : ""}`); + console.info(`Pulling from repository: ${repo}`); const [gitRoot, customTypeLibraries, sliceLibraries] = await Promise.all([ getGitRoot(projectRoot), @@ -139,7 +146,7 @@ export default createCommand(config, async ({ values }) => { await adapter.generateTypes(); await completeOnboardingStepsSilently({ - repo: parentRepo, + repo: await getRepositoryName(), token, host, stepIds: ["connectPrismic"], diff --git a/src/commands/push.ts b/src/commands/push.ts index 6a1a0bd4..00369684 100644 --- a/src/commands/push.ts +++ b/src/commands/push.ts @@ -23,7 +23,6 @@ import { completeOnboardingStepsSilently, type OnboardingStep, } from "../lib/prismic/clients/repository"; -import { resolveEnvironment } from "../lib/prismic/environments"; import { canonicalizeModel } from "../lib/prismic/models"; import { BadRequestError } from "../lib/request"; import { appendTrailingSlash, isDescendant, relativePathname } from "../lib/url"; @@ -39,21 +38,29 @@ const config = { `, options: { force: { type: "boolean", short: "f", description: "Skip safety checks" }, - repo: { type: "string", short: "r", description: "Repository domain" }, - env: { type: "string", short: "e", description: "Environment domain" }, + repo: { type: "string", short: "r", description: "Repository or environment domain" }, + env: { + type: "string", + short: "e", + description: "Alias for --repo", + deprecated: "Use `prismic env` or --repo instead.", + }, }, } satisfies CommandConfig; export default createCommand(config, async ({ values }) => { - const { force = false, repo: parentRepo = await getRepositoryName(), env } = values; + const adapter = await getAdapter(); + + const { + force = false, + env, + repo = env ?? (await adapter.getEnvironment()) ?? (await getRepositoryName()), + } = values; const { token, host } = await getCredentials(); - const adapter = await getAdapter(); const projectRoot = await findProjectRoot(); - const repo = env ? await resolveEnvironment(env, { repo: parentRepo, token, host }) : parentRepo; - - console.info(`Pushing to repository: ${parentRepo}${env ? ` (env: ${env})` : ""}`); + console.info(`Pushing to repository: ${repo}`); const [gitRoot, customTypeLibraries, sliceLibraries] = await Promise.all([ getGitRoot(projectRoot), @@ -166,7 +173,7 @@ export default createCommand(config, async ({ values }) => { } if (onboardingSteps.length > 0) { await completeOnboardingStepsSilently({ - repo: parentRepo, + repo: await getRepositoryName(), token, host, stepIds: onboardingSteps, diff --git a/src/commands/status.ts b/src/commands/status.ts index 8e23d36b..9625b543 100644 --- a/src/commands/status.ts +++ b/src/commands/status.ts @@ -7,7 +7,6 @@ import { diffArrays, type ArrayDiff } from "../lib/diff"; import { getDirtyPaths, getGitRoot } from "../lib/git"; import { getCustomTypes, getSlices } from "../lib/prismic/clients/custom-types"; import { getProfile } from "../lib/prismic/clients/user"; -import { resolveEnvironment } from "../lib/prismic/environments"; import { canonicalizeModel } from "../lib/prismic/models"; import { dedent } from "../lib/string"; import { isDescendant, relativePathname } from "../lib/url"; @@ -22,16 +21,24 @@ const config = { model files with uncommitted git changes that would block pull and push. `, options: { - repo: { type: "string", short: "r", description: "Repository domain" }, - env: { type: "string", short: "e", description: "Environment domain" }, + repo: { type: "string", short: "r", description: "Repository or environment domain" }, + env: { + type: "string", + short: "e", + description: "Alias for --repo", + deprecated: "Use `prismic env` or --repo instead.", + }, }, } satisfies CommandConfig; export default createCommand(config, async ({ values }) => { - const { repo: parentRepo = await getRepositoryName(), env } = values; + const adapter = await getAdapter(); + + const repositoryName = await getRepositoryName(); + const activeEnvironment = await adapter.getEnvironment(); + const { env, repo = env ?? activeEnvironment ?? repositoryName } = values; const { token, host } = await getCredentials(); - const adapter = await getAdapter(); const projectRoot = await findProjectRoot(); const [gitRoot, customTypeLibraries, sliceLibraries, localCustomTypesMeta, localSlicesMeta] = @@ -43,14 +50,10 @@ export default createCommand(config, async ({ values }) => { adapter.getSlices(), ]); - let repo = parentRepo; let userEmail: string | undefined; let customTypeOps: ArrayDiff | undefined; let sliceOps: ArrayDiff | undefined; if (token) { - if (env) { - repo = await resolveEnvironment(env, { repo: parentRepo, token, host }); - } const [profile, remoteCustomTypes, remoteSlices] = await Promise.all([ getProfile({ token, host }), getCustomTypes({ repo, token, host }), @@ -91,9 +94,9 @@ export default createCommand(config, async ({ values }) => { .map((path) => relativePathname(projectRoot, path)); } - console.info(`Repository: ${parentRepo}`); - if (env) { - console.info(`Environment: ${env}`); + console.info(`Repository: ${repositoryName}`); + if (repo !== repositoryName) { + console.info(`Environment: ${repo}`); } if (userEmail) { console.info(`Authenticated as: ${userEmail}`); diff --git a/src/commands/sync.ts b/src/commands/sync.ts index 7251b96b..20c79c4e 100644 --- a/src/commands/sync.ts +++ b/src/commands/sync.ts @@ -9,7 +9,6 @@ import { createCommand, type CommandConfig, CommandError } from "../lib/command" import { diffArrays } from "../lib/diff"; import { getCustomTypes, getSlices } from "../lib/prismic/clients/custom-types"; import { completeOnboardingStepsSilently } from "../lib/prismic/clients/repository"; -import { resolveEnvironment } from "../lib/prismic/environments"; import { getRepositoryName } from "../project"; import { trackCommandStart, trackCommandEnd } from "../tracking"; @@ -30,20 +29,25 @@ const config = { description: "Watch for changes and sync continuously", required: true, }, - repo: { type: "string", short: "r", description: "Repository domain" }, - env: { type: "string", short: "e", description: "Environment domain" }, + repo: { type: "string", short: "r", description: "Repository or environment domain" }, + env: { + type: "string", + short: "e", + description: "Alias for --repo", + deprecated: "Use `prismic env` or --repo instead.", + }, }, } satisfies CommandConfig; export default createCommand(config, async ({ values }) => { - const { repo: parentRepo = await getRepositoryName(), env: envFlag } = values; - - const { token, host } = await getCredentials(); const adapter = await getAdapter(); - const repo = envFlag - ? await resolveEnvironment(envFlag, { repo: parentRepo, token, host }) - : parentRepo; + const { + env: envFlag, + repo = envFlag ?? (await adapter.getEnvironment()) ?? (await getRepositoryName()), + } = values; + + const { token, host } = await getCredentials(); trackCommandStart("sync", { watch: true }); process.on("SIGINT", () => { @@ -53,7 +57,7 @@ export default createCommand(config, async ({ values }) => { }); console.info( - `Watching repository: ${parentRepo}${envFlag ? ` (env: ${envFlag})` : ""} (polling every ${POLL_INTERVAL_MS / 1000}s, Ctrl+C to stop)`, + `Watching repository: ${repo} (polling every ${POLL_INTERVAL_MS / 1000}s, Ctrl+C to stop)`, ); let lastHash = ""; @@ -118,7 +122,7 @@ export default createCommand(config, async ({ values }) => { if (isInitial) { await completeOnboardingStepsSilently({ - repo: parentRepo, + repo: await getRepositoryName(), token, host, stepIds: ["connectPrismic"], diff --git a/src/commands/token-create.ts b/src/commands/token-create.ts index 35abb8bc..8fdb013f 100644 --- a/src/commands/token-create.ts +++ b/src/commands/token-create.ts @@ -1,3 +1,4 @@ +import { getActiveRepositoryName } from "../adapters"; import { getCredentials } from "../auth"; import { CommandError, createCommand, type CommandConfig } from "../lib/command"; import { stringify } from "../lib/json"; @@ -7,8 +8,6 @@ import { createWriteToken, getOAuthApps, } from "../lib/prismic/clients/wroom"; -import { resolveEnvironment } from "../lib/prismic/environments"; -import { getRepositoryName } from "../project"; const CLI_APP_NAME = "Prismic CLI"; @@ -32,15 +31,20 @@ const config = { description: `Name to identify the token (default: "${CLI_APP_NAME}")`, }, json: { type: "boolean", description: "Output as JSON" }, - repo: { type: "string", short: "r", description: "Repository domain" }, - env: { type: "string", short: "e", description: "Environment domain" }, + repo: { type: "string", short: "r", description: "Repository or environment domain" }, + env: { + type: "string", + short: "e", + description: "Alias for --repo", + deprecated: "Use `prismic env` or --repo instead.", + }, }, } satisfies CommandConfig; export default createCommand(config, async ({ values }) => { const { - repo: parentRepo = await getRepositoryName(), env, + repo = env ?? (await getActiveRepositoryName()), write, "allow-releases": allowReleases, name = CLI_APP_NAME, @@ -52,7 +56,6 @@ export default createCommand(config, async ({ values }) => { } const { token, host } = await getCredentials(); - const repo = env ? await resolveEnvironment(env, { repo: parentRepo, token, host }) : parentRepo; let createdToken: string; let scope: string | undefined; diff --git a/src/commands/token-delete.ts b/src/commands/token-delete.ts index ac81fb91..d0afd931 100644 --- a/src/commands/token-delete.ts +++ b/src/commands/token-delete.ts @@ -1,3 +1,4 @@ +import { getActiveRepositoryName } from "../adapters"; import { getCredentials } from "../auth"; import { CommandError, createCommand, type CommandConfig } from "../lib/command"; import { @@ -6,8 +7,6 @@ import { getOAuthApps, getWriteTokens, } from "../lib/prismic/clients/wroom"; -import { resolveEnvironment } from "../lib/prismic/environments"; -import { getRepositoryName } from "../project"; const config = { name: "prismic token delete", @@ -21,17 +20,21 @@ const config = { token: { description: "Token value", required: true }, }, options: { - repo: { type: "string", short: "r", description: "Repository domain" }, - env: { type: "string", short: "e", description: "Environment domain" }, + repo: { type: "string", short: "r", description: "Repository or environment domain" }, + env: { + type: "string", + short: "e", + description: "Alias for --repo", + deprecated: "Use `prismic env` or --repo instead.", + }, }, } satisfies CommandConfig; export default createCommand(config, async ({ positionals, values }) => { const [tokenValue] = positionals; - const { repo: parentRepo = await getRepositoryName(), env } = values; + const { env, repo = env ?? (await getActiveRepositoryName()) } = values; const { token, host } = await getCredentials(); - const repo = env ? await resolveEnvironment(env, { repo: parentRepo, token, host }) : parentRepo; const [apps, writeTokensInfo] = await Promise.all([ getOAuthApps({ repo, token, host }), diff --git a/src/commands/token-list.ts b/src/commands/token-list.ts index 189bf892..58208b5f 100644 --- a/src/commands/token-list.ts +++ b/src/commands/token-list.ts @@ -1,10 +1,9 @@ +import { getActiveRepositoryName } from "../adapters"; import { getCredentials } from "../auth"; import { createCommand, type CommandConfig } from "../lib/command"; import { stringify } from "../lib/json"; import { getOAuthApps, getWriteTokens } from "../lib/prismic/clients/wroom"; -import { resolveEnvironment } from "../lib/prismic/environments"; import { formatTable } from "../lib/string"; -import { getRepositoryName } from "../project"; const config = { name: "prismic token list", @@ -16,16 +15,20 @@ const config = { `, options: { json: { type: "boolean", description: "Output as JSON" }, - repo: { type: "string", short: "r", description: "Repository domain" }, - env: { type: "string", short: "e", description: "Environment domain" }, + repo: { type: "string", short: "r", description: "Repository or environment domain" }, + env: { + type: "string", + short: "e", + description: "Alias for --repo", + deprecated: "Use `prismic env` or --repo instead.", + }, }, } satisfies CommandConfig; export default createCommand(config, async ({ values }) => { - const { repo: parentRepo = await getRepositoryName(), env, json } = values; + const { env, repo = env ?? (await getActiveRepositoryName()), json } = values; const { token, host } = await getCredentials(); - const repo = env ? await resolveEnvironment(env, { repo: parentRepo, token, host }) : parentRepo; const [apps, writeTokensInfo] = await Promise.all([ getOAuthApps({ repo, token, host }), diff --git a/src/commands/webhook-create.ts b/src/commands/webhook-create.ts index f1c2b3df..6f1d2824 100644 --- a/src/commands/webhook-create.ts +++ b/src/commands/webhook-create.ts @@ -1,8 +1,7 @@ +import { getActiveRepositoryName } from "../adapters"; import { getCredentials } from "../auth"; import { CommandError, createCommand, type CommandConfig } from "../lib/command"; import { createWebhook, WEBHOOK_TRIGGERS } from "../lib/prismic/clients/wroom"; -import { resolveEnvironment } from "../lib/prismic/environments"; -import { getRepositoryName } from "../project"; const config = { name: "prismic webhook create", @@ -24,8 +23,13 @@ const config = { short: "t", description: "Trigger events (can be repeated)", }, - repo: { type: "string", short: "r", description: "Repository domain" }, - env: { type: "string", short: "e", description: "Environment domain" }, + repo: { type: "string", short: "r", description: "Repository or environment domain" }, + env: { + type: "string", + short: "e", + description: "Alias for --repo", + deprecated: "Use `prismic env` or --repo instead.", + }, }, sections: { TRIGGERS: ` @@ -53,7 +57,13 @@ const config = { export default createCommand(config, async ({ positionals, values }) => { const [webhookUrl] = positionals; - const { repo: parentRepo = await getRepositoryName(), env, name, secret, trigger = [] } = values; + const { + env, + repo = env ?? (await getActiveRepositoryName()), + name, + secret, + trigger = [], + } = values; // Validate triggers for (const t of trigger) { @@ -65,7 +75,6 @@ export default createCommand(config, async ({ positionals, values }) => { } const { token, host } = await getCredentials(); - const repo = env ? await resolveEnvironment(env, { repo: parentRepo, token, host }) : parentRepo; const defaultValue = trigger.length > 0 ? false : true; diff --git a/src/commands/webhook-disable.ts b/src/commands/webhook-disable.ts index c546df95..dafd85ce 100644 --- a/src/commands/webhook-disable.ts +++ b/src/commands/webhook-disable.ts @@ -1,8 +1,7 @@ +import { getActiveRepositoryName } from "../adapters"; import { getCredentials } from "../auth"; import { CommandError, createCommand, type CommandConfig } from "../lib/command"; import { getWebhooks, updateWebhook } from "../lib/prismic/clients/wroom"; -import { resolveEnvironment } from "../lib/prismic/environments"; -import { getRepositoryName } from "../project"; const config = { name: "prismic webhook disable", @@ -16,17 +15,21 @@ const config = { url: { description: "Webhook URL", required: true }, }, options: { - repo: { type: "string", short: "r", description: "Repository domain" }, - env: { type: "string", short: "e", description: "Environment domain" }, + repo: { type: "string", short: "r", description: "Repository or environment domain" }, + env: { + type: "string", + short: "e", + description: "Alias for --repo", + deprecated: "Use `prismic env` or --repo instead.", + }, }, } satisfies CommandConfig; export default createCommand(config, async ({ positionals, values }) => { const [webhookUrl] = positionals; - const { repo: parentRepo = await getRepositoryName(), env } = values; + const { env, repo = env ?? (await getActiveRepositoryName()) } = values; const { token, host } = await getCredentials(); - const repo = env ? await resolveEnvironment(env, { repo: parentRepo, token, host }) : parentRepo; const webhooks = await getWebhooks({ repo, token, host }); const webhook = webhooks.find((w) => w.config.url === webhookUrl); diff --git a/src/commands/webhook-enable.ts b/src/commands/webhook-enable.ts index 0943836e..7ad3c433 100644 --- a/src/commands/webhook-enable.ts +++ b/src/commands/webhook-enable.ts @@ -1,8 +1,7 @@ +import { getActiveRepositoryName } from "../adapters"; import { getCredentials } from "../auth"; import { CommandError, createCommand, type CommandConfig } from "../lib/command"; import { getWebhooks, updateWebhook } from "../lib/prismic/clients/wroom"; -import { resolveEnvironment } from "../lib/prismic/environments"; -import { getRepositoryName } from "../project"; const config = { name: "prismic webhook enable", @@ -16,17 +15,21 @@ const config = { url: { description: "Webhook URL", required: true }, }, options: { - repo: { type: "string", short: "r", description: "Repository domain" }, - env: { type: "string", short: "e", description: "Environment domain" }, + repo: { type: "string", short: "r", description: "Repository or environment domain" }, + env: { + type: "string", + short: "e", + description: "Alias for --repo", + deprecated: "Use `prismic env` or --repo instead.", + }, }, } satisfies CommandConfig; export default createCommand(config, async ({ positionals, values }) => { const [webhookUrl] = positionals; - const { repo: parentRepo = await getRepositoryName(), env } = values; + const { env, repo = env ?? (await getActiveRepositoryName()) } = values; const { token, host } = await getCredentials(); - const repo = env ? await resolveEnvironment(env, { repo: parentRepo, token, host }) : parentRepo; const webhooks = await getWebhooks({ repo, token, host }); const webhook = webhooks.find((w) => w.config.url === webhookUrl); diff --git a/src/commands/webhook-list.ts b/src/commands/webhook-list.ts index 980e1df1..8db16fd1 100644 --- a/src/commands/webhook-list.ts +++ b/src/commands/webhook-list.ts @@ -1,10 +1,9 @@ +import { getActiveRepositoryName } from "../adapters"; import { getCredentials } from "../auth"; import { createCommand, type CommandConfig } from "../lib/command"; import { stringify } from "../lib/json"; import { getWebhooks } from "../lib/prismic/clients/wroom"; -import { resolveEnvironment } from "../lib/prismic/environments"; import { formatTable } from "../lib/string"; -import { getRepositoryName } from "../project"; const config = { name: "prismic webhook list", @@ -16,16 +15,20 @@ const config = { `, options: { json: { type: "boolean", description: "Output as JSON" }, - repo: { type: "string", short: "r", description: "Repository domain" }, - env: { type: "string", short: "e", description: "Environment domain" }, + repo: { type: "string", short: "r", description: "Repository or environment domain" }, + env: { + type: "string", + short: "e", + description: "Alias for --repo", + deprecated: "Use `prismic env` or --repo instead.", + }, }, } satisfies CommandConfig; export default createCommand(config, async ({ values }) => { - const { repo: parentRepo = await getRepositoryName(), env, json } = values; + const { env, repo = env ?? (await getActiveRepositoryName()), json } = values; const { token, host } = await getCredentials(); - const repo = env ? await resolveEnvironment(env, { repo: parentRepo, token, host }) : parentRepo; const webhooks = await getWebhooks({ repo, token, host }); if (json) { diff --git a/src/commands/webhook-remove.ts b/src/commands/webhook-remove.ts index e27b632b..06de4d77 100644 --- a/src/commands/webhook-remove.ts +++ b/src/commands/webhook-remove.ts @@ -1,8 +1,7 @@ +import { getActiveRepositoryName } from "../adapters"; import { getCredentials } from "../auth"; import { CommandError, createCommand, type CommandConfig } from "../lib/command"; import { deleteWebhook, getWebhooks } from "../lib/prismic/clients/wroom"; -import { resolveEnvironment } from "../lib/prismic/environments"; -import { getRepositoryName } from "../project"; const config = { name: "prismic webhook remove", @@ -16,17 +15,21 @@ const config = { url: { description: "Webhook URL", required: true }, }, options: { - repo: { type: "string", short: "r", description: "Repository domain" }, - env: { type: "string", short: "e", description: "Environment domain" }, + repo: { type: "string", short: "r", description: "Repository or environment domain" }, + env: { + type: "string", + short: "e", + description: "Alias for --repo", + deprecated: "Use `prismic env` or --repo instead.", + }, }, } satisfies CommandConfig; export default createCommand(config, async ({ positionals, values }) => { const [webhookUrl] = positionals; - const { repo: parentRepo = await getRepositoryName(), env } = values; + const { env, repo = env ?? (await getActiveRepositoryName()) } = values; const { token, host } = await getCredentials(); - const repo = env ? await resolveEnvironment(env, { repo: parentRepo, token, host }) : parentRepo; const webhooks = await getWebhooks({ repo, token, host }); const webhook = webhooks.find((w) => w.config.url === webhookUrl); diff --git a/src/commands/webhook-set-triggers.ts b/src/commands/webhook-set-triggers.ts index ea26d44e..a9f3ab3f 100644 --- a/src/commands/webhook-set-triggers.ts +++ b/src/commands/webhook-set-triggers.ts @@ -1,8 +1,7 @@ +import { getActiveRepositoryName } from "../adapters"; import { getCredentials } from "../auth"; import { CommandError, createCommand, type CommandConfig } from "../lib/command"; import { getWebhooks, updateWebhook, WEBHOOK_TRIGGERS } from "../lib/prismic/clients/wroom"; -import { resolveEnvironment } from "../lib/prismic/environments"; -import { getRepositoryName } from "../project"; const config = { name: "prismic webhook set-triggers", @@ -23,8 +22,13 @@ const config = { description: "Trigger events (can be repeated)", required: true, }, - repo: { type: "string", short: "r", description: "Repository domain" }, - env: { type: "string", short: "e", description: "Environment domain" }, + repo: { type: "string", short: "r", description: "Repository or environment domain" }, + env: { + type: "string", + short: "e", + description: "Alias for --repo", + deprecated: "Use `prismic env` or --repo instead.", + }, }, sections: { TRIGGERS: ` @@ -40,7 +44,7 @@ const config = { export default createCommand(config, async ({ positionals, values }) => { const [webhookUrl] = positionals; - const { repo: parentRepo = await getRepositoryName(), env, trigger = [] } = values; + const { env, repo = env ?? (await getActiveRepositoryName()), trigger = [] } = values; // Validate triggers for (const t of trigger) { @@ -52,7 +56,6 @@ export default createCommand(config, async ({ positionals, values }) => { } const { token, host } = await getCredentials(); - const repo = env ? await resolveEnvironment(env, { repo: parentRepo, token, host }) : parentRepo; const webhooks = await getWebhooks({ repo, token, host }); const webhook = webhooks.find((w) => w.config.url === webhookUrl); diff --git a/src/commands/webhook-view.ts b/src/commands/webhook-view.ts index acd360df..b61431cf 100644 --- a/src/commands/webhook-view.ts +++ b/src/commands/webhook-view.ts @@ -1,8 +1,7 @@ +import { getActiveRepositoryName } from "../adapters"; import { getCredentials } from "../auth"; import { CommandError, createCommand, type CommandConfig } from "../lib/command"; import { getWebhooks, WEBHOOK_TRIGGERS } from "../lib/prismic/clients/wroom"; -import { resolveEnvironment } from "../lib/prismic/environments"; -import { getRepositoryName } from "../project"; const config = { name: "prismic webhook view", @@ -16,17 +15,21 @@ const config = { url: { description: "Webhook URL", required: true }, }, options: { - repo: { type: "string", short: "r", description: "Repository domain" }, - env: { type: "string", short: "e", description: "Environment domain" }, + repo: { type: "string", short: "r", description: "Repository or environment domain" }, + env: { + type: "string", + short: "e", + description: "Alias for --repo", + deprecated: "Use `prismic env` or --repo instead.", + }, }, } satisfies CommandConfig; export default createCommand(config, async ({ positionals, values }) => { const [webhookUrl] = positionals; - const { repo: parentRepo = await getRepositoryName(), env } = values; + const { env, repo = env ?? (await getActiveRepositoryName()) } = values; const { token, host } = await getCredentials(); - const repo = env ? await resolveEnvironment(env, { repo: parentRepo, token, host }) : parentRepo; const webhooks = await getWebhooks({ repo, token, host }); const webhook = webhooks.find((webhook) => webhook.config.url === webhookUrl); diff --git a/src/lib/command.ts b/src/lib/command.ts index cb513237..a6f8afd6 100644 --- a/src/lib/command.ts +++ b/src/lib/command.ts @@ -15,6 +15,7 @@ export type CommandConfig = { description: string; required?: boolean; dependsOn?: string | string[]; + deprecated?: string; } >; }; @@ -84,6 +85,9 @@ export function createCommand( if (config.required && !(name in result.values)) { throw new CommandError(`Missing required option: --${name}`); } + if (config.deprecated && name in result.values) { + console.warn(`--${name} is deprecated. ${config.deprecated}`); + } if (config.dependsOn && name in result.values) { const deps = Array.isArray(config.dependsOn) ? config.dependsOn : [config.dependsOn]; for (const dep of deps) { @@ -133,6 +137,7 @@ function buildCommandHelp(config: CommandConfig): string { const optionNames = Object.keys(options); for (const optionName of optionNames) { const option = options[optionName]; + if (option.deprecated) continue; const shortPart = option.short ? `-${option.short}, ` : " "; const typeSuffix = option.type === "string" ? " string" : ""; const left = `${shortPart}--${optionName}${typeSuffix}`; diff --git a/src/lib/prismic/environments.ts b/src/lib/prismic/environments.ts index 32d85d14..d22728d0 100644 --- a/src/lib/prismic/environments.ts +++ b/src/lib/prismic/environments.ts @@ -20,28 +20,6 @@ export async function getUserEnvironments(config: { return userEnvironments; } -export async function resolveEnvironment( - env: string, - config: { repo: string; token: string | undefined; host: string }, -): Promise { - const { repo, token, host } = config; - - const [profile, environments] = await Promise.all([ - getProfile({ token, host }), - getEnvironments({ repo, token, host }), - ]); - - const availableEnvironments = environments.filter( - (environment) => - (environment.kind === "prod" || environment.kind === "stage") && - environment.users.some((user) => user.id === profile.shortId), - ); - const match = availableEnvironments.find((environment) => environment.domain === env); - if (match) return match.domain; - - throw new InvalidEnvironmentError(env, availableEnvironments, repo); -} - export class InvalidEnvironmentError extends Error { name = "InvalidEnvironmentError"; repo: string; diff --git a/test/pull.test.ts b/test/pull.test.ts index a8d446b3..a306739f 100644 --- a/test/pull.test.ts +++ b/test/pull.test.ts @@ -20,12 +20,6 @@ it.sequential("supports --help", async ({ expect, prismic }) => { expect(stdout).toContain("prismic pull [options]"); }); -it.sequential("rejects an unknown --env", async ({ expect, prismic, repo }) => { - const { stderr, exitCode } = await prismic("pull", ["--repo", repo, "--env", "does-not-exist"]); - expect(exitCode).toBe(1); - expect(stderr).toContain(`No environments available on repository "${repo}".`); -}); - it.sequential("pulls slices and custom types from remote", async ({ expect, project, diff --git a/test/push.test.ts b/test/push.test.ts index 72893ea3..44eac5c2 100644 --- a/test/push.test.ts +++ b/test/push.test.ts @@ -16,12 +16,6 @@ it("supports --help", async ({ expect, prismic }) => { expect(stdout).toContain("prismic push [options]"); }); -it("rejects an unknown --env", async ({ expect, prismic, repo }) => { - const { stderr, exitCode } = await prismic("push", ["--repo", repo, "--env", "does-not-exist"]); - expect(exitCode).toBe(1); - expect(stderr).toContain(`No environments available on repository "${repo}".`); -}); - describe("with an isolated repository", () => { it.scoped({ isolateRepo: true }); diff --git a/test/status.test.ts b/test/status.test.ts index cf243f98..df551b77 100644 --- a/test/status.test.ts +++ b/test/status.test.ts @@ -9,20 +9,6 @@ it("supports --help", async ({ expect, prismic }) => { expect(stdout).toContain("prismic status [options]"); }); -it("rejects an unknown --env", async ({ expect, prismic, repo }) => { - const { stderr, exitCode } = await prismic("status", ["--repo", repo, "--env", "does-not-exist"]); - expect(exitCode).toBe(1); - expect(stderr).toContain(`No environments available on repository "${repo}".`); -}); - -it("warns and skips --env when not logged in", async ({ expect, prismic, logout, repo }) => { - await logout(); - const { stdout, stderr, exitCode } = await prismic("status", ["--repo", repo, "--env", "anything"]); - expect(exitCode, stderr).toBe(0); - expect(stdout).toContain(`Repository: ${repo}`); - expect(stdout).toContain("Environment: anything"); -}); - describe("with an isolated repository", () => { it.scoped({ isolateRepo: true }); From fdb2078b8d2fd23941617d1ecb7332e79a6b7dbe Mon Sep 17 00:00:00 2001 From: Angelo Ashmore Date: Fri, 24 Jul 2026 21:22:58 +0000 Subject: [PATCH 7/7] fix: fall back to config repository name when environment variable is empty Co-Authored-By: Claude Fable 5 --- src/adapters/nextjs.templates.ts | 4 ++-- src/adapters/sveltekit.templates.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/adapters/nextjs.templates.ts b/src/adapters/nextjs.templates.ts index 3cac94b1..1dd4d08b 100644 --- a/src/adapters/nextjs.templates.ts +++ b/src/adapters/nextjs.templates.ts @@ -358,7 +358,7 @@ export function prismicIOFileTemplate(args: { * The project's Prismic repository name. */ export const repositoryName = - process.env.NEXT_PUBLIC_PRISMIC_ENVIRONMENT ?? prismicConfig.repositoryName; + process.env.NEXT_PUBLIC_PRISMIC_ENVIRONMENT || prismicConfig.repositoryName; ${createClientContents} `; @@ -371,7 +371,7 @@ export function prismicIOFileTemplate(args: { * The project's Prismic repository name. */ export const repositoryName = - process.env.NEXT_PUBLIC_PRISMIC_ENVIRONMENT ?? prismicConfig.repositoryName; + process.env.NEXT_PUBLIC_PRISMIC_ENVIRONMENT || prismicConfig.repositoryName; ${createClientContents} `; diff --git a/src/adapters/sveltekit.templates.ts b/src/adapters/sveltekit.templates.ts index 32699181..28196e92 100644 --- a/src/adapters/sveltekit.templates.ts +++ b/src/adapters/sveltekit.templates.ts @@ -18,7 +18,7 @@ export function prismicIOFileTemplate(args: { typescript: boolean }): string { * The project's Prismic repository name. */ export const repositoryName = - env.PUBLIC_PRISMIC_ENVIRONMENT ?? prismicConfig.repositoryName; + env.PUBLIC_PRISMIC_ENVIRONMENT || prismicConfig.repositoryName; /** * Creates a Prismic client for the project's repository. The client is used to @@ -49,7 +49,7 @@ export function prismicIOFileTemplate(args: { typescript: boolean }): string { * The project's Prismic repository name. */ export const repositoryName = - env.PUBLIC_PRISMIC_ENVIRONMENT ?? prismicConfig.repositoryName; + env.PUBLIC_PRISMIC_ENVIRONMENT || prismicConfig.repositoryName; /** * Creates a Prismic client for the project's repository. The client is used to