diff --git a/.ghjk/lock.json b/.ghjk/lock.json index aeb7365..cad9504 100644 --- a/.ghjk/lock.json +++ b/.ghjk/lock.json @@ -5,8 +5,14 @@ "ports": { "version": "0", "configResolutions": { + "bciqay4m4kmzfduj5t2clgejxgpe5zwper6lyyaxt7rhbjalaqd32nhq": { + "version": "2.34.1", + "buildDepConfigs": {}, + "portRef": "git_aa@0.1.0", + "specifiedVersion": false + }, "bciqjlw6cxddajjmznoemlmnu7mgbbm7a3hfmnd2x5oivwajmiqui5ey": { - "version": "v0.2.63", + "version": "v0.2.64", "buildDepConfigs": {}, "portRef": "act_ghrel@0.1.0", "specifiedVersion": false @@ -15,16 +21,16 @@ "version": "3.7.1", "buildDepConfigs": { "cpy_bs_ghrel": { - "version": "3.12.3", + "version": "3.12.4", "buildDepConfigs": { "tar_aa": { - "version": "1.35", + "version": "1.34", "buildDepConfigs": {}, "portRef": "tar_aa@0.1.0", "specifiedVersion": false }, "zstd_aa": { - "version": "v1.5.6,", + "version": "v1.4.8,", "buildDepConfigs": {}, "portRef": "zstd_aa@0.1.0", "specifiedVersion": false @@ -39,16 +45,16 @@ "specifiedVersion": false }, "bciqij3g6mmbjn4a6ps4eipcy2fmw2zumgv5a3gbxycthroffihwquoi": { - "version": "3.12.3", + "version": "3.12.4", "buildDepConfigs": { "tar_aa": { - "version": "1.35", + "version": "1.34", "buildDepConfigs": {}, "portRef": "tar_aa@0.1.0", "specifiedVersion": false }, "zstd_aa": { - "version": "v1.5.6,", + "version": "v1.4.8,", "buildDepConfigs": {}, "portRef": "zstd_aa@0.1.0", "specifiedVersion": false @@ -58,13 +64,13 @@ "specifiedVersion": false }, "bciqj4p5hoqweghbuvz52rupja7sqze34z63dd62nz632c5zxikv6ezy": { - "version": "1.35", + "version": "1.34", "buildDepConfigs": {}, "portRef": "tar_aa@0.1.0", "specifiedVersion": false }, "bciqe6fwheayositrdk7rkr2ngdr4wizldakex23tgivss7w6z7g3q3y": { - "version": "v1.5.6,", + "version": "v1.4.8,", "buildDepConfigs": {}, "portRef": "zstd_aa@0.1.0", "specifiedVersion": false diff --git a/.github/workflows/autoupdate.yml b/.github/workflows/autoupdate.yml index 56c7e4e..4f55938 100644 --- a/.github/workflows/autoupdate.yml +++ b/.github/workflows/autoupdate.yml @@ -10,7 +10,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 - uses: browniebroke/pre-commit-autoupdate-action@main - - uses: peter-evans/create-pull-request@v6 + - uses: peter-evans/create-pull-request@v7 with: token: ${{ secrets.GITHUB_TOKEN }} branch: update/pre-commit-hooks diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index b192524..fdb034a 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -66,6 +66,6 @@ jobs: env: GHJKFILE: ./examples/protoc/ghjk.ts - run: | - cd examples/protoc + cd examples/tasks . $(ghjk print share-dir-path)/env.sh - protoc --version + ghjk x hey diff --git a/.gitignore b/.gitignore index 260ff42..6e7b8fb 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,13 @@ play.* examples/**/.ghjk .dev + +deps/https +node_analysis_* +v8_code_cache_* +dep_analysis_* +gen +npm +deno.land +jsr.io +esm.sh diff --git a/README.md b/README.md index fbb4193..70c34cd 100644 --- a/README.md +++ b/README.md @@ -30,9 +30,9 @@ ghjk was designed to be an intermediate alternative between [Earthly](https://gi ```bash # stable -curl -fsSL https://raw.githubusercontent.com/metatypedev/ghjk/0.2.0/install.sh | bash +curl -fsSL https://raw.githubusercontent.com/metatypedev/ghjk/v0.2.1/install.sh | bash # latest (main) -curl -fsSL https://raw.githubusercontent.com/metatypedev/ghjk/0.2.0/install.sh | GHJK_VERSION=main bash/fish/zsh +curl -fsSL https://raw.githubusercontent.com/metatypedev/ghjk/v0.2.1/install.sh | GHJK_VERSION=main bash/fish/zsh ``` In your project, create a configuration file called `ghjk.ts` that look something like: @@ -41,7 +41,7 @@ In your project, create a configuration file called `ghjk.ts` that look somethin // NOTE: All the calls in your `ghjk.ts` file are ultimately modifying the 'sophon' proxy // object exported here. // WARN: always import `hack.ts` file first -export { sophon } from "https://raw.githubusercontent.com/metatypedev/ghjk/0.2.0/hack.ts"; +export { sophon } from "https://raw.githubusercontent.com/metatypedev/ghjk/v0.2.1/hack.ts"; import { install, task, @@ -70,9 +70,9 @@ Ghjk is primarily configured through constructs called "environments" or "envs" They serve as recipes for making (mostly) reproducable posix shells. ```ts -export { sophon } from "https://raw.githubusercontent.com/metatypedev/ghjk/0.2.0/hack.ts"; -import * as ghjk from "https://raw.githubusercontent.com/metatypedev/ghjk/0.2.0/hack.ts"; -import * as ports from "https://raw.githubusercontent.com/metatypedev/ghjk/0.2.0/ports/mod.ts"; +export { sophon } from "https://raw.githubusercontent.com/metatypedev/ghjk/v0.2.1/hack.ts"; +import * as ghjk from "https://raw.githubusercontent.com/metatypedev/ghjk/v0.2.1/hack.ts"; +import * as ports from "https://raw.githubusercontent.com/metatypedev/ghjk/v0.2.1/ports/mod.ts"; // top level `install`s go to the `main` env ghjk.install(ports.protoc()); diff --git a/deno.jsonc b/deno.jsonc index a8ece53..65dde40 100644 --- a/deno.jsonc +++ b/deno.jsonc @@ -1,6 +1,7 @@ { "tasks": { "test": "deno test --parallel --unstable-worker-options --unstable-kv -A tests/*", + "self": "deno run -A --unstable-kv --unstable-worker-options main.ts ", "cache": "deno cache deps/*", "check": "deno run -A ./scripts/check.ts", "dev": "deno run -A ./scripts/dev.ts" diff --git a/deno.lock b/deno.lock index 5ab46e0..53455e8 100644 --- a/deno.lock +++ b/deno.lock @@ -651,6 +651,8 @@ "https://deno.land/x/json_hash@0.2.0/mod.ts": "b0fdd79a540d3fc6aa3e0a9a93fe6735b1a174d9ba2aba103e4a18ee4872acad", "https://deno.land/x/jszip@0.11.0/mod.ts": "5661ddc18e9ac9c07e3c5d2483bc912a7022b6af0d784bb7b05035973e640ba1", "https://deno.land/x/jszip@0.11.0/types.ts": "1528d1279fbb64dd118c371331c641a3a5eff2b594336fb38a7659cf4c53b2d1", + "https://deno.land/x/object_hash@2.0.3.1/index.ts": "74b20a0065dc0066c60510174626db1d18e53ec966edb6f76fa33a67aa0c44e3", + "https://deno.land/x/object_hash@2.0.3.1/mod.ts": "648559bcafb54b930d4b6a283cc2eef20afa54de471371a97c2ccf8116941148", "https://deno.land/x/object_hash@2.0.3/index.ts": "74b20a0065dc0066c60510174626db1d18e53ec966edb6f76fa33a67aa0c44e3", "https://deno.land/x/object_hash@2.0.3/mod.ts": "648559bcafb54b930d4b6a283cc2eef20afa54de471371a97c2ccf8116941148", "https://deno.land/x/zod@v3.22.4/ZodError.ts": "4de18ff525e75a0315f2c12066b77b5c2ae18c7c15ef7df7e165d63536fdf2ea", diff --git a/examples/env_vars/ghjk.ts b/examples/env_vars/ghjk.ts new file mode 100644 index 0000000..e670d44 --- /dev/null +++ b/examples/env_vars/ghjk.ts @@ -0,0 +1,21 @@ +import { file } from "../../hack.ts"; + +const ghjk = file({ + defaultEnv: "empty", + envs: [{ name: "empty", inherit: false }], + defaultBaseEnv: "empty", + allowedBuildDeps: [], + installs: [], + stdDeps: true, + enableRuntimes: true, + tasks: {}, +}); + +export const sophon = ghjk.sophon; +const { env, task } = ghjk; + +env("main") + .var("A", "A#STATIC") + .var("C", ($) => $`echo C [$A, $B]`.text()) + .var("B", () => "B#DYNAMIC") + .onEnter(task(($) => $`echo enter $A, $B, $C`)); diff --git a/files/mod.ts b/files/mod.ts index 9fd5acf..44c0322 100644 --- a/files/mod.ts +++ b/files/mod.ts @@ -36,12 +36,13 @@ import * as std_modules from "../modules/std.ts"; import type { ExecTaskArgs } from "../modules/tasks/deno.ts"; import { TaskDefHashed, TasksModuleConfig } from "../modules/tasks/types.ts"; // envs -import type { - EnvRecipe, - EnvsModuleConfig, - Provision, - WellKnownProvision, +import { + type EnvRecipe, + type EnvsModuleConfig, + type Provision, + type WellKnownProvision, } from "../modules/envs/types.ts"; +import envsValidators from "../modules/envs/types.ts"; import modulesValidators from "../modules/types.ts"; const validators = { @@ -100,7 +101,7 @@ export type TaskDefArgs = { desc?: string; dependsOn?: string | string[]; workingDir?: string | Path; - vars?: Record; + vars?: Record; // TODO: add DynEnvValue? allowedBuildDeps?: (InstallConfigFat | AllowedPortDep)[]; installs?: InstallConfigFat | InstallConfigFat[]; inherit?: EnvParent; @@ -305,7 +306,7 @@ export class Ghjkfile { workingDir, ``, ); - await task.fn(custom$, { + return await task.fn(custom$, { argv, env: Object.freeze(envVars), $: custom$, @@ -653,6 +654,15 @@ export class Ghjkfile { const prov: WellKnownProvision = { ty: "posix.envVar", key, val }; return prov; }), + ...Object.entries(final.dynVars).map(( + [key, val], + ) => { + const prov = { ty: "posix.envVarDyn", key, taskKey: val }; + return unwrapZodRes( + envsValidators.envVarDynProvision.safeParse(prov), + prov, + ); + }), // env hooks ...hooks, ], @@ -886,6 +896,7 @@ type EnvFinalizer = () => { installSetId: string; inherit: string | string[] | boolean; vars: Record; + dynVars: Record; desc?: string; onEnterHookTasks: string[]; onExitHookTasks: string[]; @@ -894,6 +905,12 @@ type EnvFinalizer = () => { export type EnvDefArgsPartial = & { name?: string } & Omit; + +export type DynEnvValue = + | (() => string | number) + | (($_: typeof $) => string | number) + | (($_: typeof $) => Promise); + // // /** // * A version of {@link EnvDefArgs} that has all container @@ -938,6 +955,7 @@ export class EnvBuilder { #file: Ghjkfile; #inherit: string | string[] | boolean = true; #vars: Record = {}; + #dynVars: Record = {}; #desc?: string; #onEnterHookTasks: string[] = []; #onExitHookTasks: string[] = []; @@ -958,6 +976,7 @@ export class EnvBuilder { vars: Object.fromEntries( Object.entries(this.#vars).map(([key, val]) => [key, val.toString()]), ), + dynVars: this.#dynVars, desc: this.#desc, onExitHookTasks: this.#onExitHookTasks, onEnterHookTasks: this.#onEnterHookTasks, @@ -991,7 +1010,7 @@ export class EnvBuilder { /** * Add an environment variable. */ - var(key: string, val: string) { + var(key: string, val: string | DynEnvValue) { this.vars({ [key]: val }); return this; } @@ -999,10 +1018,37 @@ export class EnvBuilder { /** * Add multiple environment variable. */ - vars(envVars: Record) { + vars(envVars: Record) { + const vars = {}, dynVars = {}; + for (const [k, v] of Object.entries(envVars)) { + switch (typeof v) { + case "string": + case "number": + Object.assign(vars, { [k]: v }); + break; + case "function": { + const taskKey = this.#file.addTask({ + ty: "denoFile@v1", + fn: v, + nonce: k, + }); + Object.assign(dynVars, { [k]: taskKey }); + break; + } + default: + throw new Error( + `environment value of type "${typeof v}" is not supported`, + ); + } + } + Object.assign( this.#vars, - unwrapZodRes(validators.envVars.safeParse(envVars), { envVars }), + unwrapZodRes(validators.envVars.safeParse(vars), { envVars: vars }), + ); + Object.assign( + this.#dynVars, + dynVars, ); return this; } diff --git a/ghjk.ts b/ghjk.ts index 076a8be..3560a6a 100644 --- a/ghjk.ts +++ b/ghjk.ts @@ -24,7 +24,7 @@ install( task( "lock-sed", async ($) => { - const GHJK_VERSION = "0.2.0"; + const GHJK_VERSION = "0.2.1"; await sedLock( $.path(import.meta.dirname!), { @@ -41,7 +41,7 @@ task( ], "./README.md": [ [ - /(.*\/metatypedev\/ghjk\/)[^/]*(\/.*)/, + /(.*\/metatypedev\/ghjk\/v)[^/]*(\/.*)/, GHJK_VERSION, ], ], diff --git a/host/mod.ts b/host/mod.ts index 9e39ba4..8a62b23 100644 --- a/host/mod.ts +++ b/host/mod.ts @@ -30,7 +30,7 @@ type HostCtx = { lockedFlagSet: boolean; }; -const GHJK_VERSION = "0.2.0"; +const GHJK_VERSION = "0.2.1"; export async function cli(args: CliArgs) { logger().debug(`ghjk CLI`, GHJK_VERSION); diff --git a/install.sh b/install.sh index 5869d4a..263c7a9 100755 --- a/install.sh +++ b/install.sh @@ -2,7 +2,7 @@ set -e -u -GHJK_VERSION="${GHJK_VERSION:-v0.2.0}" +GHJK_VERSION="${GHJK_VERSION:-v0.2.1}" GHJK_INSTALLER_URL="${GHJK_INSTALLER_URL:-https://raw.github.com/metatypedev/ghjk/$GHJK_VERSION/install.ts}" GHJK_SHARE_DIR="${GHJK_SHARE_DIR:-$HOME/.local/share/ghjk}" DENO_VERSION="${DENO_VERSION:-v1.44.2}" diff --git a/modules/envs/posix.ts b/modules/envs/posix.ts index 848cdad..5e775f5 100644 --- a/modules/envs/posix.ts +++ b/modules/envs/posix.ts @@ -62,6 +62,7 @@ export async function cookPosixEnv( case "posix.headerFile": includePaths.push(wellKnownProv.absolutePath); break; + // case "posix.envVarDyn": case "posix.envVar": if (vars[wellKnownProv.key]) { throw new Error( @@ -84,7 +85,7 @@ export async function cookPosixEnv( break; default: throw Error( - `unsupported provision type: ${(wellKnownProv as any).provision}`, + `unsupported provision type: ${(wellKnownProv as any).ty}`, ); } })); diff --git a/modules/envs/reducer.ts b/modules/envs/reducer.ts index ff402a3..64cfd70 100644 --- a/modules/envs/reducer.ts +++ b/modules/envs/reducer.ts @@ -1,4 +1,6 @@ import { unwrapZodRes } from "../../port.ts"; +import { execTask } from "../tasks/exec.ts"; +import { getTasksCtx } from "../tasks/inter.ts"; import type { GhjkCtx } from "../types.ts"; import type { EnvRecipeX, @@ -7,7 +9,7 @@ import type { WellKnownEnvRecipeX, WellKnownProvision, } from "./types.ts"; -import { wellKnownProvisionTypes } from "./types.ts"; +import { envVarDynTy, wellKnownProvisionTypes } from "./types.ts"; import validators from "./types.ts"; export type ProvisionReducerStore = Map< @@ -31,6 +33,10 @@ export function getProvisionReducerStore( store = new Map(); gcx.blackboard.set(id, store); } + store?.set( + envVarDynTy, + installDynEnvReducer(gcx) as ProvisionReducer, + ); return store; } @@ -85,3 +91,38 @@ export async function reduceStrangeProvisions( }; return out; } + +export function installDynEnvReducer(gcx: GhjkCtx) { + return async (provisions: Provision[]) => { + const output = []; + const badProvisions = []; + const taskCtx = getTasksCtx(gcx); + + for (const provision of provisions) { + const ty = "posix.envVar"; + const key = provision.taskKey as string; + + const taskGraph = taskCtx.taskGraph; + const taskConf = taskCtx.config; + + const targetKey = Object.entries(taskConf.tasks) + .filter(([_, task]) => task.key == key) + .shift()?.[0]; + + if (targetKey) { + // console.log("key", key, " maps to target ", targetKey); + const results = await execTask(gcx, taskConf, taskGraph, targetKey, []); + output.push({ ...provision, ty, val: results[key] as any ?? "" }); + } else { + badProvisions.push(provision); + } + } + + if (badProvisions.length >= 1) { + throw new Error("cannot deduce task from keys", { + cause: { badProvisions }, + }); + } + return output; + }; +} diff --git a/modules/envs/types.ts b/modules/envs/types.ts index adc0cda..1a0372c 100644 --- a/modules/envs/types.ts +++ b/modules/envs/types.ts @@ -21,6 +21,8 @@ export const installProvisionTypes = [ installProvisionTy, ] as const; +export const envVarDynTy = "posix.envVarDyn"; + // we separate the posix file types in a separate // array in the interest of type inference export const wellKnownProvisionTypes = [ @@ -78,9 +80,16 @@ const envsModuleConfig = zod.object({ message: `no env found under the provided "defaultEnv"`, }); +const envVarDynProvision = zod.object({ + ty: zod.literal(envVarDynTy), + key: moduleValidators.envVarName, + taskKey: zod.string(), +}); + const validators = { provision, wellKnownProvision, + envVarDynProvision, envRecipe, envsModuleConfig, wellKnownEnvRecipe, diff --git a/modules/ports/mod.ts b/modules/ports/mod.ts index e532caa..382b82d 100644 --- a/modules/ports/mod.ts +++ b/modules/ports/mod.ts @@ -108,7 +108,6 @@ export class PortsModule extends ModuleBase { installSetProvisionTy, installSetReducer(gcx) as ProvisionReducer, ); - return pcx; } diff --git a/modules/tasks/deno.ts b/modules/tasks/deno.ts index f6ca449..680a43e 100644 --- a/modules/tasks/deno.ts +++ b/modules/tasks/deno.ts @@ -28,7 +28,7 @@ export type DriverRequests = { export type DriverResponse = { ty: "execSuccess"; - payload: boolean; + payload: { data?: unknown; status: boolean }; } | { ty: "execError"; payload: unknown; @@ -50,10 +50,10 @@ async function onMsg(msg: MessageEvent) { let res: DriverResponse; if (req.ty == "exec") { try { - await importAndExec(req.uri, req.args); + const execOutput = await importAndExec(req.uri, req.args); res = { ty: "execSuccess", - payload: true, + payload: execOutput, }; } catch (err) { res = { @@ -74,8 +74,8 @@ async function importAndExec( ) { const _shimHandle = shimDenoNamespace(args.envVars); const mod = await import(uri); - await mod.sophon.execTask(args); - return true; + const ret = await mod.sophon.execTask(args); + return { data: ret, status: true }; } async function rpc(moduleUri: string, req: DriverRequests) { @@ -130,7 +130,7 @@ export async function execTaskDeno( args, }); if (resp.ty == "execSuccess") { - // + return resp.payload.data; } else if (resp.ty == "execError") { throw resp.payload; } else { diff --git a/modules/tasks/exec.ts b/modules/tasks/exec.ts index 313014e..6e03213 100644 --- a/modules/tasks/exec.ts +++ b/modules/tasks/exec.ts @@ -91,15 +91,15 @@ export async function execTask( args: string[], // taskEnv: TaskEnvX, // installGraph: InstallGraph, -): Promise { +): Promise> { let workSet = new Set([targetKey]); { const stack = [targetKey]; while (stack.length > 0) { const taskHash = stack.pop()!; const taskDef = tasksConfig.tasks[taskHash]; - stack.push(...taskDef.dependsOn ?? []); - workSet = new Set([...workSet.keys(), ...taskDef.dependsOn ?? []]); + stack.push(...(taskDef?.dependsOn ?? [])); + workSet = new Set([...workSet.keys(), ...(taskDef?.dependsOn ?? [])]); } } const pendingDepEdges = new Map( @@ -109,6 +109,8 @@ export async function execTask( if (pendingTasks.length == 0) { throw new Error("something went wrong, task graph starting set is empty"); } + + const output = {} as Record; while (pendingTasks.length > 0) { const taskKey = pendingTasks.pop()!; const taskDef = tasksConfig.tasks[taskKey]; @@ -152,6 +154,8 @@ export async function execTask( ), ), }; + + let taskOutput: unknown | undefined; try { if (taskDef.ty == "denoFile@v1") { if (!gcx.ghjkfilePath) { @@ -160,7 +164,7 @@ export async function execTask( ); } const workingDir = gcx.ghjkfilePath.parentOrThrow(); - await execTaskDeno( + taskOutput = await execTaskDeno( gcx.ghjkfilePath.toFileUrl().toString(), { key: taskDef.key, @@ -214,8 +218,13 @@ export async function execTask( } } pendingTasks.push(...readyTasks); + + Object.assign(output, { [taskDef.key]: taskOutput }); } + if (workSet.size > 0) { throw new Error("something went wrong, task graph work set is not empty"); } + + return output; } diff --git a/ports/cargobi.ts b/ports/cargobi.ts index aa25da7..845bb07 100644 --- a/ports/cargobi.ts +++ b/ports/cargobi.ts @@ -22,6 +22,7 @@ import * as std_ports from "../modules/ports/std.ts"; import { ghConfValidator, GithubReleasesInstConf, + readGhVars, } from "../modules/ports/ghrel.ts"; import rust, { RustInstallConf } from "./rust.ts"; @@ -55,15 +56,17 @@ export type CargobiInstallConf = export default function conf(config: CargobiInstallConf) { const { rustConfOverride, ...thisConf } = config; const out: InstallConfigFat = { + ...readGhVars(), ...confValidator.parse(thisConf), - buildDepConfigs: { + port: manifest, + }; + if (rustConfOverride) { + out.buildDepConfigs = { [std_ports.rust_rustup.name]: thinInstallConfig(rust({ - profile: "minimal", ...rustConfOverride, })), - }, - port: manifest, - }; + }; + } return out; } diff --git a/ports/cmake.ts b/ports/cmake.ts index b0f96a8..99fcea1 100644 --- a/ports/cmake.ts +++ b/ports/cmake.ts @@ -25,7 +25,7 @@ import * as ports from "./mod.ts"; * */ export default function conf( - config: InstallConfigSimple, + config: InstallConfigSimple = {}, ): InstallConfigFat[] { /* The universal macOS cmake build downloaded by asdf crashes diff --git a/ports/mod.ts b/ports/mod.ts index 926d6d7..d2833cd 100644 --- a/ports/mod.ts +++ b/ports/mod.ts @@ -16,6 +16,7 @@ export { default as node } from "./node.ts"; export { default as npmi } from "./npmi.ts"; export { default as opentofu_ghrel } from "./opentofu_ghrel.ts"; export { default as pipi } from "./pipi.ts"; +export { default as poetry } from "./poetry.ts"; export { default as pnpm } from "./pnpm.ts"; export { default as protoc } from "./protoc.ts"; export { default as ruff } from "./ruff.ts"; diff --git a/ports/pipi.ts b/ports/pipi.ts index f7aa81c..7299279 100644 --- a/ports/pipi.ts +++ b/ports/pipi.ts @@ -31,6 +31,10 @@ export const manifest = { const confValidator = zod.object({ packageName: zod.string().regex(/[a-z0-9._-]*/), + peerDeps: zod.array(zod.object({ + name: zod.string(), + version: zod.string().nullish(), + })).nullish(), }).passthrough(); export type PipiInstallConf = @@ -98,9 +102,14 @@ export class Port extends PortBase { conf.packageName, args.installVersion, ); + + const dependencies = conf.peerDeps?.map((dep) => ( + dep.version ? [dep.name, dep.version].join("==") : dep.name + )) ?? []; + await $`${ depExecShimPath(std_ports.cpy_bs_ghrel, "python3", args.depArts) - } -m pip -qq install ${conf.packageName}==${args.installVersion}` + } -m pip -qq install ${conf.packageName}==${args.installVersion} ${dependencies}` .env( { ...depPathEnvs, diff --git a/ports/poetry.ts b/ports/poetry.ts new file mode 100644 index 0000000..9b68ae7 --- /dev/null +++ b/ports/poetry.ts @@ -0,0 +1,70 @@ +import { + defaultLatestStable, + InstallConfigSimple, + osXarch, + zod, +} from "../port.ts"; +import { Port as PipiPort } from "./pipi.ts"; +import { + ALL_ARCH, + ALL_OS, + DownloadArgs, + InstallArgs, + InstallConfigLiteX, + ListAllArgs, +} from "../modules/ports/types.ts"; +import * as std_ports from "../modules/ports/std.ts"; + +export const manifest = { + ty: "denoWorker@v1" as const, + name: "poetry", + version: "0.1.0", + moduleSpecifier: import.meta.url, + buildDeps: [std_ports.cpy_bs_ghrel], + platforms: osXarch([...ALL_OS], [...ALL_ARCH]), +}; + +const confValidator = zod.object({ + plugins: zod.array(zod.object({ + name: zod.string(), + version: zod.string().nullish(), + })).nullish(), +}).passthrough(); + +export type PoetryInstallConf = + & InstallConfigSimple + & zod.input; + +export default function conf(config: PoetryInstallConf = {}) { + return { + ...config, + port: manifest, + }; +} + +const toPipiConfig = (config: InstallConfigLiteX) => ({ + ...config, + packageName: "poetry", + peerDeps: config.plugins, +}); + +export class Port extends PipiPort { + listAll(args: ListAllArgs) { + return super.listAll({ ...args, config: toPipiConfig(args.config) }); + } + + latestStable(args: ListAllArgs): Promise { + return defaultLatestStable(this, { + ...args, + config: toPipiConfig(args.config), + }); + } + + download(args: DownloadArgs) { + return super.download({ ...args, config: toPipiConfig(args.config) }); + } + + install(args: InstallArgs) { + return super.install({ ...args, config: toPipiConfig(args.config) }); + } +} diff --git a/ports/rust.ts b/ports/rust.ts index bda15be..2819f8b 100644 --- a/ports/rust.ts +++ b/ports/rust.ts @@ -70,8 +70,14 @@ export type RustInstallConf = & InstallConfigSimple & zod.input; +/** + * Uses {@link import("./rustup.ts").conf} to install a rust toolchain. + * + * Defaults to the minimal profile installation of + */ export default function conf(config: RustInstallConf = {}) { return { + profile: "minimal", ...config, port: manifest, }; diff --git a/tests/tasks.ts b/tests/tasks.ts index 5e35475..d704094 100644 --- a/tests/tasks.ts +++ b/tests/tasks.ts @@ -142,6 +142,29 @@ task({ stdin: ` ghjk x eddy test (cat eddy) = 'ed edd eddy' +`, + }, + { + name: "dyn_vars", + ghjkTs: ` +import { file } from "$ghjk/hack.ts"; + +const ghjk = file({}); + +export const sophon = ghjk.sophon; +const { env, task } = ghjk; + +env("main") + .var("A", "A#STATIC") + .var("B", () => "B#DYNAMIC") + .var("D", ($) => $\`echo $A, $B > output.txt\`.text()) + .onEnter(task({ fn: ($) => $\`echo enter\` })) // not executing in test env? +`, + ePoint: `fish`, + stdin: ` +ghjk sync main +cat output.txt +test (cat output.txt) = 'A#STATIC, B#DYNAMIC' `, }, ]; diff --git a/utils/mod.ts b/utils/mod.ts index 30f2d51..8dfb4c0 100644 --- a/utils/mod.ts +++ b/utils/mod.ts @@ -201,7 +201,7 @@ let requestBuilder; try { requestBuilder = new dax.RequestBuilder() .showProgress(Deno.stderr.isTerminal()) - .timeout(Deno.env.get("GHJK_REQ_TIMEOUT") as any ?? "1m"); + .timeout(Deno.env.get("GHJK_REQ_TIMEOUT") as any ?? "5m"); } catch (err) { throw new Error( `invalid timeout param on GHJK_REQ_TIMEOUT: ${ @@ -425,6 +425,7 @@ export async function downloadFile( await $.request(url) .header(headers) + .timeout(undefined) .pipeToPath(tmpFilePath, { create: true, mode }); await $.path(downloadPath).ensureDir(); @@ -631,3 +632,36 @@ export async function detectShell(): Promise { ? std_path.basename(shellPath, ".exe").toLowerCase().trim() : undefined; } + +export function isInWorkerContext() { + return typeof WorkerGlobalScope !== "undefined" && + self instanceof WorkerGlobalScope; +} + +// /** +// * Blocks the event loop till the promise is resolved +// */ +// export function deasyncPromise(promise: Promise) { +// // Note: npm:deasync does not work on deno +// // TODO: better impl or use a lib? +// const sleepSync = (timeout: number) => { +// Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, timeout); +// }; + +// let resolved = false; +// let ret: T | undefined, err; + +// promise +// .then((r) => { ret = r; }) +// .catch((e) => { err = e; }) +// .finally(() => { resolved = true; }); + +// while (!resolved) { +// sleepSync(100); +// } + +// if (err) { +// throw err; +// } +// return ret; +// }