Skip to content

Commit d8d4f34

Browse files
committed
fix(sandbox): improve cp command & implement extend command
1 parent 0584cfd commit d8d4f34

3 files changed

Lines changed: 69 additions & 41 deletions

File tree

deno.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
"imports": {
1818
"@cfa/gitignore-parser": "jsr:@cfa/gitignore-parser@^0.1.4",
1919
"@cliffy/command": "jsr:@cliffy/command@^1.0.0-rc.8",
20-
"@deno/sandbox": "jsr:@deno/sandbox@^0.4.3",
20+
"@deno/sandbox": "jsr:@deno/sandbox@^0.4.4",
21+
"@std/async": "jsr:@std/async@^1.0.15",
2122
"@std/cli": "jsr:@std/cli@1.0.22",
2223
"@std/dotenv": "jsr:@std/dotenv@^0.225.5",
2324
"@std/encoding": "jsr:@std/encoding@^1.0.10",

deno.lock

Lines changed: 6 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

sandbox.ts

Lines changed: 61 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import { Command } from "@cliffy/command";
22
import { Sandbox } from "@deno/sandbox";
33
import { green, magenta, red } from "@std/fmt/colors";
4+
import { pooledMap } from "@std/async";
5+
import { expandGlob } from "@std/fs";
6+
import { join } from "@std/path";
47

58
import { getAppFromConfig, readConfig, writeConfig } from "./config.ts";
69
import { error, renderTemporalTimestamp, withApp } from "./util.ts";
@@ -47,6 +50,8 @@ export const sandboxCreateCommand = new Command<SandboxContext>()
4750
}
4851

4952
if (options.lifetime === "session") {
53+
console.log(`Created sandbox with id '${sandbox.id}'`);
54+
5055
const success = await sshIntoSandbox(sandbox);
5156
const stopMessage = "Stopping the sandbox...";
5257
if (success) {
@@ -174,18 +179,18 @@ export const sandboxSshCommand = new Command<SandboxContext>()
174179

175180
export const sandboxCopyCommand = new Command<SandboxContext>()
176181
.description("Copy files from or to a running sandbox")
177-
/*.example(
182+
.example(
178183
"Copy a file from a sandbox to the local machine",
179184
"copy someSandboxId:/app/remote-file.txt ./local-file.txt",
180-
)*/
185+
)
181186
.example(
182187
"Copy a file from the local machine to a sandbox",
183188
"copy ./local-file.txt someSandboxId:/app/remote-file.txt",
184189
)
185-
/*.example(
190+
.example(
186191
"Copy multiple files from a sandbox to the local machine",
187192
"copy someSandboxId:/app/remote-file.txt someSandboxId:/app/another-remote-file.txt ./",
188-
)*/
193+
)
189194
.example(
190195
"Copy multiple files from the local machine to a sandbox",
191196
"copy ./local-file.txt ./another-local-file.txt someSandboxId:/app/",
@@ -194,10 +199,14 @@ export const sandboxCopyCommand = new Command<SandboxContext>()
194199
"Copy a directory from the local machine to a sandbox",
195200
"copy ./ ./another-local-file.txt someSandboxId:/app/",
196201
)
197-
/*.example(
202+
.example(
198203
"Copy files from a sandbox to another sandbox",
199204
"copy someSandboxId:/app/remote-file.txt anotherSandboxId:/app/remote-file.txt",
200-
)*/
205+
)
206+
.example(
207+
"Copy a all files from a directory in a sandbox to the local machine",
208+
"copy someSandboxId:/app/* ./",
209+
)
201210
.arguments("<paths...:string>")
202211
.action(async (options, ...paths) => {
203212
if (paths.length < 2) {
@@ -207,56 +216,70 @@ export const sandboxCopyCommand = new Command<SandboxContext>()
207216
const target = paths.pop()!;
208217

209218
if (target.includes(":")) {
210-
const [sandboxId, sandboxPath] = target.split(":");
211-
await using sandbox = await connectToSandbox(options, sandboxId);
219+
const [sandboxId, targetSandboxPath] = target.split(":");
220+
await using targetSandbox = await connectToSandbox(options, sandboxId);
212221

213-
//const sourceSandboxPaths = [];
222+
const sourceSandboxPaths = [];
214223
const localPaths = [];
215224

216225
for (const path of paths) {
217226
if (path.includes(":")) {
218-
error(
219-
options.debug,
220-
"Copying between sandboxes is currently not supported",
221-
);
222-
223-
//sourceSandboxPaths.push(path);
227+
sourceSandboxPaths.push(path);
224228
} else {
225229
localPaths.push(path);
226230
}
227231
}
228232

229-
/*const sourceSandboxGroups = groupPathsBySandbox(sourceSandboxPaths);
233+
const sourceSandboxGroups = groupPathsBySandbox(sourceSandboxPaths);
230234
const sourceSandboxes: Record<string, Sandbox> = {};
231235

232236
for (const sandboxId of Object.keys(sourceSandboxGroups)) {
233237
sourceSandboxes[sandboxId] = await connectToSandbox(options, sandboxId);
234-
}*/
238+
}
235239

236240
await Promise.all([
237241
...localPaths.map((path) => {
238-
return sandbox.upload(path, sandboxPath);
242+
return targetSandbox.upload(path, targetSandboxPath);
239243
}),
240-
/*...Object.entries(sourceSandboxGroups).map(
244+
...Object.entries(sourceSandboxGroups).map(
241245
async ([sandboxId, sourceSandboxPaths]) => {
242246
const sourceSandbox = sourceSandboxes[sandboxId];
243247

244248
await Promise.all(
245249
sourceSandboxPaths.map(async (sourceSandboxPath) => {
246250
const tempDir = await Deno.makeTempDir();
247-
await sourceSandbox.download(sourceSandboxPath, tempDir);
248-
await sandbox.upload(tempDir, target);
251+
252+
await Array.fromAsync(pooledMap(
253+
Infinity,
254+
sourceSandbox.expandGlob(sourceSandboxPath),
255+
async (sandboxEntry) => {
256+
await sourceSandbox.download(sandboxEntry.path, tempDir);
257+
258+
await Array.fromAsync(pooledMap(
259+
Infinity,
260+
expandGlob(`${tempDir}/*`),
261+
(localEntry) =>
262+
targetSandbox.upload(
263+
localEntry.path,
264+
join(
265+
targetSandboxPath,
266+
localEntry.isDirectory
267+
? "./"
268+
: `./${localEntry.name}`,
269+
),
270+
),
271+
));
272+
},
273+
));
249274
}),
250275
);
251276

252-
await sandbox.close();
277+
await sourceSandbox.close();
253278
},
254-
),*/
279+
),
255280
]);
256281
} else {
257-
error(options.debug, "Copying from sandboxes is currently not supported");
258-
259-
/*for (const path of paths) {
282+
for (const path of paths) {
260283
if (!path.includes(":")) {
261284
error(
262285
options.debug,
@@ -276,13 +299,17 @@ export const sandboxCopyCommand = new Command<SandboxContext>()
276299
Object.entries(groups).map(async ([sandboxId, sandboxPaths]) => {
277300
const sandbox = sandboxes[sandboxId];
278301

279-
await Promise.all(sandboxPaths.map((sandboxPath) => {
280-
return sandbox.download(sandboxPath, target);
302+
await Promise.all(sandboxPaths.map(async (sandboxPath) => {
303+
await Array.fromAsync(pooledMap(
304+
Infinity,
305+
sandbox.expandGlob(sandboxPath),
306+
(entry) => sandbox.download(entry.path, target),
307+
));
281308
}));
282309

283310
await sandbox.close();
284311
}),
285-
);*/
312+
);
286313
}
287314
});
288315

