Skip to content

Commit 87fb55a

Browse files
feat: add deployment commands (#25)
1 parent 887364d commit 87fb55a

File tree

12 files changed

+780
-181
lines changed

12 files changed

+780
-181
lines changed

.assets/pspace.json

Lines changed: 365 additions & 6 deletions
Large diffs are not rendered by default.

api/deployments.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { client } from "./client.ts";
22

33
export const deployments = {
44
list: client("/deployments").get,
5-
create: client("/deployments").post,
65
get: client("/deployments/{id}").get,
76
upsert: client("/deployments").post,
87
delete: client("/deployments/{id}").delete,

commands/deployment/delete/mod.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { args, command, z } from "../../../zcli.ts";
2+
import { asserts } from "../../../lib/asserts.ts";
3+
import { select } from "../../../prompts/select.ts";
4+
import { dataTable } from "../../../lib/data-table.ts";
5+
import { pickJson } from "../../../lib/pick-json.ts";
6+
import { loading } from "../../../lib/loading.ts";
7+
import { deployments } from "../../../api/deployments.ts";
8+
/**
9+
* This variable is automatically generated by `zcli add`. Do not remove this
10+
* or change its name unless you're no longer using `zcli add`.
11+
*/
12+
const subCommands: ReturnType<typeof command>[] = [];
13+
14+
export const delete_ = command("delete", {
15+
short: "Delete a deployment.",
16+
long: ({ root }) => `
17+
Delete a deployment by its ID. If you don't provide an ID, this command will
18+
prompt you for one based on the deployments you have access to.
19+
20+
\`\`\`
21+
${root.name} deployment delete 123e4567-e89b-12d3-a456-426614174000
22+
\`\`\`
23+
`,
24+
commands: subCommands,
25+
args: args().tuple([z.string().describe("The deployment ID to delete.")])
26+
.optional(),
27+
// We use command metadata in the `persistentPreRun` function to check if a
28+
// command requires an API key. If it does, we'll check to see if one is
29+
// set. If not, we'll throw an error.
30+
meta: {
31+
requireApiKey: true,
32+
},
33+
}).run(async function* ({ args, flags }) {
34+
let [id] = args;
35+
36+
if (!id) {
37+
const existingProjects = await loading(deployments.list({ limit: 50 }));
38+
asserts(existingProjects.ok, existingProjects);
39+
40+
const selected = await select(
41+
"Select a deployment:",
42+
existingProjects.data.items,
43+
{
44+
filter(input, option) {
45+
return option.name.toLowerCase().startsWith(input);
46+
},
47+
renderOption(option, isSelected) {
48+
return `${isSelected ? ">" : " "} ${option.name}`;
49+
},
50+
},
51+
);
52+
53+
asserts(selected, "No deployment selected.");
54+
id = selected.id;
55+
}
56+
57+
const result = await loading(deployments.delete({ id }), {
58+
enabled: !flags.json,
59+
});
60+
61+
asserts(result.ok, result);
62+
63+
if (!flags.json) {
64+
for await (const line of dataTable([result.data])) {
65+
yield line;
66+
}
67+
} else {
68+
yield pickJson(result.data);
69+
}
70+
});

commands/deployment/get/mod.ts

Lines changed: 66 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
import { command } from "../../../zcli.ts";
1+
import { args, command, flag, flags, z } from "../../../zcli.ts";
2+
import { asserts } from "../../../lib/asserts.ts";
3+
import { select } from "../../../prompts/select.ts";
4+
import { dataTable } from "../../../lib/data-table.ts";
5+
import { pickJson } from "../../../lib/pick-json.ts";
6+
import { loading } from "../../../lib/loading.ts";
7+
import { deployments } from "../../../api/deployments.ts";
28

39
/**
410
* This variable is automatically generated by `zcli add`. Do not remove this
@@ -7,16 +13,70 @@ import { command } from "../../../zcli.ts";
713
const subCommands: ReturnType<typeof command>[] = [];
814

915
export const get = command("get", {
10-
short: "",
16+
short: "Get a deployment.",
17+
long: ({ root }) => `
18+
Get a deployment by its ID. If you don't provide an ID, this command will
19+
prompt you for one based on the deployments you have access to.
20+
21+
\`\`\`
22+
${root.name} deployment get 123e4567-e89b-12d3-a456-426614174000
23+
\`\`\`
24+
25+
Pick a subset of fields to display:
26+
\`\`\`
27+
${root.name} deployment get 123e4567-e89b-12d3-a456-426614174000 -F name
28+
\`\`\`
29+
`,
1130
commands: subCommands,
31+
args: args().tuple([z.string().describe("The deployment ID to get.")])
32+
.optional(),
33+
flags: flags({
34+
fields: flag({
35+
short: "The fields to include in the response.",
36+
aliases: ["F"],
37+
}).array(z.string()).optional(),
38+
}),
1239
// We use command metadata in the `persistentPreRun` function to check if a
1340
// command requires an API key. If it does, we'll check to see if one is
1441
// set. If not, we'll throw an error.
1542
meta: {
1643
requireApiKey: true,
1744
},
18-
}).run(({ args, flags, ctx }) => {
19-
console.log("Arguments:", args);
20-
console.log("Flags:", flags);
21-
console.log("Context:", ctx);
45+
}).run(async function* ({ args, flags }) {
46+
let [id] = args;
47+
48+
if (!id) {
49+
const existingProjects = await loading(deployments.list({ limit: 50 }));
50+
asserts(existingProjects.ok, existingProjects);
51+
52+
const selected = await select(
53+
"Select a deployment:",
54+
existingProjects.data.items,
55+
{
56+
filter(input, option) {
57+
return option.name.toLowerCase().startsWith(input);
58+
},
59+
renderOption(option, isSelected) {
60+
return `${isSelected ? ">" : " "} ${option.name}`;
61+
},
62+
},
63+
);
64+
65+
asserts(selected, "No deployment selected.");
66+
id = selected.id;
67+
}
68+
69+
const result = await loading(deployments.get({ id }), {
70+
enabled: !flags.json,
71+
});
72+
73+
asserts(result.ok, result);
74+
75+
if (!flags.json) {
76+
for await (const line of dataTable([result.data], flags.fields)) {
77+
yield line;
78+
}
79+
} else {
80+
yield pickJson(result.data, flags.fields);
81+
}
2282
});

commands/deployment/list/mod.ts

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1+
import { deployments } from "../../../api/deployments.ts";
2+
import { loading } from "../../../lib/loading.ts";
13
import { command } from "../../../zcli.ts";
4+
import * as psFlags from "../../../flags.ts";
5+
import { asserts } from "../../../lib/asserts.ts";
6+
import { dataTable } from "../../../lib/data-table.ts";
7+
import { pickJson } from "../../../lib/pick-json.ts";
28

39
/**
410
* This variable is automatically generated by `zcli add`. Do not remove this
@@ -7,16 +13,41 @@ import { command } from "../../../zcli.ts";
713
const subCommands: ReturnType<typeof command>[] = [];
814

915
export const list = command("list", {
10-
short: "",
16+
short: "List deployments.",
17+
long: ({ root }) => `
18+
List deployments in your team.
19+
20+
Examples:
21+
22+
Pick a subset of fields to display:
23+
\`\`\`
24+
${root.name} deployment list -F id -F name
25+
\`\`\`
26+
`,
1127
commands: subCommands,
28+
flags: psFlags.paginator,
1229
// We use command metadata in the `persistentPreRun` function to check if a
1330
// command requires an API key. If it does, we'll check to see if one is
1431
// set. If not, we'll throw an error.
1532
meta: {
1633
requireApiKey: true,
1734
},
18-
}).run(({ args, flags, ctx }) => {
19-
console.log("Arguments:", args);
20-
console.log("Flags:", flags);
21-
console.log("Context:", ctx);
35+
}).run(async function* ({ flags }) {
36+
const result = await loading(
37+
deployments.list({
38+
...flags,
39+
order: flags.asc ? "asc" : undefined,
40+
}),
41+
{ enabled: !flags.json },
42+
);
43+
44+
asserts(result.ok, result);
45+
46+
if (!flags.json) {
47+
for await (const line of dataTable(result.data.items, flags.fields)) {
48+
yield line;
49+
}
50+
} else {
51+
yield pickJson(result.data, flags.fields);
52+
}
2253
});

commands/deployment/mod.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
11
import { command } from "../../zcli.ts";
22
import { get } from "./get/mod.ts";
33
import { list } from "./list/mod.ts";
4+
import { delete_ } from "./delete/mod.ts";
5+
import { up } from "./up/mod.ts";
46

57
/**
68
* This variable is automatically generated by `zcli add`. Do not remove this
79
* or change its name unless you're no longer using `zcli add`.
810
*/
9-
const subCommands: ReturnType<typeof command>[] = [get, list];
11+
const subCommands: ReturnType<typeof command>[] = [get, list, delete_, up];
1012

1113
export const deployment = command("deployment", {
1214
short: "Effortlessly deploy ML apps to Paperspace.",
1315
commands: subCommands,
14-
}).run(({ args, flags, ctx }) => {
15-
console.log("Arguments:", args);
16-
console.log("Flags:", flags);
17-
console.log("Context:", ctx);
16+
}).run(function* ({ ctx }) {
17+
for (const line of deployment.help(ctx)) {
18+
yield line;
19+
}
1820
});

commands/deployment/up/mod.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { command } from "../../../zcli.ts";
2+
import { runUp, upFlags } from "../../up/mod.ts";
3+
4+
/**
5+
* This variable is automatically generated by `zcli add`. Do not remove this
6+
* or change its name unless you're no longer using `zcli add`.
7+
*/
8+
const subCommands: ReturnType<typeof command>[] = [];
9+
10+
export const up = command("up", {
11+
short: "Create or update a deployment",
12+
long: ({ root }) => `
13+
This will create a new deployment for your app or update it if it already exists.
14+
You can optionally specify a path to a config file. If no config file is specified,
15+
the default config file paths will be tried.
16+
17+
Create a new deployment for the app in the current directory.
18+
\`\`\`
19+
$ ${root.name} deployment create
20+
\`\`\`
21+
22+
Create a new deployment using a config file.
23+
\`\`\`
24+
$ ${root.name} deployment create -c paperspace.json
25+
\`\`\`
26+
27+
Create a new deployment for an app in a different directory.
28+
\`\`\`
29+
$ ${root.name} deployment create --cwd ../my-app
30+
\`\`\`
31+
32+
Create a new deployment for an app in a specific project.
33+
\`\`\`
34+
$ ${root.name} deployment create --project-id 1234
35+
\`\`\`
36+
`,
37+
commands: subCommands,
38+
flags: upFlags,
39+
// We use command metadata in the `persistentPreRun` function to check if a
40+
// command requires an API key. If it does, we'll check to see if one is
41+
// set. If not, we'll throw an error.
42+
meta: {
43+
requireApiKey: true,
44+
},
45+
}).run(async function* ({ flags, ctx }) {
46+
for await (const line of runUp({ flags, ctx })) {
47+
yield line;
48+
}
49+
});

commands/login/mod.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,12 @@ export const login = command("login", {
2828
let apiKey: string | undefined = args[0];
2929

3030
if (!apiKey) {
31-
open(new URL("settings/apikeys", env.get("PAPERSPACE_CONSOLE_URL")) + "");
31+
try {
32+
open(new URL("settings/apikeys", env.get("PAPERSPACE_CONSOLE_URL")) + "");
33+
} catch (_err) {
34+
// do nothing it's all good
35+
}
36+
3237
apiKey = await secret("Enter an API key:");
3338

3439
if (!apiKey) {

commands/project/list/mod.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,14 @@ const subCommands: ReturnType<typeof command>[] = [];
1414

1515
export const list = command("list", {
1616
short: "List projects.",
17-
long: () => `
17+
long: ({ root }) => `
1818
List projects in your team.
1919
2020
Examples:
2121
2222
Pick a subset of fields to display:
2323
\`\`\`
24-
pspace project list -F handle -F name -F dtCreated
24+
${root.name} project list -F handle -F name -F dtCreated
2525
\`\`\`
2626
`,
2727
commands: subCommands,

0 commit comments

Comments
 (0)