Skip to content

Commit

Permalink
Fix circular dependency in hooks
Browse files Browse the repository at this point in the history
  • Loading branch information
mxcl committed Sep 20, 2022
1 parent ef316bc commit 1f356e1
Show file tree
Hide file tree
Showing 13 changed files with 66 additions and 56 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Change how your team works.
 


# tea/cli 0.6.1
# tea/cli 0.6.2

tea is a universal virtual‑environment manager:

Expand Down
6 changes: 3 additions & 3 deletions src/app.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import useFlags, { useArgs } from "hooks/useFlags.ts"
import { useCellar, useMagic, useVirtualEnv } from "hooks"
import { usePrefix, useMagic, useVirtualEnv } from "hooks"
import dump from "./app.dump.ts"
import exec from "./app.exec.ts"
import help from "./app.help.ts"
Expand Down Expand Up @@ -33,7 +33,7 @@ try {
await print(`tea ${version}`)
break
case "prefix":
await print(useCellar().prefix.string)
await print(usePrefix().string)
}
} catch (err) {
if (silent) {
Expand All @@ -45,7 +45,7 @@ try {

function announce() {
const self = new Path(Deno.execPath())
const prefix = useCellar().prefix.string
const prefix = usePrefix().string

if (self.basename() == "deno") {
console.verbose({ deno: self.string, prefix, import: import.meta })
Expand Down
17 changes: 11 additions & 6 deletions src/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
// order is important to avoid circular dependencies and thus uncaught ReferenceErrors

import usePrefix from "./usePrefix.ts"
import useDownload from "./useDownload.ts"
import useCache from "./useCache.ts"
import useCellar from "./useCellar.ts"
import useDownload from "./useDownload.ts"
import useExecutableMarkdown from "./useExecutableMarkdown.ts"
import useFlags from "./useFlags.ts"
import useGitHubAPI from "./useGitHubAPI.ts"
import useInventory from "./useInventory.ts"
import useMagic from "./useMagic.ts"
import usePantry from "./usePantry.ts"
import useShellEnv from "./useShellEnv.ts"
import useVirtualEnv from "./useVirtualEnv.ts"
import useSourceUnarchiver from "./useSourceUnarchiver.ts"
import usePantry from "./usePantry.ts"
import useVirtualEnv from "./useVirtualEnv.ts"
import useMagic from "./useMagic.ts"

// but we can sort these alphabetically
export {
useCache,
useCellar,
Expand All @@ -21,7 +25,8 @@ export {
useInventory,
useMagic,
usePantry,
usePrefix,
useShellEnv,
useVirtualEnv,
useSourceUnarchiver
useSourceUnarchiver,
useVirtualEnv
}
32 changes: 5 additions & 27 deletions src/hooks/useCellar.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Package, PackageRequirement, Installation } from "types"
import { compare_pkg } from "utils"
import { compare_pkg, pkg_str } from "utils"
import { usePrefix } from "hooks"
import Path from "path"
import SemVer, * as semver from "semver"

Expand All @@ -12,8 +13,6 @@ interface Return {
resolve(pkg: Package | PackageRequirement | Path): Promise<Installation>
/// returns all installed versions of a project
ls(project: string): Promise<Installation[]>
/// typically: ~/.tea
prefix: Path

/// eg. ~/.tea/deno.land
shelf(project: string): Path
Expand All @@ -22,20 +21,7 @@ interface Return {
}

export default function useCellar(): Return {
const prefix = (() => {
//NOTE doesn't work for scripts as Deno.run doesn't push through most env :/
const env = Deno.env.get("TEA_PREFIX")
if (env) {
return new Path(env)
} else {
// calculate from our PATH
// NOTE expands symlinks (we don't want recursive expansion, but seemingly
// deno does this for us 😒)
// this works if tea is installed correctly to /opt/tea.xyz/vx/bin or we
// are a source installation running off a tea installed deno at /opt/deno.land/vx/bin
return new Path(Deno.execPath()).readlink().parent().parent().parent().parent()
}
})()
const prefix = usePrefix()

const ls = async (project: string) => {
if (!prefix.join(project).isDirectory()) return []
Expand Down Expand Up @@ -77,7 +63,7 @@ export default function useCellar(): Return {
return { path, pkg: { project: pkg.project, version } }
}
}
throw new Error(`not-found:${str(pkg)}`)
throw new Error(`not-found:${pkg_str(pkg)}`)
}

const shelf = (project: string) => {
Expand All @@ -92,7 +78,7 @@ export default function useCellar(): Return {
return resolution
}

return { resolve, ls, mkpath, prefix, shelf, isInstalled }
return { resolve, ls, mkpath, shelf, isInstalled }
}


Expand All @@ -109,11 +95,3 @@ async function looksEmpty(path: Path): Promise<boolean> {
}
return true
}

function str(pkg: Package | PackageRequirement): string {
if ("constraint" in pkg) {
return `${pkg.project}@${pkg.constraint}`
} else {
return `${pkg.project}@${pkg.version}`
}
}
6 changes: 3 additions & 3 deletions src/hooks/useDownload.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { readerFromStreamReader, copy } from "deno/streams/conversion.ts"
import { useCellar, useFlags } from "hooks"
import { useFlags, usePrefix } from "hooks"
import { flatmap } from "utils"
import { Sha256 } from "deno/hash/sha256.ts"
import Path from "path"
Expand Down Expand Up @@ -74,14 +74,14 @@ function hash_key(url: URL): Path {
return new Sha256().update(formatted).toString()
}

return useDownload().prefix
return usePrefix()
.join(url.protocol.slice(0, -1))
.join(url.hostname)
.join(hash(url))
.mkpath()
}

export default function useDownload() {
const prefix = useCellar().prefix.join("tea.xyz/var/www")
const prefix = usePrefix().join("tea.xyz/var/www")
return { download, prefix, hash_key }
}
4 changes: 2 additions & 2 deletions src/hooks/useFlags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import { flatmap, chuzzle } from "utils"
import { Verbosity, PackageRequirement } from "types"
import { isNumber } from "is_what"
import { set_tmp } from "path"
import { useCellar } from "hooks"
import { usePrefix } from "hooks"
import Path from "path"

// doing here as this is the only file all our scripts import
set_tmp(useCellar().prefix.join('tea.xyz/tmp'))
set_tmp(usePrefix().join('tea.xyz/tmp'))


export type Mode = 'exec' | ['dump', 'env' | 'help' | 'version' | 'prefix']
Expand Down
5 changes: 2 additions & 3 deletions src/hooks/usePackageYAML.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { PackageRequirement } from "types"
import { isPlainObject, isString, isArray, PlainObject } from "is_what"
import { validatePackageRequirement } from "utils/hacks.ts"
import { useCellar } from "hooks"
import { usePrefix } from "hooks"
import { validate_plain_obj } from "utils"
import Path from "path"

Expand Down Expand Up @@ -33,7 +33,6 @@ interface Return2 extends Return1 {
}

export async function usePackageYAMLFrontMatter(script: Path, srcroot?: Path): Promise<Return2> {
const cellar = useCellar()
const yaml = await script.readYAMLFrontMatter()
const rv = usePackageYAML(yaml)

Expand All @@ -57,6 +56,6 @@ export async function usePackageYAMLFrontMatter(script: Path, srcroot?: Path): P
return input
.replace(/{{\s*srcroot\s*}}/ig, srcroot!.string)
.replace(/{{\s*home\s*}}/ig, Path.home().string)
.replace(/{{\s*tea.prefix\s*}}/ig, cellar.prefix.string)
.replace(/{{\s*tea.prefix\s*}}/ig, usePrefix().string)
}
}
8 changes: 4 additions & 4 deletions src/hooks/usePantry.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// deno-lint-ignore-file no-cond-assign
import { Package, PackageRequirement } from "types"
import { run, host, flatmap, undent, validate_plain_obj, validate_str, validate_arr, panic } from "utils"
import { useCellar, useGitHubAPI } from "hooks"
import { useCellar, useGitHubAPI, usePrefix } from "hooks"
import { validatePackageRequirement } from "utils/hacks.ts"
import { isNumber, isPlainObject, isString, isArray, isPrimitive, PlainObject, isBoolean } from "is_what"
import SemVer, * as semver from "semver"
Expand All @@ -15,7 +15,7 @@ interface Entry {
versions: Path
}

const prefix = new Path(`${useCellar().prefix}/tea.xyz/var/pantry/projects`)
const prefix = new Path(`${usePrefix()}/tea.xyz/var/pantry/projects`)

export default function usePantry() {
return {
Expand Down Expand Up @@ -106,7 +106,7 @@ const getScript = async (pkg: Package, key: 'build' | 'test') => {

const update = async () => {
//FIXME real fix is: don’t use git!
const git = useCellar().prefix.join('git-scm.org/v*')
const git = usePrefix().join('git-scm.org/v*')
if (git.isDirectory() || Path.root.join("usr/bin/git").isExecutableFile()) {
await run({
cmd: ["git", "-C", prefix, "pull", "origin", "HEAD", "--no-edit"]
Expand Down Expand Up @@ -348,7 +348,7 @@ const remapTokens = (input: string, pkg: Package) => {
{ from: "prefix", to: prefix.string },
{ from: "hw.concurrency", to: navigator.hardwareConcurrency.toString() },
{ from: "pkg.pantry-prefix", to: getPrefix(pkg).string },
{ from: "tea.prefix", to: cellar.prefix.string }
{ from: "tea.prefix", to: usePrefix().string }
].reduce((acc, {from, to}) =>
acc.replace(new RegExp(`\\$?{{\\s*${from}\\s*}}`, "g"), to),
input)
Expand Down
18 changes: 18 additions & 0 deletions src/hooks/usePrefix.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import Path from "path"

const prefix = (() => {
//NOTE doesn't work for scripts as Deno.run doesn't push through most env :/
const env = Deno.env.get("TEA_PREFIX")
if (env) {
return new Path(env)
} else {
// we’re either deno.land/vx/bin/deno, tea.xyz/vx/bin/tea or some symlink to the latter
return new Path(Deno.execPath())
.readlink() // resolves the leaf symlink (if any)
.parent().parent().parent().parent()
}
})()

export default function usePrefix() {
return prefix
}
6 changes: 3 additions & 3 deletions src/hooks/useShellEnv.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Installation, PackageRequirement } from "types"
import { useCellar } from "hooks"
import { usePrefix, useCellar } from "hooks"
import { host } from "utils"

type Env = Record<string, string[]>
Expand Down Expand Up @@ -91,7 +91,7 @@ export default async function useShellEnv(requirements: PackageRequirement[] | I
for (const key of EnvKeys) {
const defaultValue = Deno.env.get(key)
?.split(":")
?.filter(x => !x.startsWith(cellar.prefix.string)) ?? [] //FIXME not great
?.filter(x => !x.startsWith(usePrefix().string)) ?? [] //FIXME not great
defaults[key] = defaultValue
combined[key] = (vars[key] ?? []).concat(defaultValue)
combinedStrings[key] = combined[key].join(":")
Expand Down Expand Up @@ -131,5 +131,5 @@ export function expand(env: Record<string, string[]>) {
}

function tea_PATH() {
return useCellar().prefix.join('tea.xyz/v*/bin')
return usePrefix().join('tea.xyz/v*/bin')
}
5 changes: 3 additions & 2 deletions src/prefab/install.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useCache, useCellar, useFlags, useDownload } from "hooks"
import { usePrefix, useCache, useCellar, useFlags, useDownload } from "hooks"
import { run, TarballUnarchiver, host } from "utils"
import { encode } from "deno/encoding/hex.ts"
import { crypto } from "deno/crypto/mod.ts"
Expand All @@ -18,8 +18,9 @@ export default async function install(pkg: Package): Promise<Installation> {
const { project, version } = pkg
const { s3Key, download } = useCache()
const url = new URL(`https://dist.tea.xyz/${s3Key(pkg)}`)
const { prefix: dstdir, ...cellar } = useCellar()
const cellar = useCellar()
const { verbosity } = useFlags()
const dstdir = usePrefix()

const tarball = await download({ url, pkg: { project, version } })

Expand Down
9 changes: 9 additions & 0 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,15 @@ export function compare_pkg(a: Package, b: Package): number {
: (a.project < b.project ? -1 : 1)
}

export function pkg_str(pkg: Package | PackageRequirement): string {
if ("constraint" in pkg) {
return `${pkg.project}@${pkg.constraint}`
} else {
return `${pkg.project}@${pkg.version}`
}
}


/////////////////////////////////////////////////////////////////////// semver
import SemVer, * as semver from "semver"

Expand Down
4 changes: 2 additions & 2 deletions tests/utils.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import Path from "path"
import { useCellar } from "hooks"
import { usePrefix } from "hooks"

export async function shout({ tea: args, cwd }: { tea: string[], cwd?: Path }) {
const srcroot = Deno.env.get("SRCROOT")
const cmd = [
'deno',
'run',
'--allow-env', '--allow-read', '--allow-run',
`--allow-write=${useCellar().prefix}`,
`--allow-write=${usePrefix()}`,
`--import-map=${srcroot}/import-map.json`,
`${srcroot}/src/app.ts`,
...args
Expand Down

0 comments on commit 1f356e1

Please sign in to comment.