Skip to content

feat: track requested packages in a manifest to fix dep resolution (#88) #330

feat: track requested packages in a manifest to fix dep resolution (#88)

feat: track requested packages in a manifest to fix dep resolution (#88) #330

Workflow file for this run

name: ci
on:
push:
branches:
- main
pull_request:
concurrency:
group: ${{ github.ref }}
cancel-in-progress: true
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: denolib/setup-deno@v2
with:
deno-version: v2.x
- run: deno fmt --check .
- run: deno lint .
- run: deno check ./pkgm.ts
#TODO test on linux! we are currently broken due to rpath issues
# https://github.com/pkgxdev/pkgm/pull/30#issuecomment-2678957666
test:
continue-on-error: true
strategy:
matrix:
os:
- macos-latest
- ubuntu-latest
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: pkgxdev/setup@v4
- run: ./pkgm.ts i git
- run: ~/.local/bin/git --version
- run: "! test -f /usr/local/bin/git"
- run: ./pkgm.ts ls | grep .local/pkgs/git-scm.org
- run: ./pkgm.ts rm git
- run: test ! -f ~/.local/bin/git
- run: ./pkgm.ts i pkgx.sh/brewkit
- run: ~/.local/bin/bk --help
# check repeats work
- run: rm ~/.local/bin/bk
- run: test ! -f /usr/local/bin/bk
- run: ./pkgm.ts i pkgx.sh/brewkit
- run: ~/.local/bin/bk --help
- run: ./pkgm.ts i gum
- run: ~/.local/bin/gum --version
# test a thing with deps
# https://github.com/pkgxdev/pkgm/issues/24
- run: ./pkgm.ts i curl
- run: ~/.local/bin/curl -L pkgx.sh
- run: ./pkgm.ts shim semverator
- run: ~/.local/bin/semverator validate 1.0.0
# tests shims do not shim deps
- run: ./pkgm.ts shim node@20
- run: test ! -d ~/.local/bin/openssl
- run: if [[ $(~/.local/bin/node --version) != v20* ]]; then false; fi
# `@1.18` records a `~1.18` pin in the manifest, so `update` must respect
# it and NOT cross to 1.19…
- run: ./pkgm.ts i hyperfine@1.18
- run: if pkgx semverator satisfies '>=1.19' "$(hyperfine --version | cut -f 2 -d ' ')"; then false; fi
- run: ./pkgm.ts update
- run: if pkgx semverator satisfies '>=1.19' "$(hyperfine --version | cut -f 2 -d ' ')"; then false; fi
# …until the pin is widened to `*`, after which `update` tracks latest.
# `-i.bak` (not bare `-i`) so it's portable: GNU and BSD/macOS sed both
# take the suffix here; the leftover .bak file is harmless.
- run: sed -i.bak 's/= "~1.18"/= "*"/' "${XDG_CONFIG_HOME:-$HOME/.config}/pkgm/manifest.toml"
- run: ./pkgm.ts outdated | grep hyperfine
- run: ./pkgm.ts update
- run: pkgx semverator satisfies '>=1.19' "$(hyperfine --version | cut -f 2 -d ' ')"
# TODO pending: https://github.com/pkgxdev/pantry/issues/8487
# - run: ./pkgm.ts i xpra.org # https://github.com/pkgxdev/pkgm/issues/13
# - run: ls -la /usr/local/pkgs/xpra.org/v6.2.3/venv/bin
# - run: xpra --version
# verifies that libpkgx is creating the pantry at the right place
# Refs: https://github.com/pkgxdev/pkgm/issues/59
- run: |
./pkgm.ts i semverator
if test -d /tmp/foo/pkgx; then
test $(uname) = Linux
else
test $(uname) = Darwin
fi
env:
XDG_DATA_HOME: /tmp/foo
- run: |
set -x
sudo ./pkgm.ts i node@22 dev
[[ $(node --version) = v22* ]] || exit 2
mkdir foo
cd foo
echo "dependencies: node@20" > pkgx.yaml
mkdir -p /tmp/bar/pkgx/dev$PWD/
touch /tmp/bar/pkgx/dev$PWD/dev.pkgx.activated # `dev .` doesn’t work in CI (fix in dev^2)
[[ $(node --version) = v20* ]] || exit 3
env:
XDG_DATA_HOME: /tmp/bar
# https://github.com/pkgxdev/pkgm/issues/62
- run: |
./pkgm.ts i spotify_player
spotify_player --version
# Validates `sudo pkgm install` behaviour fixed in 2b33f20:
# - privilege drop so pkgx cache stays owned by $SUDO_USER, not root
# - HOME override so the cache lands under the invoking user's tree
# - fallback to running pkgx as root when it lives under /root/.pkgx
# and is therefore unreachable to $SUDO_USER (pkgxdev/pkgm#68)
# Linux-only: the /root/.pkgx scenario doesn't arise on macOS in practice.
sudo-install:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pkgxdev/setup@v4
- name: sudo install drops privileges and overrides HOME
run: |
set -eux
# The pkgm.ts shebang makes pkgx fetch a private deno (and unzip, to
# unpack deno’s .zip) the first time it runs. Under sudo that bootstrap
# is the kernel-invoked `pkgx … deno run` executing as root with
# HOME=/root, so it lands in /root/.pkgx before any pkgm code runs —
# pkgm can’t relocate it. Warm it before the marker so the checks below
# scope only to what the install itself creates.
sudo ./pkgm.ts --version
# marker to scope ownership checks to files created by this install
touch /tmp/pkgm-sudo-marker
sudo ./pkgm.ts i hyperfine
test -x /usr/local/bin/hyperfine
# HOME override: pkgx must not have created anything under /root/.pkgx
# during this install. We scope the check to paths newer than the
# marker so a pre-existing /root/.pkgx from the runner image or
# setup action does not cause a false failure.
created_under_root=$(sudo find /root/.pkgx -newer /tmp/pkgm-sudo-marker -print 2>/dev/null || true)
if [ -n "$created_under_root" ]; then
echo "::error::pkgx cached under /root/.pkgx — HOME override failed"
echo "$created_under_root"
exit 1
fi
# Privilege drop: nothing newly created under ~/.pkgx should be
# owned by root. Any root-owned file here means pkgx ran as root
# despite SUDO_USER being set.
owned_by_root=$(sudo find "$HOME/.pkgx" -newer /tmp/pkgm-sudo-marker -user root -print 2>/dev/null || true)
if [ -n "$owned_by_root" ]; then
echo "::error::pkgx cache files created as root under \$HOME/.pkgx:"
echo "$owned_by_root"
exit 1
fi
- name: sudo install falls back when pkgx is unreachable as $SUDO_USER
# Must be last — this step strips pkgx from every location the
# runner user can reach, leaving only /root/.pkgx, which the
# subsequent shebang resolution still needs to walk through sudo.
run: |
set -eux
# Stage pkgx exclusively under /root so that reachable_as() returns
# false for the runner user and no alternative is found.
pkgx_src=$(command -v pkgx)
sudo mkdir -p /root/.pkgx/bin
sudo cp "$pkgx_src" /root/.pkgx/bin/pkgx
# Wipe every alternative the resolver looks for:
# ~/.pkgx/pkgx.sh/v*/bin/pkgx, ~/.local/bin/pkgx, /usr/local/bin/pkgx
rm -rf "$HOME/.pkgx"
sudo rm -f /usr/local/bin/pkgx "$HOME/.local/bin/pkgx"
# Invoke pkgm.ts with the /root pkgx on PATH. `sudo env PATH=...`
# is the canonical way around the default secure_path policy in
# Ubuntu's sudoers.
set +e
out=$(sudo env PATH="/root/.pkgx/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" ./pkgm.ts i gum 2>&1)
rc=$?
set -e
echo "$out"
# Regression check for pkgxdev/pkgm#68: the install succeeds without
# crashing when only the sudo-only pkgx remains reachable.
test $rc -eq 0
test -x /usr/local/bin/gum
# Exercises the manifest behaviour added for pkgxdev/pkgm#88. macOS mirrors
# where the issue was reported and avoids the linux rpath caveats above.
manifest:
runs-on: macos-latest
env:
MANIFEST: ${{ github.workspace }}/.config/pkgm/manifest.toml
XDG_CONFIG_HOME: ${{ github.workspace }}/.config
steps:
- uses: actions/checkout@v4
- uses: pkgxdev/setup@v4
# install records intent (requested → major-lock) and marks deps `dep`.
- name: install records intent and deps
run: |
set -eux
./pkgm.ts i curl
test -f "$MANIFEST"
cat "$MANIFEST"
grep -qE '"curl.se" = "\^[0-9]' "$MANIFEST" # requested → ^major
grep -q '= "dep"' "$MANIFEST" # transitive deps → dep
# no blank lines between entries (only the one after the header)
test "$(grep -c '^$' "$MANIFEST")" -eq 1
# deleting the manifest then reinstalling recreates it.
- name: manifest is recreated
run: |
set -eux
rm -f "$MANIFEST"
./pkgm.ts i curl
test -f "$MANIFEST"
grep -q '= "dep"' "$MANIFEST"
# uninstall drops the package's entry.
- name: uninstall removes the entry
run: |
set -eux
./pkgm.ts rm curl
if grep -q '"curl.se"' "$MANIFEST"; then
echo "curl.se was not removed"; cat "$MANIFEST"; exit 1
fi
# issue #88: conflicting transitive deps must never surface as an uncaught
# exception — in either install order.
- name: node then uv keeps outdated/update alive
run: |
set -eux
rm -rf "$HOME/.local" "$XDG_CONFIG_HOME/pkgm"
./pkgm.ts i node
./pkgm.ts i uv
out=$(./pkgm.ts outdated 2>&1 || true)
echo "$out"
if echo "$out" | grep -q "Uncaught"; then
echo "outdated raised an uncaught exception"; exit 1
fi
- name: uv then node keeps outdated/update alive
run: |
set -eux
rm -rf "$HOME/.local" "$XDG_CONFIG_HOME/pkgm"
./pkgm.ts i uv
./pkgm.ts i node
out=$(./pkgm.ts outdated 2>&1 || true)
echo "$out"
if echo "$out" | grep -q "Uncaught"; then
echo "outdated raised an uncaught exception"; exit 1
fi
# #88 root cause: a later install must resolve against the whole requested
# set (via the manifest), so python's strict zlib pin (pulled in by uv)
# wins in either order — node, whose constraint is loose, must not relink
# zlib to whatever it picked in isolation. derived from uv, not hardcoded,
# so it survives the pantry moving the pin.
- name: shared dep settles on the strict pin in either order
run: |
set -eux
zver() {
basename "$(dirname "$(dirname "$(readlink "$HOME/.local/lib/libz.dylib")")")"
}
reset() { rm -rf "$HOME/.local" "$XDG_CONFIG_HOME/pkgm"; }
# the version uv (→ python) requires; this must win throughout.
reset; ./pkgm.ts i uv >/dev/null
pin=$(zver); echo "uv pins zlib to $pin"
./pkgm.ts i node >/dev/null
test "$(zver)" = "$pin" # adding node must not relink zlib
# other order must converge to the same pin
reset; ./pkgm.ts i node >/dev/null; ./pkgm.ts i uv >/dev/null
test "$(zver)" = "$pin"