Skip to content

Commit

Permalink
Add std.runnableBashScript
Browse files Browse the repository at this point in the history
  • Loading branch information
kylewlacy committed May 31, 2024
1 parent 5a52dba commit a12eb0f
Show file tree
Hide file tree
Showing 4 changed files with 235 additions and 1 deletion.
2 changes: 1 addition & 1 deletion projects/std/core/index.bri
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export * from "./recipes";
export { source } from "./source.bri";
export { assert, unreachable, indoc, type Awaitable } from "./utils.bri";
export { assert, unreachable, indoc, mixin, type Awaitable } from "./utils.bri";
export {
utf8Encode,
utf8Decode,
Expand Down
1 change: 1 addition & 0 deletions projects/std/extra/index.bri
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from "./autowrap.bri";
export * from "./run_bash.bri";
export * from "./runnable_bash_script.bri";
export * from "./set_env.bri";
export * from "./set_run.bri";
172 changes: 172 additions & 0 deletions projects/std/extra/runnable_bash_script.bri
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
import * as std from "/core";
import { tools } from "/toolchain";
import {
type RunnableTemplate,
makeRunnableExecutable,
} from "/runnable_tools.bri";

export type RunnableBashScript = std.Recipe<std.Directory> &
RunnableBashScriptUtils;

export interface RunnableBashScriptUtils {
env(values: Record<string, RunnableTemplateValue>): RunnableBashScript;
dependencies(
...dependencies: std.AsyncRecipe<std.Directory>[]
): RunnableBashScript;
}

export function runnableBashScript(
strings: TemplateStringsArray,
...values: string[]
): RunnableBashScript {
const script = std.indoc(strings, ...values);
return makeRunnableBashScript({
script,
env: {
artifact: { relativePath: "." },
},
dependencies: [],
});
}

interface RunnableBashScriptOptions {
script: string;
env: Record<string, RunnableTemplateValue>;
dependencies: std.AsyncRecipe<std.Directory>[];
}

function makeRunnableBashScript(
options: RunnableBashScriptOptions,
): RunnableBashScript {
let recipe = std.directory();
let n = 0;
let command: RunnableTemplate = { components: [] };
[command, recipe, n] = buildTemplate([tools().get("bin/bash")], recipe, n);

const argTemplates: RunnableTemplateValue = [
"-e",
"-u",
"-o",
"pipefail",
"-c",
options.script,
"--",
];
const args: RunnableTemplate[] = [];
for (const arg of argTemplates) {
let argTemplate: RunnableTemplate;
[argTemplate, recipe, n] = buildTemplate(arg, recipe, n);
args.push(argTemplate);
}

const env: Record<string, RunnableTemplate> = {};
for (const [key, value] of Object.entries(options.env)) {
let valueTemplate: RunnableTemplate;
[valueTemplate, recipe, n] = buildTemplate(value, recipe, n);
env[key] = valueTemplate;
}

const path = env["PATH"] ?? { components: [] };
for (const dep of options.dependencies) {
let depTemplate: RunnableTemplate;
[depTemplate, recipe, n] = buildTemplate([dep, "/bin"], recipe, n);

if (path.components.length > 0) {
path.components.push(
{ type: "literal", value: std.bstring(":") },
...depTemplate.components,
);
} else {
path.components.push(...depTemplate.components);
}
}

if (path.components.length > 0) {
env["PATH"] = path;
}

const runnable = makeRunnableExecutable({
command,
args,
env,
});

recipe = recipe.insert("brioche-run", runnable);

return std.mixin(recipe, {
env(values: Record<string, RunnableTemplateValue>): RunnableBashScript {
return makeRunnableBashScript({
script: options.script,
env: { ...options.env, ...values },
dependencies: options.dependencies,
});
},

dependencies(
...dependencies: std.AsyncRecipe<std.Directory>[]
): RunnableBashScript {
return makeRunnableBashScript({
script: options.script,
env: options.env,
dependencies: [...options.dependencies, ...dependencies],
});
},
});
}

type RunnableTemplateValue =
| string
| undefined
| { relativePath: string }
| std.AsyncRecipe
| RunnableTemplateValue[];

function buildTemplate(
template: RunnableTemplateValue,
recipe: std.Recipe<std.Directory>,
n: number,
): [RunnableTemplate, std.Recipe<std.Directory>, number] {
if (template == null || template === "") {
return [{ components: [] }, recipe, n];
} else if (typeof template === "string") {
return [
{ components: [{ type: "literal", value: std.bstring(template) }] },
recipe,
n,
];
} else if (Array.isArray(template)) {
const resultComponents = [];
for (const component of template) {
let result: RunnableTemplate;
[result, recipe, n] = buildTemplate(component, recipe, n);

resultComponents.push(...result.components);
}

return [{ components: resultComponents }, recipe, n];
} else if ("relativePath" in template) {
return [
{
components: [
{ type: "relative_path", path: std.bstring(template.relativePath) },
],
},
recipe,
n,
];
} else {
recipe = recipe.insert(`brioche-run.d/recipe-${n}`, template);
return [
{
components: [
{
type: "relative_path",
path: std.bstring(`brioche-run.d/recipe-${n}`),
},
],
},
recipe,
n + 1,
];
}
}
61 changes: 61 additions & 0 deletions projects/std/runnable_tools.bri
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import * as std from "/core";

export function runtimeUtils(): std.Recipe<std.Directory> {
return std
.download({
url: "https://development-content.brioche.dev/github.com/brioche-dev/brioche-runtime-utils/commits/fc48540924428944e30a4901c2fff2135abf5342/x86_64-linux/brioche-runtime-utils.tar.zstd",
hash: std.sha256Hash(
"c1ea132ed08abd7f719a698cdc32e2444e6f5ca0792c7737d42a41771a04cfa7",
),
})
.unarchive("tar", "zstd");
}

interface RunnableData {
command: RunnableTemplate;
args: RunnableTemplate[];
env: Record<string, RunnableTemplate>;
}

export function makeRunnableExecutable(
data: RunnableData,
): std.Recipe<std.File> {
return std
.process({
command: runtimeUtils().get("bin/runnable"),
args: [
"make-runnable",
"--runnable",
runtimeUtils().get("bin/start-runnable"),
"--output",
std.outputPath,
"--runnable-data",
JSON.stringify(data),
],
})
.cast("file");
}

// function template(template: RunnableTemplate): RunnableTemplateJson {
// return {
// components: [template].flat().map((component) => {
// if (Array.isArray(component)) {
// throw new Error("Expected template to be flattened");
// }

// if (typeof component === "string") {
// return { type: "literal", value: std.tickEncode(component) };
// } else {
// return { type: "relative_path", path: std.tickEncode(component.path) };
// }
// }),
// };
// }

export interface RunnableTemplate {
components: RunnableTemplateComponent[];
}

export type RunnableTemplateComponent =
| { type: "literal"; value: std.BString }
| { type: "relative_path"; path: std.BString };

0 comments on commit a12eb0f

Please sign in to comment.