Skip to content

Commit

Permalink
Various related fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
mxcl committed Feb 22, 2023
1 parent 4a3ace5 commit 2e6f203
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 69 deletions.
1 change: 1 addition & 0 deletions src/app.err-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export default async function(err: Error) {

// this way: deno will show the backtrace
if (err instanceof Error == false) throw err
return 1
}
}

Expand Down
1 change: 1 addition & 0 deletions src/app.exec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export default async function(cmd: string[], env: Record<string, string>) {
console.error(err)
} else if (err instanceof Deno.errors.NotFound) {
console.error("tea: command not found:", teal(arg0))
Deno.exit(127) // 127 is used for command not found
} else if (err instanceof Deno.errors.PermissionDenied) {
if (Path.abs(arg0)?.isDirectory()) {
console.error("tea: is directory:", teal(arg0))
Expand Down
79 changes: 27 additions & 52 deletions src/hooks/useExec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,65 +32,46 @@ export default async function({ pkgs, inject, sync, ...opts }: Parameters) {
}

if (arg0 instanceof Path && arg0.isFile()) {
//TODO should check we're a text file first

const precmd: string[] = []

const yaml = await usePackageYAMLFrontMatter(arg0, inject?.srcroot)
if (yaml) {
pkgs.push(...yaml.pkgs)
precmd.unshift(...yaml.args)
Object.assign(env, yaml.env) //FIXME should override env from pkgs

if (pkgs.length == 0) {
const found = await which(cmd[0])
if (found) {
pkgs.push(found)
}
}
pkgs.push(...yaml.pkgs)
}

const shebang_args = await read_shebang(arg0)
if (shebang_args == 'tea') {
if (yaml) {
cmd.unshift(...yaml.args)
if (pkgs.length == 0) {
const found = await which(cmd[0])
if (found) {
pkgs.push(found)
}
}
} else {
const found = await usePantry().getInterpreter(arg0.extname())
if (found) {
const constraint = new semver.Range('*')
pkgs.push({ ...found, constraint })
if (cmd.length == opts.args.length) {
// if YAML specified args then we use them
cmd.unshift(...found.args)
}
await add_companions(found)
} else {
throw new TeaError("confused: interpreter", {arg0})
}
const is_tea_shebang = shebang_args[0] == 'tea'
if (shebang_args.length) {
if (is_tea_shebang) {
do {
shebang_args.shift()
} while (shebang_args[0]?.startsWith('-'))
}
} else if (shebang_args.length) {
if (yaml?.args) console.warn("warning: YAML Front Matter `args` are being ignored")
precmd.unshift(...shebang_args)
}

const found = await which(shebang_args[0])
if (precmd.length == 0) {
const found = await usePantry().getInterpreter(arg0.extname())
if (found) {
pkgs.push(found)
cmd.unshift(...shebang_args)
pkgs.push({ ...found, constraint: new semver.Range('*') })
precmd.unshift(...found.args)
await add_companions(found)
} else if (is_tea_shebang) {
throw new TeaError("confused: interpreter", {arg0})
}
} else {
const found = await usePantry().getInterpreter(arg0.extname())
const found = await which(precmd[0])
if (found) {
const constraint = new semver.Range('*')
pkgs.push({ ...found, constraint })
if (cmd.length == opts.args.length) {
// if YAML specified args then we use them
cmd.unshift(...found.args)
}
pkgs.push(found)
await add_companions(found)
}
// else just try to run it
}

cmd.unshift(...precmd)

} else if (!clutch && !(arg0 instanceof Path)) {
const found = await which(arg0)
if (found) {
Expand Down Expand Up @@ -144,17 +125,12 @@ async function install(pkgs: PackageSpecification[], update: boolean): Promise<I

import { readLines } from "deno/io/read_lines.ts"

async function read_shebang(path: Path): Promise<'tea' | string[]> {
async function read_shebang(path: Path): Promise<string[]> {
const f = await Deno.open(path.string, { read: true })
const line = (await readLines(f).next()).value as string
let shebang = line.match(/^#!\/usr\/bin\/env (-\S+ )?(.*)$/)?.[2]
if (shebang) {
const args = shebang.split(/\s+/).filter(x => x)
if (args[0] == 'tea') {
args.shift() // only drop tea; return rest
if (args.length == 0) return 'tea'
}
return args
return shebang.split(/\s+/).filter(x => x)
}

// allowing leading whitespace since it seems pretty common in the wild
Expand All @@ -163,7 +139,6 @@ async function read_shebang(path: Path): Promise<'tea' | string[]> {
const args = shebang.split(/\s+/).filter(x => x)
const arg0 = Path.abs(args.shift() ?? '')?.basename()
if (!arg0) throw new Error(`couldn’t figure out shebang: ${line} for ${path}`)
if (arg0 == 'tea') return 'tea'
return [arg0, ...args]
}

Expand Down
42 changes: 25 additions & 17 deletions tests/integration/tea.scripts.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,11 @@ it(suite, "tea env shebang with args", async function() {
console.log('${fuzz}')
`
})
const out = await this.run({args: [fixture.string]}).stdout()
}).chmod(0o500)
let out = await this.run({args: [fixture.string]}).stdout()
assertEquals(out.trim(), fuzz)

out = await this.run({cmd: [fixture.string]}).stdout()
assertEquals(out.trim(), fuzz)
})

Expand All @@ -60,7 +63,10 @@ it(suite, "tea env shebang with args in the shebang", async function() {
echo "${fuzz}"
`
}).chmod(0o500)
const out = await this.run({cmd: [fixture.string]}).stdout()
let out = await this.run({cmd: [fixture.string]}).stdout()
assertEquals(out.trim(), fuzz)

out = await this.run({args: [fixture.string]}).stdout()
assertEquals(out.trim(), fuzz)
})

Expand All @@ -74,7 +80,11 @@ if (Deno.build.os == "darwin") {
echo "${fuzz}"
`
}).chmod(0o500)
const out = await this.run({cmd: [fixture.string]}).stdout()

let out = await this.run({cmd: [fixture.string]}).stdout()
assertEquals(out.trim(), fuzz)

out = await this.run({args: [fixture.string]}).stdout()
assertEquals(out.trim(), fuzz)
})
}
Expand Down Expand Up @@ -124,10 +134,10 @@ it(suite, "env shebang with args in the shebang line without the -S executing vi
it(suite, "env shebang with args in both places", async function() {
const fuzz = "hi"
const fixture = this.sandbox.join("fixture.sh").write({ text: undent`
#!/usr/bin/env -S deno run --allow-read
#!/usr/bin/env -S deno run
/*---
args: [deno, __WOULD_FAIL]
args: [--allow-read]
---*/
Deno.readTextFileSync("fixture.sh")
Expand All @@ -139,13 +149,13 @@ it(suite, "env shebang with args in both places", async function() {
assertEquals(out.trim(), fuzz)
})

it(suite, "shebang with args in both places", async function() {
it(suite, "deno shebang with args in both places", async function() {
const fuzz = "hi"
const fixture = this.sandbox.join("fixture.sh").write({ text: undent`
#!/usr/bin/deno run --allow-read
#!/usr/bin/deno run
/*---
args: [deno, __WOULD_FAIL]
args: [--allow-read]
---*/
Deno.readTextFileSync("fixture.sh")
Expand All @@ -157,14 +167,14 @@ it(suite, "shebang with args in both places", async function() {
assertEquals(out.trim(), fuzz)
})

it(suite, "tea shebang with args in both places", async function() {
it(suite, "tea shebang with YAML args", async function() {
const fuzz = "hi"

const fixture = this.sandbox.join("fixture.sh").write({ text: undent`
#!/usr/bin/env -S tea deno run --allow-read
#!/usr/bin/env -S tea
/*---
args: [deno, __WOULD_FAIL]
args: [deno, run, --allow-read]
---*/
Deno.readTextFileSync("fixture.sh")
Expand All @@ -187,17 +197,15 @@ it(suite, "tea script that tea doesn’t know what to do with errors cleanly", a
assertEquals(out, 103)
})

it(suite, "tea shebang but executed via tea still works", async function() {
it(suite, "tea shebang but executed via tea interprets shebang", async function() {
const fuzz = "hi"

const fixture = this.sandbox.join("fixture.sh").write({ text: undent`
#!/usr/bin/env -S tea deno run --allow-read
Deno.readTextFileSync("fixture.sh")
#!/usr/bin/env -S tea node
console.log('${fuzz}')
`
}).chmod(0o500)
const out = await this.run({args: [fixture.string]}).stdout()
assertEquals(out.trim(), fuzz)
})
})

0 comments on commit 2e6f203

Please sign in to comment.