Skip to content
Open
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
55 changes: 50 additions & 5 deletions packages/targets/deploy-vercel/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { defineTarget, manualSetup } from '@profullstack/sh1pt-core';
import { defineTarget, manualSetup, exec } from '@profullstack/sh1pt-core';

interface Config {
project?: string;
Expand All @@ -10,15 +10,60 @@ export default defineTarget<Config>({
id: 'deploy-vercel',
kind: 'web',
label: 'Vercel',
async build(ctx) {
ctx.log('vercel build');
async build(ctx, config) {
const prod = config.prod ?? ctx.channel === 'stable';
ctx.log(`vercel build${prod ? ' --prod' : ''}`);

const token = ctx.secret('VERCEL_TOKEN');
if (!token) throw new Error('VERCEL_TOKEN secret not set. Run: sh1pt secret set VERCEL_TOKEN <token>');

const args: string[] = ['build'];
if (prod) args.push('--prod');

await exec('vercel', args, {
cwd: ctx.projectDir,
log: ctx.log,
throwOnNonZero: true,
env: { VERCEL_TOKEN: token },
});

return { artifact: `${ctx.outDir}/vercel-output` };
},
async ship(ctx, config) {
const prod = config.prod ?? ctx.channel === 'stable';
ctx.log(`vercel deploy ${prod ? '--prod' : ''} · project=${config.project ?? 'linked'}`);
ctx.log(`vercel deploy${prod ? ' --prod' : ''} · project=${config.project ?? 'linked'}`);
if (ctx.dryRun) return { id: 'dry-run' };
return { id: `${config.project ?? 'vercel'}@${ctx.version}`, url: undefined };

const token = ctx.secret('VERCEL_TOKEN');
if (!token) {
throw new Error(
'VERCEL_TOKEN is required for deployment. ' +
'Create a token at vercel.com/account/tokens, ' +
'then set it: sh1pt secret set VERCEL_TOKEN <token>'
);
}

const args: string[] = ['deploy', '--token', token];
if (prod) args.push('--prod');

const { stdout } = await exec('vercel', args, {
cwd: ctx.projectDir,
log: ctx.log,
throwOnNonZero: true,
env: { VERCEL_TOKEN: token },
});

// Vercel prints the deployment URL as the last line of stdout
// e.g. "https://my-project-abc123.vercel.app"
const lines = stdout.trim().split('\n').filter(Boolean);
const urlMatch = stdout.match(/https:\/\/[a-zA-Z0-9.-]+\.vercel\.app/);
const url = urlMatch?.[0] ?? lines[lines.length - 1]?.trim();

const projectLabel = config.project ?? 'vercel';
const id = `${projectLabel}@${ctx.version}`;

ctx.log(`vercel deploy complete · id=${id} · url=${url}`);
return { id, url };
},
setup: manualSetup({
label: 'Vercel CLI',
Expand Down