diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f009f64..1bbb50b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -108,3 +108,70 @@ jobs: - 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 + # 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