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
16 changes: 9 additions & 7 deletions src/commands/cloud/connect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -327,13 +327,15 @@ export async function connectTurso(
}
}

const TRIGGERDEV_PROJECT_REF_HINT =
'The project ref is the `project` line in trigger.config.ts and starts with proj_.';
const TRIGGERDEV_PROJECT_REF_WHERE =
"Paste the project ref from the project's settings page (it starts with proj_). It is also the `project` line in trigger.config.ts.";

const TRIGGERDEV_PROJECT_REF_HINT = `This key cannot name its project. ${TRIGGERDEV_PROJECT_REF_WHERE}`;

const TRIGGERDEV_HEADLESS_HINT =
'Create an environment API key in your Trigger.dev project (production environment > API Keys, No restrictions access preset), then re-run:\n' +
'Create an environment API key in your Trigger.dev project (production environment > API Keys, "No restrictions" access preset), then re-run:\n' +
'polylane cloud connect --provider triggerdev --api-key <key>\n' +
`Add --project-ref <proj_...> when the API asks for it. ${TRIGGERDEV_PROJECT_REF_HINT}`;
`Add --project-ref <proj_...> when the API answers that the key cannot name its project. ${TRIGGERDEV_PROJECT_REF_WHERE}`;

// The generated client trails the deployed API spec; the triggerdev body
// shape is the contract from the API-side design record.
Expand Down Expand Up @@ -376,8 +378,8 @@ export async function connectTriggerdev(
note(`${err.message}\n${TRIGGERDEV_PROJECT_REF_HINT}`, 'Trigger.dev project ref');
const picked = await promptTextOrBack(
{ nonInteractive: config.nonInteractive },
'Trigger.dev project ref (proj_...)',
{ validate: (v: string) => (v.trim() ? undefined : 'Required') }
'Project ref',
{ placeholder: 'proj_…', validate: (v: string) => (v.trim() ? undefined : 'Required') }
);
if (picked === BACK) return BACK;
return send({ ...body, projectRef: picked.trim() });
Expand Down Expand Up @@ -588,7 +590,7 @@ async function connectProvider(
{
message: 'Trigger.dev environment API key',
instructions:
'In your Trigger.dev project, open the production environment, then API Keys, and create a key with the No restrictions access preset. That preset is the only kind on the Free and Hobby plans; on Pro you may instead use restricted keys such as Observer plus Deploy only, adding them one at a time. Re-running this command with another key adds it to the same account.',
'In your production environment open API Keys and create a key with the "No restrictions" access preset. That preset is the only kind on the Free and Hobby plans; on Pro you may instead use restricted keys such as "Observer" plus "Deploy only", adding them one at a time. Re-running this command with another key adds it to the same account.',
link: 'https://cloud.trigger.dev',
linkLabel: 'Open Trigger.dev',
},
Expand Down
14 changes: 7 additions & 7 deletions test/cloud-connect-triggerdev.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ import type { PolylaneAPI } from '../src/generated/client';

const config = { nonInteractive: true } as Config;
const body = { workspaceId: 'ws_1', provider: 'triggerdev', apiKey: 'tr_key' } as const;
const REF_REQUIRED =
'This API key is valid for multiple projects. Specify the project ref of the project to connect.';
// The API's `detail` strings, verbatim from nominal
// apps/apis/api-cloud-accounts/src/routers/cloud-accounts/connects/triggerdev.ts.
const REF_REQUIRED = 'The Trigger.dev project ref is required for a restricted key';
const RUNS_REQUIRED = 'This Trigger.dev API key cannot read runs';

function mockApi(connect: (body: unknown) => Promise<unknown>): PolylaneAPI {
return { cloudAccountsConnect: connect } as unknown as PolylaneAPI;
Expand Down Expand Up @@ -50,6 +52,8 @@ describe('connectTriggerdev', () => {
err.exitCode === ExitCode.USAGE &&
err.message.includes('project ref') &&
(err.hint?.includes('--project-ref') ?? false) &&
(err.hint?.includes('This key cannot name its project.') ?? false) &&
(err.hint?.includes("project's settings page") ?? false) &&
(err.hint?.includes('trigger.config.ts') ?? false)
);
});
Expand All @@ -66,11 +70,7 @@ describe('connectTriggerdev', () => {
});

it('rethrows other 400s untouched, including a key that cannot read runs', async () => {
const original = new ApiError(
400,
'This API key cannot read runs. Create a key with the No restrictions access preset, or a restricted key that includes run read access.',
ExitCode.USAGE
);
const original = new ApiError(400, RUNS_REQUIRED, ExitCode.USAGE);
const api = mockApi(async () => {
throw original;
});
Expand Down
Loading