Files
pmg/.github/workflows/pmg-e2e.yml
T
6546116e28 feat: migrate PMG to use PATH shims for package manager wrapper (#246)
* feat: add FilterPMGFromPath utility for PATH shim recursion prevention

* feat: add FilterPMGFromEnv to filter PATH from env slices

* feat: filter ~/.pmg/bin from PATH in proxy subprocess env

* feat: add PathExport method to Shell interface for shim PATH integration

* feat: add ShimManager for PATH shim install/remove lifecycle

* feat: wire ShimManager into setup commands with --use-aliases fallback

* refactor: add DefaultShimConfig helper to reduce setup boilerplate

* fix: resolve real binary path to prevent shim double-invocation

exec.CommandContext resolves the binary using the current process PATH,
which still contains ~/.pmg/bin. This caused pmg to launch the shim
instead of the real package manager, resulting in a second pmg instance
with its own proxy — producing duplicate error messages and wasted work.

ResolveRealBinary searches a filtered PATH (without ~/.pmg/bin) to find
the real package manager binary before execution.

* fix: resolve real binary in runner.Execute and expand path resolution tests

Ensure guard mode and proxy skip paths also resolve through
ResolveRealBinary to prevent infinite shim recursion. Add table-driven
tests covering error cases, multi-binary PATH, and PATH restoration.

* fix: handle error return values from os.Setenv and file Close calls

Address errcheck lint failures: check os.Setenv returns in
ResolveRealBinary, and check f.Close/tempFile.Close in ShimManager.

* feat: auto-migrate shell aliases to PATH shims on setup install

When running `pmg setup install`, detect existing shell aliases and
automatically remove them before installing shims. Existing users
get a seamless migration with no extra flags or commands needed.

* fix: update E2E test to verify shim installation instead of alias RC file

Replace the .pmg.rc file check with assertions that ~/.pmg/bin/ exists
and contains executable shim scripts for npm and pip.

* feat: add FilterPMGFromPath utility for PATH shim recursion prevention

* feat: add FilterPMGFromEnv to filter PATH from env slices

* feat: filter ~/.pmg/bin from PATH in proxy subprocess env

* feat: add PathExport method to Shell interface for shim PATH integration

* feat: add ShimManager for PATH shim install/remove lifecycle

* feat: wire ShimManager into setup commands with --use-aliases fallback

* refactor: add DefaultShimConfig helper to reduce setup boilerplate

* fix: resolve real binary path to prevent shim double-invocation

exec.CommandContext resolves the binary using the current process PATH,
which still contains ~/.pmg/bin. This caused pmg to launch the shim
instead of the real package manager, resulting in a second pmg instance
with its own proxy — producing duplicate error messages and wasted work.

ResolveRealBinary searches a filtered PATH (without ~/.pmg/bin) to find
the real package manager binary before execution.

* fix: resolve real binary in runner.Execute and expand path resolution tests

Ensure guard mode and proxy skip paths also resolve through
ResolveRealBinary to prevent infinite shim recursion. Add table-driven
tests covering error cases, multi-binary PATH, and PATH restoration.

* fix: handle error return values from os.Setenv and file Close calls

Address errcheck lint failures: check os.Setenv returns in
ResolveRealBinary, and check f.Close/tempFile.Close in ShimManager.

* feat: auto-migrate shell aliases to PATH shims on setup install

When running `pmg setup install`, detect existing shell aliases and
automatically remove them before installing shims. Existing users
get a seamless migration with no extra flags or commands needed.

* fix: update E2E test to verify shim installation instead of alias RC file

Replace the .pmg.rc file check with assertions that ~/.pmg/bin/ exists
and contains executable shim scripts for npm and pip.

* feat: install both aliases and shims for full coverage

Aliases win in interactive shells (including venvs), shims catch
non-interactive contexts (IDEs, CI, subprocesses). Remove --use-aliases
flag and migration logic since both are always installed together.
Update E2E to verify all shim scripts and alias RC file.

* feat: address review feedback for shim implementation

- Install both aliases and shims together for full coverage
- Move homeDir resolution into NewDefaultShimManager (internal concern)
- Add mutex to ResolveRealBinary to guard against concurrent PATH mutation
- Use filepath.SplitList for platform-correct PATH splitting
- Add ResolveRealBinary to runner.Execute and proxy flow to prevent
  shim recursion in all execution paths
- Remove print side-effects from ShimManager.Remove
- Update E2E to verify all shim scripts and alias RC file
- Expand ResolveRealBinary tests with table-driven cases

* fix: restore errcheck handling and add concurrency test for ResolveRealBinary

- Restore proper defer with log.Warnf for PATH restoration in ResolveRealBinary
- Restore errcheck handling for f.Close() and tempFile.Close() in ShimManager
- Add explanatory comment for ResolveRealBinary call in proxy_flow
- Add TestResolveRealBinaryConcurrent to verify mutex guards concurrent access

* feat: skip shell integration on Windows with informative warning

On Windows, pmg setup install now writes only the config file and
prints a warning that shell aliases and PATH shims require WSL.

* fix: PMG use pre-resolved binary path (#253)

---------

Co-authored-by: Abhisek Datta <abhisek.datta@gmail.com>
2026-05-12 22:27:05 +05:30

701 lines
25 KiB
YAML

name: PMG E2E Tests
on:
pull_request:
branches:
- main
push:
branches:
- main
workflow_dispatch:
# PRs share a concurrency group (cancel/serialize); Pushes use unique groups to avoid cancellation/queuing
concurrency:
group: ${{ github.workflow }}-${{ (github.event_name == 'pull_request' && github.ref) || github.run_id }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
permissions:
contents: read
jobs:
e2e-test:
name: PMG E2E Tests - All Package Managers
runs-on: ${{ matrix.os }}
timeout-minutes: 20
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest]
defaults:
run:
shell: bash
steps:
- name: Checkout Source
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
- name: Setup Go
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6
with:
go-version-file: go.mod
- name: Setup Node.js
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5
with:
node-version: 20
check-latest: true
- name: Setup PNPM
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5
with:
version: 10
- name: Setup Bun
uses: oven-sh/setup-bun@735343b667d3e6f658f44d0eca948eb6282f2b76 # v2
with:
bun-version: latest
- name: Setup Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
with:
python-version: "3.11"
- name: Setup uv
uses: astral-sh/setup-uv@caf0cab7a618c569241d31dcd442f54681755d39
- name: Install Poetry
uses: snok/install-poetry@76e04a911780d5b312d89783f7b1cd627778900a
- name: Build PMG
run: make
- name: Add pmg to PATH
run: echo "$GITHUB_WORKSPACE/bin" >> $GITHUB_PATH
- name: Setup PMG
run: pmg setup install
- name: Test PMG - Config File is Created
run: |
test -f $HOME/.config/safedep/pmg/config.yml
- name: Test PMG Aliases and Shims are Installed
run: |
test -f $HOME/.pmg.rc
test -d $HOME/.pmg/bin
for shim in npm pip pip3 pnpm bun uv yarn poetry npx pnpx; do
test -x $HOME/.pmg/bin/$shim || { echo "Missing shim: $shim"; exit 1; }
done
- name: Test NPM - Single Package & Manifest
run: |
echo "Testing NPM single package installation..."
mkdir npm-test && cd npm-test
pmg --proxy-mode=false npm init -y
pmg --proxy-mode=false npm install express@5.2.1
pmg --proxy-mode=false npm install lodash@4.17.21
# Verification: npm added packages present and manifest updated
test -d node_modules/express
test -d node_modules/lodash
grep -q '"express"' package.json
grep -q '"lodash"' package.json
echo "Testing NPM manifest installation..."
rm -rf node_modules package-lock.json
pmg --proxy-mode=false npm install
# Verification: npm lockfile and installed modules exist after manifest install
test -f package-lock.json
test -d node_modules/express
test -d node_modules/lodash
cd .. && rm -rf npm-test
- name: Test NPM - Proxy Mode
run: |
echo "Testing NPM with proxy-based interception..."
mkdir npm-proxy-test && cd npm-proxy-test
pmg npm init -y
echo "Testing proxy mode single package installation..."
pmg npm install express@5.2.1
pmg npm install lodash@4.17.21
# Verification: packages installed via proxy mode
test -d node_modules/express
test -d node_modules/lodash
grep -q '"express"' package.json
grep -q '"lodash"' package.json
echo "Testing proxy mode manifest installation..."
rm -rf node_modules package-lock.json
pmg npm install
# Verification: manifest install via proxy mode works
test -f package-lock.json
test -d node_modules/express
test -d node_modules/lodash
echo "Testing proxy mode with scoped package..."
pmg npm install @types/node@18.0.0
# Verification: scoped package installed via proxy
test -d node_modules/@types
test -d node_modules/@types/node
grep -q '"@types/node"' package.json
echo "Testing proxy mode dry-run (should not create files)..."
rm -rf node_modules package-lock.json
pmg --dry-run npm install
# Verification: dry-run doesn't create files even in proxy mode
test ! -d node_modules
test ! -f package-lock.json
cd .. && rm -rf npm-proxy-test
- name: Test PyPI - Proxy Mode
run: |
echo "Testing PyPI package managers with proxy-based interception..."
mkdir pypi-proxy-test && cd pypi-proxy-test
echo "Setting up Python virtual environment for pip and pip3 tests..."
python -m venv venv && source venv/bin/activate
python --version
pip --version
echo "Testing pip single package installation via proxy mode..."
pmg pip install requests==2.32.4
pmg pip install numpy==2.3.5
# Verification: packages installed and importable
python -c "import requests, numpy; print('pip ok:', requests.__version__, numpy.__version__)"
echo "Testing pip manifest installation via proxy mode..."
pmg pip freeze > requirements.txt
pmg pip uninstall -y requests numpy
pmg pip install -r <(grep -v '^PMG:' requirements.txt)
python -c "import requests, numpy; print('pip manifest ok:', requests.__version__, numpy.__version__)"
deactivate
echo "Setting up Python virtual environment for pip3 tests..."
python -m venv venv3 && source venv3/bin/activate
python --version
pip3 --version
echo "Testing pip3 single package installation via proxy mode..."
pmg pip3 install requests==2.32.4
pmg pip3 install numpy==2.3.5
# Verification: packages installed and importable
python -c "import requests, numpy; print('pip3 ok:', requests.__version__, numpy.__version__)"
echo "Testing pip3 manifest installation via proxy mode..."
pmg pip3 freeze > requirements3.txt
pmg pip3 uninstall -y requests numpy
pmg pip3 install -r <(grep -v '^PMG:' requirements3.txt)
python -c "import requests, numpy; print('pip3 manifest ok:', requests.__version__, numpy.__version__)"
deactivate
echo "Testing uv add and uv pip install via proxy mode..."
mkdir uv-proxy && cd uv-proxy
pmg uv init --no-readme
pmg uv add requests==2.32.4
pmg uv add numpy==2.3.5
# Verification: pyproject.toml lists expected dependencies
test -f pyproject.toml
grep -q 'requests' pyproject.toml
grep -q 'numpy' pyproject.toml
echo "Sync environment and verify installations..."
pmg uv sync
pmg uv pip show requests >/dev/null
pmg uv pip show numpy >/dev/null
echo "Testing uv pip install from requirements via proxy mode..."
pmg uv pip freeze > requirements.txt
pmg uv pip install -r <(grep -v '^PMG:' requirements.txt)
pmg uv pip show requests >/dev/null
pmg uv pip show numpy >/dev/null
cd ..
cd .. && rm -rf pypi-proxy-test
- name: Test PNPM - Single Package & Manifest
run: |
echo "Testing PNPM single package installation..."
mkdir pnpm-test && cd pnpm-test
pmg --proxy-mode=false pnpm init
pmg --proxy-mode=false pnpm add express@5.2.1
pmg --proxy-mode=false pnpm add lodash@4.17.21
# Verification: pnpm packages installed and lockfile created
test -d node_modules/express
test -d node_modules/lodash
test -f pnpm-lock.yaml
echo "Testing PNPM manifest installation..."
rm -rf node_modules pnpm-lock.yaml
pmg --proxy-mode=false pnpm install
# Verification: pnpm lockfile and modules exist after manifest install
test -f pnpm-lock.yaml
test -d node_modules/express
test -d node_modules/lodash
cd .. && rm -rf pnpm-test
- name: Test Bun - Single Package & Manifest
run: |
echo "Testing Bun single package installation..."
mkdir bun-test && cd bun-test
pmg --proxy-mode=false bun init -y
pmg --proxy-mode=false bun add express@5.2.1
pmg --proxy-mode=false bun add lodash@4.17.21
# Verification: bun packages installed and lockfile created
test -d node_modules/express
test -d node_modules/lodash
test -f bun.lock
echo "Testing Bun manifest installation..."
rm -rf node_modules bun.lock
pmg --proxy-mode=false bun install
# Verification: bun lockfile and modules exist after manifest install
test -f bun.lock
test -d node_modules/express
test -d node_modules/lodash
cd .. && rm -rf bun-test
- name: Test Yarn - Single Package & Manifest
run: |
echo "Testing Yarn single package installation..."
export YARN_ENABLE_HARDENED_MODE=0
npm install -g yarn@1.22.22
yarn --version
mkdir yarn-test && cd yarn-test
pmg --proxy-mode=false yarn init -y
pmg --proxy-mode=false yarn add express@5.2.1
pmg --proxy-mode=false yarn add lodash@4.17.21
# Verification: yarn packages installed and lockfile created
test -d node_modules/express
test -d node_modules/lodash
test -f yarn.lock
echo "Testing Yarn manifest installation..."
rm -rf node_modules yarn.lock
pmg --proxy-mode=false yarn install
# Verification: yarn lockfile and modules exist after manifest install
test -f yarn.lock
test -d node_modules/express
test -d node_modules/lodash
cd .. && rm -rf yarn-test
- name: Test NPX - Package Execution
run: |
echo "Testing NPX package execution..."
mkdir npx-test && cd npx-test
echo "Testing npx with a simple package..."
pmg --proxy-mode=false npx cowsay@1.6.0 "Hello from pmg npx" | tee npx-output.txt
# Verification: cowsay output contains our message
grep -q "Hello from pmg npx" npx-output.txt
echo "Testing npx with --package flag..."
pmg --proxy-mode=false npx --package cowsay@1.6.0 -- cowsay "Hello with package flag" | tee npx-pkg-output.txt
# Verification: package flag execution produces expected output
grep -q "Hello with package flag" npx-pkg-output.txt
echo "Testing npx dry-run mode..."
pmg --proxy-mode=false --dry-run npx cowsay@1.6.0 "This should not execute" | tee npx-dry-output.txt
# Verification: dry-run should NOT produce cowsay ASCII art (cow face ^__^ should not appear)
! grep -q '\^__\^' npx-dry-output.txt
cd .. && rm -rf npx-test
- name: Test PNPX - Package Execution
run: |
echo "Testing PNPX package execution..."
mkdir pnpx-test && cd pnpx-test
echo "Testing pnpx with a simple package..."
pmg --proxy-mode=false pnpx cowsay@1.6.0 "Hello from pmg pnpx" | tee pnpx-output.txt
# Verification: cowsay output contains our message
grep -q "Hello from pmg pnpx" pnpx-output.txt
echo "Testing pnpx with --package flag..."
pmg --proxy-mode=false pnpx --package cowsay@1.6.0 -- cowsay "Hello with package flag" | tee pnpx-pkg-output.txt
# Verification: package flag execution produces expected output
grep -q "Hello with package flag" pnpx-pkg-output.txt
echo "Testing pnpx dry-run mode..."
pmg --proxy-mode=false --dry-run pnpx cowsay@1.6.0 "This should not execute" | tee pnpx-dry-output.txt
# Verification: dry-run should NOT produce cowsay ASCII art (cow face ^__^ should not appear)
! grep -q '\^__\^' pnpx-dry-output.txt
cd .. && rm -rf pnpx-test
- name: Test Pip - Single Package & Manifest
run: |
echo "Testing Pip single package installation..."
mkdir pip-test && cd pip-test
python -m venv venv && source venv/bin/activate
pmg --proxy-mode=false pip install requests==2.32.4
pmg --proxy-mode=false pip install numpy==2.3.5
pmg --proxy-mode=false pip freeze > requirements.txt
# Verification: requirements.txt contains expected packages
test -s requirements.txt
grep -E '^requests==' requirements.txt
grep -E '^numpy==' requirements.txt
echo "Testing Pip manifest installation..."
pmg --proxy-mode=false pip uninstall -y requests numpy
pmg --proxy-mode=false pip install -r requirements.txt
# Verification: imported packages are available in the environment
python -c "import requests, numpy; print(requests.__version__); print(numpy.__version__)"
deactivate
cd .. && rm -rf pip-test
- name: Test Pip3 - Single Package & Manifest
run: |
echo "Testing Pip3 single package installation..."
mkdir pip3-test && cd pip3-test
python -m venv venv && source venv/bin/activate
pmg --proxy-mode=false pip3 install requests==2.32.4
pmg --proxy-mode=false pip3 install numpy==2.3.5
pmg --proxy-mode=false pip3 freeze > requirements.txt
# Verification: requirements.txt contains expected packages
test -s requirements.txt
grep -E '^requests==' requirements.txt
grep -E '^numpy==' requirements.txt
echo "Testing Pip3 manifest installation..."
pmg --proxy-mode=false pip3 uninstall -y requests numpy
pmg --proxy-mode=false pip3 install -r requirements.txt
# Verification: imported packages are available in the environment
python -c "import requests, numpy; print(requests.__version__); print(numpy.__version__)"
deactivate
cd .. && rm -rf pip3-test
- name: Test UV - Single Package & Manifest
run: |
echo "Testing UV single package installation..."
mkdir uv-test && cd uv-test
pmg --proxy-mode=false uv init --no-readme
pmg --proxy-mode=false uv add requests==2.32.4
pmg --proxy-mode=false uv add numpy==2.3.5
# Verification: pyproject.toml lists expected dependencies
test -f pyproject.toml
grep -q 'requests' pyproject.toml
grep -q 'numpy' pyproject.toml
echo "Testing UV manifest installation..."
rm -rf .venv uv.lock
pmg --proxy-mode=false uv sync
# Verification: uv lockfile and virtualenv created; packages present
test -d .venv
test -f uv.lock
pmg --proxy-mode=false uv pip show requests >/dev/null
pmg --proxy-mode=false uv pip show numpy >/dev/null
echo "Testing UV pip commands..."
pmg --proxy-mode=false uv pip freeze > requirements.txt
pmg --proxy-mode=false uv pip install -r requirements.txt
pmg --proxy-mode=false uv pip sync requirements.txt
# Verification: uv pip can show installed packages after requirements sync
pmg --proxy-mode=false uv pip show requests >/dev/null
pmg --proxy-mode=false uv pip show numpy >/dev/null
cd .. && rm -rf uv-test
- name: Test Poetry - Single Package & Manifest
run: |
echo "Testing Poetry single package installation..."
mkdir poetry-test && cd poetry-test
pmg --proxy-mode=false poetry init --name poetry-test --no-interaction --quiet
pmg --proxy-mode=false poetry add requests==2.32.4
pmg --proxy-mode=false poetry add numpy==2.3.5
# Verification: pyproject.toml dependencies updated
test -f pyproject.toml
grep -q 'requests' pyproject.toml
grep -q 'numpy' pyproject.toml
echo "Testing Poetry manifest installation..."
rm -rf .venv poetry.lock
pmg --proxy-mode=false poetry install --no-root
cd .. && rm -rf poetry-test
- name: Test Malicious Package Detection
run: |
echo "Testing malicious package detection..."
mkdir malicious-test && cd malicious-test
pmg --proxy-mode=false npm init -y
! pmg --proxy-mode=false npm install nyc-config@10.0.0 || echo "Malicious package correctly blocked"
cd .. && rm -rf malicious-test
- name: Test safedep-test-pkg is Blocked using Proxy mode
run: |
echo "Testing that safedep-test-pkg is blocked..."
mkdir safedep-test-pkg-test && cd safedep-test-pkg-test
pmg npm init -y
# Attempt to install safedep-test-pkg - should fail
if pmg npm --no-cache --prefer-online i safedep-test-pkg@0.1.3; then
echo "ERROR: safedep-test-pkg was not blocked!"
exit 1
else
echo "SUCCESS: safedep-test-pkg correctly blocked"
fi
# Verify package is not installed locally
if [ -d "node_modules/safedep-test-pkg" ]; then
echo "ERROR: safedep-test-pkg found in node_modules!"
exit 1
else
echo "SUCCESS: safedep-test-pkg not present in node_modules"
fi
cd .. && rm -rf safedep-test-pkg-test
- name: Test PMG Modes
run: |
echo "Testing different PMG modes..."
mkdir pmg-modes-test && cd pmg-modes-test
pmg npm init -y
# Mode: --dry-run should not create node_modules or lockfiles
pmg --proxy-mode=false --dry-run npm install express
# Verification: no files created during dry-run
test ! -d node_modules
test ! -f package-lock.json
# Mode: --silent should install without noisy output
pmg --proxy-mode=false --silent npm install express
# Verification: package installed
test -d node_modules/express
# Clean and test --verbose installation
rm -rf node_modules package-lock.json
pmg --proxy-mode=false --verbose npm install express
# Verification: package installed
test -d node_modules/express
# Clean and test --debug with log output
rm -rf node_modules package-lock.json
pmg --proxy-mode=false --debug --log debug.json npm install express
# Verification: debug log written
test -f debug.json
# Mode: --paranoid may require cloud credentials; run non-blocking with dry-run
pmg --proxy-mode=false --paranoid --dry-run npm install express || true
cd .. && rm -rf pmg-modes-test
sandbox-e2e-macos:
name: Sandbox E2E - macOS
runs-on: macos-latest
timeout-minutes: 10
steps:
- name: Checkout Source
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
- name: Setup Go
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6
with:
go-version-file: go.mod
- name: Setup Node.js
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5
with:
node-version: 20
check-latest: true
- name: Setup PNPM
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5
with:
version: 10
- name: Build PMG
run: make
- name: Add pmg to PATH
run: echo "$GITHUB_WORKSPACE/bin" >> $GITHUB_PATH
- name: Setup PMG
run: pmg setup install
- name: Create Test Directories for Sandbox Permissions Tests
run: mkdir -p ~/.aws ~/.gcloud ~/.kube ~/.ssh
- name: Create Test Files for Sandbox Permissions Tests
run: |
touch ~/.aws/credentials
touch ~/.gcloud/credentials.json
touch ~/.kube/config
touch ~/.ssh/id_rsa
touch ./.env
- name: Run Sandbox E2E Test
run: pmg --sandbox --sandbox-enforce npm exec -- node test/sandbox-e2e.js
- name: Run Package Manager E2E Test
run: pmg --sandbox --sandbox-enforce npm exec -- node test/pm-e2e.js
sandbox-e2e-linux:
name: Sandbox E2E - Linux (Bubblewrap)
runs-on: ubuntu-latest
timeout-minutes: 10
defaults:
run:
shell: bash
env:
PMG_SANDBOX_DRIVER: bubblewrap
steps:
- name: Checkout Source
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
- name: Setup Go
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6
with:
go-version-file: go.mod
- name: Setup Node.js
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5
with:
node-version: 20
check-latest: true
- name: Setup PNPM
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5
with:
version: 10
- name: Install Bubblewrap
run: sudo apt-get update && sudo apt-get install -y bubblewrap
- name: Verify Bubblewrap Installation
run: bwrap --version
- name: Build PMG
run: make
- name: Add pmg to PATH
run: echo "$GITHUB_WORKSPACE/bin" >> $GITHUB_PATH
- name: Setup PMG
run: pmg setup install
- name: Create Test Directories for Sandbox Permissions Tests
run: mkdir -p ~/.aws ~/.gcloud ~/.kube ~/.ssh ~/.gnupg ~/.docker
- name: Create Test Files for Sandbox Permissions Tests
run: |
touch ~/.aws/credentials
touch ~/.gcloud/credentials.json
touch ~/.kube/config
touch ~/.ssh/id_rsa
touch ~/.gnupg/pubring.kbx
touch ~/.docker/config.json
touch ./.env
- name: Disable AppArmor for Bubblewrap
run: |
sudo systemctl stop apparmor
sudo systemctl disable apparmor
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
- name: Run Sandbox E2E Test
run: pmg --sandbox --sandbox-enforce --sandbox-profile npm-restrictive npm exec -- node test/sandbox-e2e.js
- name: Run Package Manager E2E Test
run: pmg --sandbox --sandbox-enforce --sandbox-profile npm-restrictive npm exec -- node test/pm-e2e.js
sandbox-e2e-linux-landlock:
name: Sandbox E2E - Linux (Landlock)
runs-on: ubuntu-latest
timeout-minutes: 10
defaults:
run:
shell: bash
env:
PMG_SANDBOX_DRIVER: landlock
PMG_LANDLOCK_E2E: "1"
# Redirect npm's cache into /tmp so it sits outside any pre-existing
# state in /home/runner/.npm (which setup-node / the runner image may
# have populated with state the sandbox policy doesn't account for).
# The npm-restrictive profile already grants /tmp/** read+write.
npm_config_cache: /tmp/npm-cache
steps:
- name: Checkout Source
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
- name: Setup Go
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6
with:
go-version-file: go.mod
- name: Setup Node.js
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5
with:
node-version: 20
check-latest: true
- name: Setup PNPM
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5
with:
version: 10
- name: Build PMG
run: make
- name: Add pmg to PATH
run: echo "$GITHUB_WORKSPACE/bin" >> $GITHUB_PATH
- name: Setup PMG
run: pmg setup install
- name: Create Test Directories for Sandbox Permissions Tests
run: mkdir -p ~/.aws ~/.gcloud ~/.kube ~/.ssh ~/.gnupg ~/.docker
- name: Create Test Files for Sandbox Permissions Tests
run: |
touch ~/.aws/credentials
touch ~/.gcloud/credentials.json
touch ~/.kube/config
touch ~/.ssh/id_rsa
touch ~/.gnupg/pubring.kbx
touch ~/.docker/config.json
touch ./.env
- name: Disable AppArmor for User Namespaces
run: |
sudo systemctl stop apparmor
sudo systemctl disable apparmor
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
- name: Verify Landlock Available
run: |
if [ ! -d /sys/kernel/security/landlock ] && ! grep -q landlock /proc/kallsyms 2>/dev/null; then
echo "Landlock not detected by sysfs probe (continuing — driver will fail loudly if unavailable)"
fi
uname -a
- name: Run Landlock Helper E2E Tests (Go)
run: go test -count=1 -v -run TestLandlockHelper ./sandbox/platform/...
- name: Run Sandbox E2E Test
run: pmg --sandbox --sandbox-enforce --sandbox-profile npm-restrictive npm exec -- node test/sandbox-e2e.js
- name: Run Package Manager E2E Test
run: pmg --sandbox --sandbox-enforce --sandbox-profile npm-restrictive npm exec -- node test/pm-e2e.js