name: PMG E2E Tests on: pull_request: branches: - main push: branches: - main workflow_dispatch: 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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - name: Setup Go uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5 with: go-version-file: go.mod - name: Setup Node.js uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: node-version: 20 check-latest: true - name: Setup PNPM uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4 with: version: 10 - name: Setup Bun uses: oven-sh/setup-bun@735343b667d3e6f658f44d0eca948eb6282f2b76 # v2 with: bun-version: latest - name: Setup Python uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 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.rc File is Created run: | test -f $HOME/.pmg.rc - name: Test NPM - Single Package & Manifest run: | echo "Testing NPM single package installation..." mkdir npm-test && cd npm-test pmg npm init -y pmg npm install express@5.2.1 pmg 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 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 - Experimental Proxy Mode run: | echo "Testing NPM with experimental proxy-based interception..." mkdir npm-proxy-test && cd npm-proxy-test pmg npm init -y echo "Testing proxy mode single package installation..." pmg --experimental-proxy-mode npm install express@5.2.1 pmg --experimental-proxy-mode 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 --experimental-proxy-mode 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 --experimental-proxy-mode 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 --experimental-proxy-mode --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 PNPM - Single Package & Manifest run: | echo "Testing PNPM single package installation..." mkdir pnpm-test && cd pnpm-test pmg pnpm init pmg pnpm add express@5.2.1 pmg 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 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 bun init -y pmg bun add express@5.2.1 pmg 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 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 yarn init -y pmg yarn add express@5.2.1 pmg 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 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 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 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 --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 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 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 --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 pip install requests==2.32.4 pmg pip install numpy==2.3.5 pmg 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 pip uninstall -y requests numpy pmg 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 pip3 install requests==2.32.4 pmg pip3 install numpy==2.3.5 pmg 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 pip3 uninstall -y requests numpy pmg 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 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 "Testing UV manifest installation..." rm -rf .venv uv.lock pmg uv sync # Verification: uv lockfile and virtualenv created; packages present test -d .venv test -f uv.lock pmg uv pip show requests >/dev/null pmg uv pip show numpy >/dev/null echo "Testing UV pip commands..." pmg uv pip freeze > requirements.txt pmg uv pip install -r requirements.txt pmg uv pip sync requirements.txt # Verification: uv pip can show installed packages after requirements sync pmg uv pip show requests >/dev/null pmg 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 poetry init --name poetry-test --no-interaction --quiet pmg poetry add requests==2.32.4 pmg 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 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 npm init -y ! pmg 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 --experimental-proxy-mode 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 --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 --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 --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 --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 --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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - name: Setup Go uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5 with: go-version-file: go.mod - name: Setup Node.js uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: node-version: 20 check-latest: true - 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 - name: Run Sandbox E2E Test run: pmg --sandbox --sandbox-enforce npm exec -- node test/sandbox-e2e.js sandbox-e2e-linux: name: Sandbox E2E - Linux (Bubblewrap) runs-on: ubuntu-latest timeout-minutes: 10 defaults: run: shell: bash steps: - name: Checkout Source uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - name: Setup Go uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5 with: go-version-file: go.mod - name: Setup Node.js uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: node-version: 20 check-latest: true - 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 - 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