Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 63 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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

Expand Down
6 changes: 5 additions & 1 deletion action.js
Original file line number Diff line number Diff line change
Expand Up @@ -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", "")
Expand Down
11 changes: 6 additions & 5 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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)"
Expand All @@ -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
32 changes: 25 additions & 7 deletions app.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,35 @@
#!/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,
},
});

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) {
Expand All @@ -36,16 +39,31 @@ 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 });
break;
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 });
}
}
}
2 changes: 1 addition & 1 deletion deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
Expand Down
4 changes: 4 additions & 0 deletions deno.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 8 additions & 3 deletions src/dump.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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();
Expand Down Expand Up @@ -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() {
Expand Down
22 changes: 12 additions & 10 deletions src/shellcode().ts
Original file line number Diff line number Diff line change
@@ -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")
Expand All @@ -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")"
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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) {
Expand Down