Skip to content

Commit

Permalink
feat(root): add script to manage vercel deployments
Browse files Browse the repository at this point in the history
  • Loading branch information
neuodev committed Aug 3, 2024
1 parent a85b558 commit bddeef5
Show file tree
Hide file tree
Showing 3 changed files with 159 additions and 1 deletion.
8 changes: 7 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
"install:clean": "yarn workspaces foreach -A run rimraf node_modules && yarn install",
"build:orbit": "yarn workspace @litespace/orbit build",
"build:types": "yarn workspace @litespace/types build",
"build:atlas": "yarn workspace @litespace/atlas build"
"build:atlas": "yarn workspace @litespace/atlas build",
"vercel": "env-cmd ts-node scripts/vercel.ts"
},
"workspaces": {
"packages": [
Expand All @@ -24,7 +25,12 @@
},
"packageManager": "[email protected]",
"devDependencies": {
"env-cmd": "^10.1.0",
"rimraf": "^5.0.7",
"ts-node": "^10.9.2",
"vercel": "^34.2.7"
},
"dependencies": {
"commander": "^12.1.0"
}
}
142 changes: 142 additions & 0 deletions scripts/vercel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import axios from "axios";
import { Command } from "commander";
import fs from "node:fs";
import path from "node:path";

type Project = {
id: string;
name: string;
accountId: string;
createdAt: string;
framework: string;
devCommand: string | null;
installCommand: string | null;
buildCommand: string | null;
outputDirectory: string | null;
rootDirectory: string | null;
directoryListing: string | null;
nodeVersion: string | null;
};

async function safe<T>(callback: () => Promise<T>): Promise<T | Error> {
try {
return await callback();
} catch (error: unknown) {
if (axios.isAxiosError(error)) {
const message = error.response?.data?.error?.message || error.message;
return new Error(message);
}
if (error instanceof Error) return error;
throw new Error("Unkown error type");
}
}

const client = axios.create({
baseURL: "https://api.vercel.com",
headers: { Authorization: `Bearer ${process.env.VERCEL_TOKEN}` },
});

async function findProject(name: string): Promise<Project> {
return await client
.get<Project>(`/v9/projects/${name}`)
.then((response) => response.data);
}

async function createProject({
name,
buildCommand = "yarn build",
outputDirectory = "dist",
installCommand = "yarn",
}: {
name: string;
buildCommand?: string;
outputDirectory?: string;
installCommand?: string;
}): Promise<Project> {
const { data } = await client.post<Project>("/v10/projects", {
name,
buildCommand,
installCommand,
outputDirectory,
framework: "vite",
});

return data;
}

function asVercelDirectory(workspace: string) {
return path.join(workspace, ".vercel");
}

function saveProject(vercel: string, project: Project) {
const data = {
projectId: project.id,
orgId: project.accountId,
settings: {
createdAt: project.createdAt,
framework: project.framework,
devCommand: project.devCommand,
installCommand: project.installCommand,
buildCommand: project.buildCommand,
outputDirectory: project.outputDirectory,
rootDirectory: project.rootDirectory,
directoryListing: project.directoryListing,
nodeVersion: project.nodeVersion,
},
} as const;

const file = path.join(vercel, "project.json");
fs.writeFileSync(file, JSON.stringify(data, null, 2));
}

const create = new Command()
.name("create")
.argument("<name>", "Project name")
.option("-b, --build <build-command>", "Project build command")
.option("-i, --install <install-command>", "Project install command")
.option("-o, --output <output-directory>", "Project output directory")
.action(
async (
name: string,
options: { build?: string; install?: string; output?: string }
) => {
const project = await safe(() =>
createProject({
name,
buildCommand: options.build,
installCommand: options.install,
outputDirectory: options.output,
})
);

if (project instanceof Error) throw project;
console.log(`Project (${project.name}) created successfully`);
}
);

const pull = new Command()
.name("pull")
.argument("<name>", "Project name")
.argument("<path>", "Where to save project info after pulling it from vercel")
.action(async (name: string, path: string) => {
if (!fs.existsSync(path)) throw new Error(`"${path}" not found`);

const vercel = asVercelDirectory(path);
if (fs.existsSync(vercel))
fs.rmSync(vercel, { recursive: true, force: true });

fs.mkdirSync(vercel);

const project = await safe(() => findProject(name));
if (project instanceof Error) throw project;

saveProject(vercel, project);
});

new Command()
.name("vercel")
.description("Manage Vercel deployments.")
.version("1.0.0")
.addCommand(create)
.addCommand(pull)
.parse();
10 changes: 10 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -11389,6 +11389,13 @@ __metadata:
languageName: node
linkType: hard

"commander@npm:^12.1.0":
version: 12.1.0
resolution: "commander@npm:12.1.0"
checksum: 10c0/6e1996680c083b3b897bfc1cfe1c58dfbcd9842fd43e1aaf8a795fbc237f65efcc860a3ef457b318e73f29a4f4a28f6403c3d653d021d960e4632dd45bde54a9
languageName: node
linkType: hard

"commander@npm:^2.20.0":
version: 2.20.3
resolution: "commander@npm:2.20.3"
Expand Down Expand Up @@ -16601,7 +16608,10 @@ __metadata:
version: 0.0.0-use.local
resolution: "litespace@workspace:."
dependencies:
commander: "npm:^12.1.0"
env-cmd: "npm:^10.1.0"
rimraf: "npm:^5.0.7"
ts-node: "npm:^10.9.2"
vercel: "npm:^34.2.7"
languageName: unknown
linkType: soft
Expand Down

0 comments on commit bddeef5

Please sign in to comment.