Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/commands/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { findProjectsBySlug } from "../lib/api/projects.js";
import { looksLikePath, parseOrgProjectArg } from "../lib/arg-parsing.js";
import { buildCommand } from "../lib/command.js";
import { ContextError, ValidationError } from "../lib/errors.js";
import { warmOrgDetection } from "../lib/init/prefetch.js";
import { warmOrgDetection } from "../lib/init/org-prefetch.js";
import { runWizard } from "../lib/init/wizard-runner.js";
import { validateResourceId } from "../lib/input-validation.js";
import { logger } from "../lib/logger.js";
Expand Down Expand Up @@ -104,7 +104,7 @@ function classifyArgs(
*
* For `project-search` (bare slug), searches for an existing project first.
* If not found, treats the slug as a **new project name** to create —
* org will be resolved later by the wizard's `resolveOrgSlug()`.
* org will be resolved later by init preflight before the workflow starts.
* If the slug matches an org name, treats it as org-only (like `slug/`).
*/
async function resolveTarget(targetArg: string | undefined): Promise<{
Expand Down Expand Up @@ -154,7 +154,7 @@ async function resolveTarget(targetArg: string | undefined): Promise<{
}

// Truly not found — treat as the name for a new project to create.
// Org will be resolved later by the wizard via resolveOrgSlug().
// Org will be resolved later by init preflight before the workflow starts.
log.info(
`No existing project "${parsed.projectSlug}" found — will create a new project with this name.`
);
Expand Down
4 changes: 2 additions & 2 deletions src/commands/project/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ import {
import { resolveOrg } from "../../lib/resolve-target.js";
import {
buildOrgNotFoundError,
type ResolvedTeam,
type ResolvedConcreteTeam,
resolveOrCreateTeam,
} from "../../lib/resolve-team.js";
import { slugify } from "../../lib/utils.js";
Expand Down Expand Up @@ -380,7 +380,7 @@ export const createCommand = buildCommand({
const orgSlug = resolved.org;

// Resolve team — auto-creates a team if the org has none
const team: ResolvedTeam = await resolveOrCreateTeam(orgSlug, {
const team: ResolvedConcreteTeam = await resolveOrCreateTeam(orgSlug, {
team: flags.team,
detectedFrom: resolved.detectedFrom,
usageHint: USAGE_HINT,
Expand Down
33 changes: 33 additions & 0 deletions src/lib/init/existing-project.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { getProject, tryGetPrimaryDsn } from "../api-client.js";
import { ApiError } from "../errors.js";
import { buildProjectUrl } from "../sentry-urls.js";
import type { ExistingProjectData } from "./types.js";

/**
* Fetch Sentry metadata for an existing project.
*
* Returns `null` when the project does not exist, while allowing other API
* errors to propagate so callers can decide whether the lookup is best-effort
* or should fail the current operation.
*/
export async function tryGetExistingProjectData(
orgSlug: string,
projectSlug: string
): Promise<ExistingProjectData | null> {
try {
const project = await getProject(orgSlug, projectSlug);
const dsn = await tryGetPrimaryDsn(orgSlug, project.slug);
return {
orgSlug,
projectSlug: project.slug,
projectId: project.id,
dsn: dsn ?? "",
url: buildProjectUrl(orgSlug, project.slug),
};
} catch (error) {
if (error instanceof ApiError && error.status === 404) {
return null;
}
throw error;
}
}
10 changes: 5 additions & 5 deletions src/lib/init/interactive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@ import {
import { REQUIRED_FEATURE } from "./constants.js";
import type {
ConfirmPayload,
InteractiveContext,
InteractivePayload,
MultiSelectPayload,
SelectPayload,
WizardOptions,
} from "./types.js";

export async function handleInteractive(
payload: InteractivePayload,
options: WizardOptions
options: InteractiveContext
): Promise<Record<string, unknown>> {
switch (payload.kind) {
case "select":
Expand All @@ -41,7 +41,7 @@ export async function handleInteractive(

async function handleSelect(
payload: SelectPayload,
options: WizardOptions
options: InteractiveContext
): Promise<Record<string, unknown>> {
const apps = payload.apps ?? [];
const items = payload.options ?? apps.map((a) => a.name);
Expand Down Expand Up @@ -78,7 +78,7 @@ async function handleSelect(

async function handleMultiSelect(
payload: MultiSelectPayload,
options: WizardOptions
options: InteractiveContext
): Promise<Record<string, unknown>> {
const available = payload.availableFeatures ?? payload.options ?? [];

Expand Down Expand Up @@ -137,7 +137,7 @@ async function handleMultiSelect(

async function handleConfirm(
payload: ConfirmPayload,
options: WizardOptions
options: InteractiveContext
): Promise<Record<string, unknown>> {
if (options.yes) {
log.info("Auto-confirmed: continuing");
Expand Down
Loading
Loading