From 72778e83401e6432c0fb5f58d69b84e8f7942d8c Mon Sep 17 00:00:00 2001 From: Max Howell Date: Mon, 22 May 2023 07:56:07 -0400 Subject: [PATCH] workaround link(pkg) twice at once --- README.md | 6 ++++-- src/prefab/install.test.ts | 3 ++- src/prefab/link.test.ts | 37 +++++++++++++++++++++++++++++++++++++ src/prefab/link.ts | 18 ++++++++++++++---- 4 files changed, 57 insertions(+), 7 deletions(-) create mode 100644 src/prefab/link.test.ts diff --git a/README.md b/README.md index 46df651..9067b38 100644 --- a/README.md +++ b/README.md @@ -57,12 +57,14 @@ const { pkgs: tree } = await hydrate(pkg) const { installed, pending } = await resolve(tree) for (const pkg of pending) { - const install = await install(pkg) + const installation = await install(pkg) // ^^ install packages that aren’t yet installed // ^^ takes a logger parameter so you can show progress to the user // ^^ you could do these in parallel to speed things up // ^^ by default, versioned installs go to ~/.tea, separated from the user’s system. The install location can be customized, see next section. - installed.push(install) + await link(installation) + // ^^ creates v*, vx, vx.y symlinks ∵ some packages depend on this + installed.push(installation) } const { map, flatten } = useShellEnv() diff --git a/src/prefab/install.test.ts b/src/prefab/install.test.ts index 833d67f..3d54d69 100644 --- a/src/prefab/install.test.ts +++ b/src/prefab/install.test.ts @@ -24,7 +24,8 @@ Deno.test("install()", async runner => { assertEquals(installation.pkg.version, pkg.version) assertEquals(installation.path, conf.prefix.join(pkg.project, `v${pkg.version}`)) - installation.path.rm({ recursive: true }) + /// so next test works + installation.path.rm({ recursive: true }) stubber.restore() }) diff --git a/src/prefab/link.test.ts b/src/prefab/link.test.ts new file mode 100644 index 0000000..d6f5837 --- /dev/null +++ b/src/prefab/link.test.ts @@ -0,0 +1,37 @@ +import { useTestConfig } from "../hooks/useConfig.test.ts" +import { assert } from "deno/testing/asserts.ts" +import SemVer from "../utils/semver.ts" +import { Package } from "../types.ts" +import install from "./install.ts" +import link from "./link.ts"; + +Deno.test("link()", async runner => { + const pkg: Package = { + project: "tea.xyz/brewkit", + version: new SemVer("0.30.0") + } + + await runner.step("link()", async () => { + useTestConfig() + + const installation = await install(pkg) + await link(installation) + await link(installation) // calling twice works + + /// test symlinks work + assert(installation.path.parent().join("v*").isDirectory()) + assert(installation.path.parent().join(`v${pkg.version.major}`).isDirectory()) + }) + + await runner.step("link() ×2 at once", async () => { + const installation = await install(pkg) + const p1 = link(installation) + const p2 = link(installation) + + await Promise.all([p1, p2]) + + /// test symlinks work + assert(installation.path.parent().join("v*").isDirectory()) + assert(installation.path.parent().join(`v${pkg.version.major}`).isDirectory()) + }) +}) diff --git a/src/prefab/link.ts b/src/prefab/link.ts index 541fff8..120942a 100644 --- a/src/prefab/link.ts +++ b/src/prefab/link.ts @@ -43,9 +43,19 @@ export default async function link(pkg: Package | Installation) { } async function makeSymlink(symname: string) { - await Deno.symlink( - installation.path.basename(), - shelf.join(symname).rm().string, - {type: 'dir'}) + try { + await Deno.symlink( + installation.path.basename(), // makes it relative + shelf.join(symname).rm().string, + {type: 'dir'}) + } catch (err) { + if (err instanceof Deno.errors.AlreadyExists) { + //FIXME race condition for installing the same pkg simultaneously + // real fix is to lock around the entire download/untar/link process + return + } else { + throw err + } + } } }