@@ -379,20 +406,17 @@ export const sandboxRunCommand = new Command<SandboxContext>()
379406
Deno.exit(status.code);
380407
});
381408

382-
/*
383409
export const sandboxExtendCommand = new Command<SandboxContext>()
384410
.description("Extend the lifetime of a running sandbox")
385411
.arguments("<sandbox-id:string> <lifetime:string>")
386412
.action(async (options, sandboxId, lifetime) => {
387413
await using sandbox = await connectToSandbox(options, sandboxId);
388414

389-
console.log(await sandbox.extendLifetime(lifetime));
415+
console.log(await sandbox.extendLifetime(lifetime as any));
390416
});
391-
*/
392417

393-
/*
394418
function groupPathsBySandbox(paths: string[]): Record<string, string[]> {
395-
const groups = {};
419+
const groups: Record<string, string[]> = {};
396420

397421
for (const path of paths) {
398422
const [sandboxId, sandboxPath] = path.split(":");
@@ -403,7 +427,7 @@ function groupPathsBySandbox(paths: string[]): Record<string, string[]> {
403427
}
404428

405429
return groups;
406-
}*/
430+
}
407431

408432
async function ensureOrg(options: SandboxContext, quiet: boolean = true) {
409433
const config = await readConfig(Deno.cwd(), options.config);
@@ -440,6 +464,7 @@ async function connectToSandbox(
440464

441465
return await Sandbox.connect({
442466
id: sandboxId,
467+
apiEndpoint: options.endpoint,
443468
region: cluster.region,
444469
debug: options.debug,
445470
token,
@@ -597,5 +622,5 @@ export const sandboxCommand = new Command<GlobalOptions>()
597622
.alias("cp")
598623
.command("exec", sandboxExecCommand)
599624
.command("run", sandboxRunCommand)
600-
//.command("extend", sandboxExtendCommand)
625+
.command("extend", sandboxExtendCommand)
601626
.command("ssh", sandboxSshCommand);

0 commit comments

Comments
 (0)