diff --git a/README.md b/README.md index c4398d4..e6392ba 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,66 @@ dev, across your team and in production. ## Getting Started +Since `dev` v1.7.0 we integrate with `pkgm` and this is the recommended way to +use `dev`. + +> [!IMPORTANT] +> +> `dev` must be installed to `/usr/local/bin/dev` for this route to work: +> +> ```sh +> sudo pkgm install dev # use of `shim` is also fine +> ``` + +```sh +$ cd my-project +$ ls +package.json + +$ node --version +command not found: node + +$ sudo pkgm install dev node +$ node --version && which node +v23.11.0 +/usr/local/bin/node + +$ cat package.json | jq .engines +{ + "node": "^20" +} + +$ dev +activated `~/my-project` (+node^20) + +$ node --version && which node +v20.19.0 +/usr/local/bin/node + +$ cd .. +$ node --version && which node +v23.11.0 +/usr/local/bin/node + +$ cd - +$ node --version && which node +v20.19.0 +/usr/local/bin/node +``` + +`pkgm` installs `dev`-aware packages to `/usr/local/bin`. Provided you have +`/usr/local/bin/dev` installed and you have activated `dev` in your project +directories the `node` that is invoked is swapped out _when invoked_. + +This is the recommended way to use `dev` because it works everywhere and not +just the terminal. + +## `dev` via Shellcode + +Shellcode works _as well_ and is your preference. It has notable caveats with +regard to use in tools like editors. It also requires you to add shellcode to +your `shell.rc` files and thus is more intrusive (depending on your outlook). + ```sh pkgx dev integrate ``` @@ -78,6 +138,7 @@ command not found: node > ```sh > $ cd my-project > $ eval "$(pkgx dev)" +> +deno^2 > ``` > > The devenv will only exist for the duration of your shell session. @@ -166,8 +227,8 @@ environment. We recommend Visual Studio Code, `dev && code .` works great. - uses: pkgxdev/dev@v1 ``` -Installs needed packages and sets up the environment the same as `dev` does in -your terminal. +Installs needed packages (via `pkgx`) and sets up the environment the same as +`dev` does in your terminal. ## Contributing diff --git a/action.js b/action.js index 6ba0255..2cc6a36 100644 --- a/action.js +++ b/action.js @@ -21,12 +21,16 @@ const replaceEnvVars = (str) => { return value; }; +let found = false; + readInterface.on("line", (line) => { + if (!found) found = line.trim() == "set -a"; + if (!found) return; const match = line.match(/^([^=]+)=(.*)$/); if (match) { const [_, key, value_] = match; const value = stripQuotes(value_); - if (key === "PATH") { + if (key.trim() === "PATH") { value .replaceAll("${PATH:+:$PATH}", "") .replaceAll("$PATH", "") diff --git a/action.yml b/action.yml index 97174e0..f7ea17c 100644 --- a/action.yml +++ b/action.yml @@ -4,12 +4,13 @@ description: inputs: path: + description: path that should be evaluated by `dev` required: false runs: using: composite steps: - - uses: pkgxdev/setup@v3 + - uses: pkgxdev/setup@v4 - run: | TMP="$(mktemp)" @@ -20,10 +21,10 @@ runs: shell: bash - run: | - if ! node --version >/dev/null 2>&1; then - set -a - eval "$(pkgx +node)" - set +a + if ! command -v node >/dev/null 2>&1; then + node() { + pkgx node^20 "$@" + } fi node "$GITHUB_ACTION_PATH"/action.js ${{ steps.env.outputs.file }} shell: bash diff --git a/app.ts b/app.ts index ffcbb17..4ff1e7c 100755 --- a/app.ts +++ b/app.ts @@ -1,24 +1,27 @@ -#!/usr/bin/env -S pkgx deno^2 run -A +#!/usr/bin/env -S pkgx --quiet deno^2 run -A //TODO if you step into dev-dir/subdir and type `dev` does it find the root properly? //TODO dev off uses PWD which may not be correct if in subdir (obv) import { Path } from "libpkgx"; -import shellcode from "./src/shellcode().ts"; +import shellcode, { datadir } from "./src/shellcode().ts"; import app_version from "./src/app-version.ts"; import integrate from "./src/integrate.ts"; -import { parse } from "jsr:@std/flags"; +import { parseArgs } from "jsr:@std/cli@^1/parse-args"; import dump from "./src/dump.ts"; +import sniff from "./src/sniff.ts"; -const parsedArgs = parse(Deno.args, { +const parsedArgs = parseArgs(Deno.args, { alias: { n: "dry-run", "just-print": "dry-run", recon: "dry-run", v: "version", h: "help", + q: "quiet", }, - boolean: ["help", "version", "shellcode"], + collect: ["quiet"], + boolean: ["help", "version", "shellcode", "quiet"], default: { "dry-run": false, }, @@ -26,7 +29,7 @@ const parsedArgs = parse(Deno.args, { if (parsedArgs.help) { const status = await new Deno.Command("pkgx", { - args: ["gh", "repo", "view", "pkgxdev/dev"], + args: ["--quiet", "gh", "repo", "view", "pkgxdev/dev"], }).spawn().status; Deno.exit(status.code); } else if (parsedArgs.shellcode) { @@ -36,6 +39,7 @@ if (parsedArgs.help) { } else { const subcommand = parsedArgs._[0]; const dryrun = parsedArgs["dry-run"] as boolean; + const quiet = parsedArgs["quiet"] != undefined; switch (subcommand) { case "integrate": await integrate("install", { dryrun }); @@ -43,9 +47,23 @@ if (parsedArgs.help) { case "deintegrate": await integrate("uninstall", { dryrun }); break; + case "status": + { + const cwd = Path.cwd(); + if ( + datadir().join(cwd.string.slice(1), "dev.pkgx.activated").isFile() + ) { + //FIXME probably slower than necessary + const { pkgs } = await sniff(cwd); + Deno.exit(pkgs.length == 0 ? 1 : 0); + } else { + Deno.exit(1); + } + } + break; default: { const cwd = Path.cwd().join(subcommand as string); - await dump(cwd, { dryrun }); + await dump(cwd, { dryrun, quiet }); } } } diff --git a/deno.json b/deno.json index f2c9b1f..39e8daf 100644 --- a/deno.json +++ b/deno.json @@ -12,7 +12,7 @@ }, "imports": { "libpkgx": "https://raw.githubusercontent.com/pkgxdev/libpkgx/refs/tags/v0.21.0/mod.ts", - "libpkgx/": "https://raw.githubusercontent.com/pkgxdev/libpkgx/refs/tags/v0.20.1/src/", + "libpkgx/": "https://raw.githubusercontent.com/pkgxdev/libpkgx/refs/tags/v0.21.0/src/", "is-what": "https://deno.land/x/is_what@v4.1.15/src/index.ts", "outdent": "https://deno.land/x/outdent@v0.8.0/mod.ts" } diff --git a/deno.lock b/deno.lock index 8576c98..96d12a1 100644 --- a/deno.lock +++ b/deno.lock @@ -5,6 +5,7 @@ "jsr:@std/assert@0.224": "0.224.0", "jsr:@std/assert@^1.0.6": "1.0.6", "jsr:@std/bytes@^1.0.2": "1.0.2", + "jsr:@std/cli@1": "1.0.11", "jsr:@std/crypto@1": "1.0.3", "jsr:@std/encoding@1": "1.0.5", "jsr:@std/flags@*": "0.224.0", @@ -40,6 +41,9 @@ "@std/bytes@1.0.2": { "integrity": "fbdee322bbd8c599a6af186a1603b3355e59a5fb1baa139f8f4c3c9a1b3e3d57" }, + "@std/cli@1.0.11": { + "integrity": "ec219619fdcd31bcf0d8e53bee1e2706ec9a02f70255365a094f69755dadd340" + }, "@std/crypto@1.0.3": { "integrity": "a2a32f51ddef632d299e3879cd027c630dcd4d1d9a5285d6e6788072f4e51e7f" }, diff --git a/src/dump.ts b/src/dump.ts index fe0c90d..62ca976 100644 --- a/src/dump.ts +++ b/src/dump.ts @@ -2,7 +2,10 @@ import { Path, utils } from "libpkgx"; import sniff from "./sniff.ts"; import shell_escape from "./shell-escape.ts"; -export default async function (cwd: Path, opts: { dryrun: boolean }) { +export default async function ( + cwd: Path, + opts: { dryrun: boolean; quiet: boolean }, +) { const snuff = await sniff(cwd); if (snuff.pkgs.length === 0 && Object.keys(snuff.env).length === 0) { @@ -20,7 +23,7 @@ export default async function (cwd: Path, opts: { dryrun: boolean }) { if (snuff.pkgs.length > 0) { const cmd = new Deno.Command("pkgx", { - args: [...pkgspecs], + args: ["--quiet", ...pkgspecs], stdout: "piped", env: { CLICOLOR_FORCE: "1" }, // unfortunate }).spawn(); @@ -54,7 +57,9 @@ export default async function (cwd: Path, opts: { dryrun: boolean }) { " ", ); - console.error("%c%s", "color: green", pkgspecs.join(" ")); + if (!opts.quiet) { + console.error("%c%s", "color: green", pkgspecs.join(" ")); + } console.log(` eval "_pkgx_dev_try_bye() { diff --git a/src/shellcode().ts b/src/shellcode().ts index a1f4a34..3293bd9 100644 --- a/src/shellcode().ts +++ b/src/shellcode().ts @@ -1,10 +1,6 @@ import { Path } from "libpkgx"; export default function shellcode() { - const datadir = new Path( - Deno.env.get("XDG_DATA_HOME")?.trim() || platform_data_home_default(), - ).join("pkgx", "dev"); - // find self const dev_cmd = Deno.env.get("PATH")?.split(":").map((path) => Path.abs(path)?.join("dev") @@ -17,9 +13,9 @@ export default function shellcode() { _pkgx_chpwd_hook() { if ! type _pkgx_dev_try_bye >/dev/null 2>&1 || _pkgx_dev_try_bye; then dir="$PWD" - while [ "$dir" != "/" ]; do - if [ -f "${datadir}/$dir/dev.pkgx.activated" ]; then - eval "$(${dev_cmd})" + while [ "$dir" != / -a "$dir" != . ]; do + if [ -f "${datadir()}/$dir/dev.pkgx.activated" ]; then + eval "$(${dev_cmd})" "$dir" break fi dir="$(dirname "$dir")" @@ -31,7 +27,7 @@ dev() { case "$1" in off) if type -f _pkgx_dev_try_bye >/dev/null 2>&1; then - rm "${datadir}$PWD/dev.pkgx.activated" + rm "${datadir()}$PWD/dev.pkgx.activated" PWD=/ _pkgx_dev_try_bye else echo "no devenv" >&2 @@ -40,8 +36,8 @@ dev() { if [ "$2" ]; then "${dev_cmd}" "$@" elif ! type -f _pkgx_dev_try_bye >/dev/null 2>&1; then - mkdir -p "${datadir}$PWD" - touch "${datadir}$PWD/dev.pkgx.activated" + mkdir -p "${datadir()}$PWD" + touch "${datadir()}$PWD/dev.pkgx.activated" eval "$(${dev_cmd})" else echo "devenv already active" >&2 @@ -74,6 +70,12 @@ fi `.trim(); } +export function datadir() { + return new Path( + Deno.env.get("XDG_DATA_HOME")?.trim() || platform_data_home_default(), + ).join("pkgx", "dev"); +} + function platform_data_home_default() { const home = Path.home(); switch (Deno.build.os) {