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
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,16 @@ export YSTACK_HOME=/Users/me/Yolean/ystack
export PATH=$PATH:$YSTACK_HOME/bin
```

Note that ystach should be after system path entries because it contains fallback impls for MacOS such as `basepath` and `sha256sum`.
Note that ystack should be after system path entries because it contains fallback impls for MacOS such as `basepath` and `sha256sum`.

However you're recommended to override default binaries for some commands. Run:

```
y-yarn help
y-npx help
hash -r
y-bin-default ensure
```

## Why

Expand Down
1 change: 1 addition & 0 deletions bin/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,4 @@ osv-scanner
chrome-devtools-mcp
static-web-server
yarn
npx
136 changes: 136 additions & 0 deletions bin/y-bin-default
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
#!/usr/bin/env bash
[ -z "$DEBUG" ] || set -x
set -eo pipefail

YBIN="$(cd "$(dirname "$0")" && pwd)"

YHELP='y-bin-default - Ensure ystack-managed binaries take precedence via ~/.local/bin symlinks

Usage: y-bin-default [help] SUBCOMMAND [BIN...]

Subcommands:
check Verify symlinks exist and take precedence in PATH
ensure Create symlinks and verify precedence (fails if link exists but loses to PATH)

Arguments:
BIN Binary name (e.g. yarn). If omitted, iterates all known defaults.

Known defaults: yarn, npx

The convention is to symlink ~/.local/bin/BIN -> $YSTACK_HOME/bin/BIN so that
the ystack-managed version takes precedence without requiring PATH changes to
$YSTACK_HOME/bin itself. Users must ensure ~/.local/bin is early in their PATH.

Dependencies:

Exit codes:
0 Success
1 Usage error
2 Check or ensure failed
'

case "${1:-}" in
help) echo "$YHELP"; exit 0 ;;
--help) echo "$YHELP"; exit 0 ;;
esac

USER_BIN="$HOME/.local/bin"
KNOWN_DEFAULTS="yarn npx" # no_npx: manages the npx default symlink

subcmd="${1:-}"
# y-script-lint:disable=or-true # shift fails when no args after subcmd
shift || true

case "$subcmd" in
check|ensure) ;;
"") echo "ERROR: subcommand required (check or ensure)" >&2; exit 1 ;;
*) echo "ERROR: unknown subcommand: $subcmd (use: check, ensure)" >&2; exit 1 ;;
esac

bins=("$@")
if [ ${#bins[@]} -eq 0 ]; then
IFS=' ' read -ra bins <<< "$KNOWN_DEFAULTS"
fi

failed=0

for bin in "${bins[@]}"; do
source_bin="$YBIN/$bin"
target_link="$USER_BIN/$bin"

if [[ "$bin" == y-* ]]; then
echo "ERROR: refusing to install y- prefixed binary $bin (y-* binaries belong in ystack PATH, not ~/.local/bin)" >&2
failed=1
continue
fi

if [ ! -e "$source_bin" ] && [ ! -L "$source_bin" ]; then
echo "ERROR: $source_bin does not exist" >&2
failed=1
continue
fi

link_exists=false
link_correct=false
if [ -L "$target_link" ]; then
link_exists=true
existing_target="$(readlink "$target_link")"
if [ "$existing_target" = "$source_bin" ] || [ "$existing_target" = "$(cd "$YBIN" && pwd)/$bin" ]; then
link_correct=true
fi
elif [ -e "$target_link" ]; then
echo "ERROR: $target_link exists but is not a symlink" >&2
failed=1
continue
fi

case "$subcmd" in
check)
if [ "$link_exists" = "false" ]; then
echo "MISSING $target_link -> $source_bin"
failed=1
continue
fi
if [ "$link_correct" = "false" ]; then
echo "WRONG $target_link -> $existing_target (expected $source_bin)"
failed=1
continue
fi
;;
ensure)
if [ "$link_exists" = "true" ] && [ "$link_correct" = "false" ]; then
echo "ERROR: $target_link -> $existing_target (not managed by ystack, remove manually)" >&2
failed=1
continue
fi
if [ "$link_correct" = "false" ]; then
mkdir -p "$USER_BIN"
ln -s "$source_bin" "$target_link"
echo "CREATED $target_link -> $source_bin"
fi
;;
esac

# Check PATH precedence
# y-script-lint:disable=or-true # command -v returns 1 when bin not in PATH
resolved="$(command -v "$bin" 2>/dev/null || true)"
if [ -z "$resolved" ]; then
echo "WARN $bin not found in PATH after linking" >&2
failed=1
continue
fi

# Check that the resolved path is our symlink in USER_BIN or our bin, not some other bin earlier in PATH
if [ "$resolved" != "$target_link" ] && [ "$resolved" != "$source_bin" ]; then
echo "NO_PRECEDENCE $bin resolves to $resolved via $(dirname "$resolved")"
echo " Recommendation: ensure $USER_BIN appears before $(dirname "$resolved") in PATH" >&2
if [ "$subcmd" = "ensure" ]; then
failed=1
fi
else
echo "OK $bin -> $resolved"
fi
done

[ "$failed" -ne 0 ] && exit 2
exit 0
20 changes: 15 additions & 5 deletions bin/y-bin.runner.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -136,11 +136,21 @@ turbo:
path: turbo-${os}-${xarm}64/bin/turbo

yarn:
version: 0.1.0
version: 0.2.1
templates:
download: https://github.com/Yolean/y1/releases/download/v${version}/y1-${os}-${arch}
sha256:
darwin_amd64: bd0310a0578a53d8639b1f480dc6258920a2b063f9270943ffb9eb3d2fa97416
darwin_arm64: 8cb3f050fe85b4bc70a3ed88329ae203b6156ff420dc2e53638c50f6e22143cf
linux_amd64: 6cfe37280da7def39e661a2e48a708007b2ae1b2db206e287a808e38a0b4d2db
linux_arm64: 02a7071e09096992f8bb67b49b16bff43fbfde7af0bdee90a90d54a540d089ae
darwin_amd64: 29538a2cb2a68ec9b5da75a35e85665057efa5479c518a6e7d57c4f97569b3d3
darwin_arm64: 8ebb026be9f0bec5114691b82c67fae936f856df0598b4ab1642f1cf729936c0
linux_amd64: 16252fe8ac0b3500bd697bc47213cc438209e2d5f8a812def075a0cdec891301
linux_arm64: 05ca3451a6f78a68b08c13b4b0b582cb22220b90ccd00160bffff80224d8c50d

npx:
version: 0.2.1
templates:
download: https://github.com/Yolean/y1/releases/download/v${version}/y1-${os}-${arch}
sha256:
darwin_amd64: 29538a2cb2a68ec9b5da75a35e85665057efa5479c518a6e7d57c4f97569b3d3
darwin_arm64: 8ebb026be9f0bec5114691b82c67fae936f856df0598b4ab1642f1cf729936c0
linux_amd64: 16252fe8ac0b3500bd697bc47213cc438209e2d5f8a812def075a0cdec891301
linux_arm64: 05ca3451a6f78a68b08c13b4b0b582cb22220b90ccd00160bffff80224d8c50d
8 changes: 8 additions & 0 deletions bin/y-npx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/usr/bin/env bash
[ -z "$DEBUG" ] || set -x
set -eo pipefail
YBIN="$(cd "$(dirname "$(readlink -f "$0")")" && pwd)"

version=$(y-bin-download $YBIN/y-bin.runner.yaml npx) # no_npx

exec -a y-npx "y-npx-v${version}-bin" "$@" # no_npx
6 changes: 3 additions & 3 deletions bin/y-script-lint
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ if [ ${#TARGET_DIRS[@]} -eq 1 ]; then
elif [ -d "$arg" ]; then
: # directory, handled below
elif [[ "$arg" != */* ]]; then
# Bare name like "y-turbo" search PATH
# Bare name like "y-turbo" -search PATH
found=""
IFS=: read -ra path_entries <<< "$PATH"
for dir in "${path_entries[@]}"; do
Expand Down Expand Up @@ -158,9 +158,9 @@ check_help_handler() {
check_no_npx() {
local file="$1" lang="$2"
if is_node "$lang"; then
! grep -vE '^\s*//' "$file" 2>/dev/null | grep -qE '\bnpx\b'
! sed '/^const YHELP = `/,/^`;/d' "$file" 2>/dev/null | grep -vE '^\s*//' | grep -qE '\bnpx\b'
else
! grep -vE '^\s*#' "$file" 2>/dev/null | grep -vE '(no_npx|"uses npx"|No npx)' | grep -qE '\bnpx\b'
! sed "/^YHELP='/,/^'/d" "$file" 2>/dev/null | grep -vE '^\s*#' | grep -vE '(no_npx|"uses npx"|No npx)' | grep -qE '\bnpx\b'
fi
}

Expand Down
8 changes: 7 additions & 1 deletion runner.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ ENV YSTACK_HOME=/usr/local/src/ystack \
DO_NOT_TRACK=1 \
npm_config_update_notifier=false

FROM --platform=$TARGETPLATFORM node:24.14.0-trixie-slim@sha256:4fc981bf8dfc5e36e15e0cb73c5761a14cabff0932dcad1cf26cd3c3425db5d4 \
FROM --platform=$TARGETPLATFORM node:24.14.1-trixie-slim@sha256:c319bb4fac67c01ced508b67193a0397e02d37555d8f9b72958649efd302b7f8 \
as node

FROM base as bin
Expand All @@ -47,6 +47,12 @@ COPY bin/y-bin.runner.yaml \
bin/y-bin-dependency-download \
/usr/local/src/ystack/bin/

COPY bin/y-yarn /usr/local/src/ystack/bin/
RUN y-yarn help

COPY bin/y-npx /usr/local/src/ystack/bin/
RUN ! y-npx 2>/dev/null

COPY bin/y-kubectl /usr/local/src/ystack/bin/
RUN y-kubectl version --client=true --output=json

Expand Down
Loading