Skip to content

Commit

Permalink
getting there (#62)
Browse files Browse the repository at this point in the history
  • Loading branch information
mxcl authored Sep 24, 2022
1 parent 452da22 commit 0f5c78b
Show file tree
Hide file tree
Showing 11 changed files with 115 additions and 78 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.4
# tea/cli 0.6.5

tea is a universal virtual‑environment manager:

Expand Down
16 changes: 13 additions & 3 deletions scripts/install.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,28 @@ args:
---*/

import { link, install, resolve } from "prefab"
import { pkg } from "utils"
import { useFlags } from "hooks"
import { pkg } from "utils"

useFlags()

const pkgs = Deno.args.map(project => {
const force = !!Deno.env.get("FORCE")

const rqs = Deno.args.map(project => {
const match = project.match(/projects\/(.+)\/package.yml/)
return match ? match[1] : project
}).map(pkg.parse)

const { pending, installed } = await resolve(rqs)

if (!force) {
console.info({'already-installed': installed})
}

const pkgs = force ? [...installed.map(x=>x.pkg), ...pending] : pending

// resolve and install precise versions that are available in available inventories
for await (const pkg of await resolve(pkgs)) {
for (const pkg of pkgs) {
console.log({ installing: pkg.project })
const installation = await install(pkg)
await link(installation)
Expand Down
21 changes: 19 additions & 2 deletions src/app.dump.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Installation, PackageRequirement } from "types"
import { useShellEnv, usePantry, useCellar } from "hooks"
import { VirtualEnv } from "hooks/useVirtualEnv.ts"
import { useShellEnv, usePantry } from "hooks"
import { print, undent } from "utils"

interface Options {
Expand Down Expand Up @@ -31,7 +32,23 @@ export default async function dump({ env: blueprint }: Options) {
await print(unsetEnv("SRCROOT"))
}

const { combinedStrings: vars, pending } = await useShellEnv(blueprint?.requirements ?? [])
const [installations, pending] = await (async () => {
const cellar = useCellar()
const installations: Installation[] = []
const pending: PackageRequirement[] = []

for (const rq of blueprint?.requirements ?? []) {
const installation = await cellar.has(rq)
if (installation) {
installations.push(installation)
} else {
pending.push(rq)
}
}
return [installations, pending]
})()

const { combinedStrings: vars } = await useShellEnv(installations)

//TODO if PATH is the same as the current PATH maybe don't do it
// though that makes the behavior of --env --dump very specific
Expand Down
33 changes: 17 additions & 16 deletions src/app.exec.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { useFlags, useCellar, useShellEnv, usePantry, useExecutableMarkdown } from "hooks"
import { useFlags, useShellEnv, usePantry, useExecutableMarkdown } from "hooks"
import { hydrate, resolve, install as base_install, link } from "prefab"
import { VirtualEnv } from "hooks/useVirtualEnv.ts"
import { run, undent } from "utils"
import { PackageRequirement } from "types"
import { hydrate, resolve, install as base_install, link } from "prefab"
import { run, undent } from "utils"
import Path from "path"

type Options = {
Expand All @@ -12,12 +12,11 @@ type Options = {
}

export default async function exec({ args, ...opts }: Options) {
const cellar = useCellar()
const flags = useFlags()

if (args.length < 1) throw "contract violation"

await install(opts.pkgs)
const installations = await install(opts.pkgs)

const filename = Path.cwd().join(args[0]).isFile()
if (filename?.extname() == '.md') {
Expand All @@ -30,7 +29,7 @@ export default async function exec({ args, ...opts }: Options) {
args = [path, ...args.slice(2)]
}

const env = (await useShellEnv(opts.pkgs)).combinedStrings
const env = useShellEnv(installations).combinedStrings
if (opts.env) {
env["SRCROOT"] = opts.env.srcroot.string
if (opts.env.version) env["VERSION"] = opts.env.version.toString()
Expand All @@ -41,17 +40,19 @@ export default async function exec({ args, ...opts }: Options) {

const cmd = [...args]
await run({ cmd, env }) //TODO implement `execvp`
}

/////////////////////////////////////////////////////////////
async function install(dry: PackageRequirement[]) {
const get = (x: PackageRequirement) => usePantry().getDeps(x).then(x => x.runtime)
const wet = await hydrate(dry, get) ; console.debug({wet})
const gas = await resolve(wet.pkgs) ; console.debug({gas})
for (const pkg of gas) {
if (await cellar.has(pkg)) continue
console.info({ installing: pkg })
const installation = await base_install(pkg)
await link(installation)
}
async function install(dry: PackageRequirement[]) {
const get = (x: PackageRequirement) => usePantry().getDeps(x).then(x => x.runtime)
const wet = await hydrate(dry, get) ; console.debug({wet})
const gas = await resolve(wet.pkgs) ; console.debug({gas})

for (const pkg of gas.pending) {
console.info({ installing: pkg })
const installation = await base_install(pkg)
await link(installation)
gas.installed.push(installation)
}
return gas.installed
}
16 changes: 11 additions & 5 deletions src/hooks/useInventory.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { PackageRequirement } from "types"
import { Package, PackageRequirement } from "types"
import { host } from "utils"
import SemVer from "semver"

Expand All @@ -10,19 +10,25 @@ export interface Inventory {
}
}

const select = async ({ project, constraint }: PackageRequirement) => {
const select = async (rq: PackageRequirement | Package) => {
const { platform, arch } = host()
const url = `https://dist.tea.xyz/${project}/${platform}/${arch}/versions.txt`
const url = `https://dist.tea.xyz/${rq.project}/${platform}/${arch}/versions.txt`
const rsp = await fetch(url)

if (!rsp.ok) throw new Error(`404-not-found: ${url}`) //FIXME

const releases = await rsp.text()
const versions = releases.split("\n").map(x => new SemVer(x))

console.debug({ project, versions })
if (versions.length < 1) throw new Error()

return constraint.max(versions)
console.debug({ project: rq.project, versions })

if ("constraint" in rq) {
return rq.constraint.max(versions)
} else if (versions.find(x => x.eq(rq.version))) {
return rq.version
}
}

export default function useInventory() {
Expand Down
32 changes: 19 additions & 13 deletions src/hooks/usePantry.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Package, PackageRequirement } from "types"
import { Package, PackageRequirement, Installation } from "types"
import { run, host, flatmap, undent, validate_plain_obj, validate_str, validate_arr, panic, pkg } from "utils"
import { useCellar, useGitHubAPI, usePrefix } from "hooks"
import { validatePackageRequirement } from "utils/hacks.ts"
Expand Down Expand Up @@ -85,12 +85,12 @@ const getDistributable = async (pkg: Package) => {
return { url, stripComponents }
}

const getScript = async (pkg: Package, key: 'build' | 'test') => {
const getScript = async (pkg: Package, key: 'build' | 'test', deps: Installation[]) => {
const yml = await entry(pkg).yml()
const node = yml[key]

if (isPlainObject(node)) {
let raw = remapTokens(validate_str(node.script), pkg)
let raw = remapTokens(validate_str(node.script), pkg, deps)

let wd = node["working-directory"]
if (wd) {
Expand All @@ -105,11 +105,11 @@ const getScript = async (pkg: Package, key: 'build' | 'test') => {

const env = node.env
if (isPlainObject(env)) {
raw = `${expand_env(env, pkg)}\n\n${raw}`
raw = `${expand_env(env, pkg, deps)}\n\n${raw}`
}
return raw
} else {
return remapTokens(validate_str(node), pkg)
return remapTokens(validate_str(node), pkg, deps)
}
}

Expand Down Expand Up @@ -263,7 +263,7 @@ async function handleComplexVersions(versions: PlainObject): Promise<SemVer[]> {
return rv
}

function expand_env(env_: PlainObject, pkg: Package): string {
function expand_env(env_: PlainObject, pkg: Package, deps: Installation[]): string {
const env = {...env_}
const sys = host()

Expand Down Expand Up @@ -314,36 +314,42 @@ function expand_env(env_: PlainObject, pkg: Package): string {
} else if (value === undefined || value === null) {
return "0"
} else if (isString(value)) {
return remapTokens(value, pkg)
return remapTokens(value, pkg, deps)
} else if (isNumber(value)) {
return value.toString()
}
throw new Error("unexpected-error")
}
}

const remapTokens = (input: string, pkg: Package) => {
const remapTokens = (input: string, pkg: Package, deps?: Installation[]) => {
const sys = host()
const cellar = useCellar()
const prefix = cellar.keg(pkg)

return [
const map = [
{ from: "version", to: pkg.version.toString() },
{ from: "version.major", to: pkg.version.major.toString() },
{ from: "version.minor", to: pkg.version.minor.toString() },
{ from: "version.patch", to: pkg.version.patch.toString() },
{ from: "version.build", to: pkg.version.build.join('+') },
{ from: "version.marketing", to: `${pkg.version.major}.${pkg.version.minor}` },
// deno-lint-ignore no-explicit-any
{ from: "version.raw", to: (pkg.version as any).raw },
{ from: "version.raw", to: pkg.version.raw },
{ from: "hw.arch", to: sys.arch },
{ from: "hw.target", to: sys.target },
{ from: "hw.platform", to: sys.platform },
{ from: "prefix", to: prefix.string },
{ from: "hw.concurrency", to: navigator.hardwareConcurrency.toString() },
{ from: "pkg.pantry-prefix", to: getPrefix(pkg).string },
{ from: "tea.prefix", to: usePrefix().string }
].reduce((acc, {from, to}) =>
]

for (const dep of deps ?? []) {
map.push({ from: `deps.${dep.pkg.project}.prefix`, to: dep.path.string })
map.push({ from: `deps.${dep.pkg.project}.version`, to: dep.pkg.version.toString() })
map.push({ from: `deps.${dep.pkg.project}.version.major`, to: dep.pkg.version.major.toString() })
}

return map.reduce((acc, {from, to}) =>
acc.replace(new RegExp(`\\$?{{\\s*${from}\\s*}}`, "g"), to),
input)
}
Expand Down
33 changes: 7 additions & 26 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 { usePrefix, useCellar } from "hooks"
import { usePrefix } from "hooks"
import { host } from "utils"

type Env = Record<string, string[]>
Expand All @@ -20,33 +20,17 @@ interface Response {
defaults: Env
combined: Env
combinedStrings: Record<string, string>
pending: PackageRequirement[]
}

export default async function useShellEnv(requirements: PackageRequirement[] | Installation[]): Promise<Response> {
const cellar = useCellar()
export default function useShellEnv(installations: Installation[], pending: PackageRequirement[] = []): Response {
const vars: Env = {}
const pending: PackageRequirement[] = []
const isMac = host().platform == 'darwin'

const pkgs = (await Promise.all(requirements.map(async rq => {
if ("constraint" in rq) {
const installation = await cellar.has(rq)
if (!installation) {
pending.push(rq)
} else {
return installation
}
} else {
return rq
}
}))).compact(x => x)

const projects = new Set([...pkgs.map(x => x.pkg.project), ...pending.map(x=>x.project)])
const projects = new Set([...installations.map(x => x.pkg.project), ...pending.map(x=>x.project)])
const has_cmake = projects.has('cmake.org')
const archaic = true

for (const installation of pkgs) {
for (const installation of installations) {
for (const key of EnvKeys) {
for (const suffix of suffixes(key)!) {
if (!vars[key]) vars[key] = []
Expand Down Expand Up @@ -76,7 +60,8 @@ export default async function useShellEnv(requirements: PackageRequirement[] | I
}
}

const tea = tea_PATH()
//FIXME figure out correct tea-path not assuming 'v*'
const tea = usePrefix().join('tea.xyz/v*/bin')
//FIXME we add these paths so “binutils” and POSIX-utils are available
// but these PATHs will almost certainly contain other things that will
// interfere with our ability to create reproducibility
Expand All @@ -97,7 +82,7 @@ export default async function useShellEnv(requirements: PackageRequirement[] | I
combinedStrings[key] = combined[key].join(":")
}

return { vars, defaults, combined, combinedStrings, pending }
return { vars, defaults, combined, combinedStrings }
}

function suffixes(key: string) {
Expand Down Expand Up @@ -129,7 +114,3 @@ export function expand(env: Record<string, string[]>) {
}
return rv
}

function tea_PATH() {
return usePrefix().join('tea.xyz/v*/bin')
}
2 changes: 1 addition & 1 deletion src/prefab/hydrate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export default async function hydrate(

const dry = input.map(spec => {
if ("version" in spec) {
return {project: spec.project, constraint: new semver.Range(`=${spec.version}`)}
return {project: spec.project, constraint: new semver.Range(spec.version.toString())}
} else {
return spec
}
Expand Down
1 change: 1 addition & 0 deletions src/prefab/install.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export default async function install(pkg: Package): Promise<Installation> {
await sumcheck(tarball, sha_url, pkg)
} catch (err) {
tarball.rm()
console.error("we deleted the invalid tarball. try again?")
throw err
}

Expand Down
Loading

0 comments on commit 0f5c78b

Please sign in to comment.