Five Tested Recipes for the Remixware Dependency Gate
These are short setups for remixware deps, the check that compares a pull request’s lockfiles with main’s. Each one was run before it went on this page. The test repository is small: its main branch holds TypeScript’s package-lock.json from just before esbuild was added, and a pull request branch holds the lockfile from just after, the same change as in the TypeScript case study. The outputs below are copied from the recorded runs in test/results/case-studies/deps/recipes.
1. A GitHub Actions Job
name: Dependency gate
on: pull_request
jobs:
deps:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install Remixware
run: |
mkdir -p "$HOME/bin"
curl -sSL https://github.com/softdevstudio/remixware/releases/latest/download/remixware_linux_amd64.tar.gz \
| tar -xz -C "$HOME/bin" ./remixware
echo "$HOME/bin" >> "$GITHUB_PATH"
- name: Check dependency changes
run: remixware deps . --base-ref "origin/${{ github.base_ref }}" --base-policy --json deps.json
- name: Keep the result
if: always()
uses: actions/upload-artifact@v4
with:
name: dependency-gate
path: deps.json
fetch-depth: 0 matters. --base-ref reads main’s lockfiles out of the local git repository, so main has to be fetched. We ran the install lines and the gate command on the test repository’s pull request branch with base_ref set to main. It downloaded 0.3.2, printed the 23 new packages and exited with 1, which is what makes the job red.
2. An Allow List With Reasons
Every rule names a package and says why it’s there. The reason is printed next to each package it lets in.
{
"deps": {
"allow": [
{"package": "npm/esbuild", "install_scripts": true, "reason": "build tool; its install script fetches the binary for this platform"},
{"package": "npm/esbuild-*", "reason": "esbuild's platform binaries (0.15 and earlier)"},
{"package": "npm/@esbuild/*", "reason": "esbuild's platform binaries"}
]
}
}
With this policy the same pull request passes:
ALLOWED npm/esbuild-windows-arm64 0.15.13, added (transitive)
in package-lock.json
New, and on the allow list: esbuild's platform binaries (0.15 and earlier)
23 changes: 23 allowed.
PASS: no changes break the policy (fail on: not-allowed, install-script, publisher-changed, not-checked).
Keep install_scripts: true for the packages whose scripts you’ve looked at. A rule without it lets the package in and still fails it for its script.
3. Pull Requests You Don’t Trust
The policy file lives in the repository, so a pull request can edit it. On the test repository, a second branch adds esbuild and also adds allow rules for it, with the reason “needed for the new build”. Checked with the pull request’s own policy, it passes, and the output says why:
Allow rules this change adds to the policy (review them: a change can allow its own packages):
npm/esbuild, install scripts too: needed for the new build
npm/esbuild-*: needed for the new build
npm/@esbuild/*: needed for the new build
23 changes: 23 allowed.
PASS: no changes break the policy (fail on: not-allowed, install-script, publisher-changed, not-checked).
Add --base-policy and the gate reads .remixware.json from main instead, so only rules that are already merged count. The same branch then fails with exit code 1 and 23 changes: 1 install-script, 22 not-allowed. Use it for pull requests from forks, and anywhere a reviewer could miss a policy edit. The job in recipe 1 already has it.
4. JSON for Other Tools
--json deps.json writes the whole result. A few jq lines cover most needs. The failing packages, one per line:
jq -r '.changes[] | select(.status != "allowed" and .status != "updated" and .status != "removed") | "\(.status)\t\(.package)\t\(.to // [] | join(","))"' deps.json
install-script npm/esbuild 0.15.13
not-allowed npm/@esbuild/android-arm 0.15.13
not-allowed npm/@esbuild/linux-loong64 0.15.13
not-allowed npm/esbuild-android-64 0.15.13
The counts per status, for a dashboard or a pull request comment, come from jq '.counts' deps.json, which gave {"install-script": 1, "not-allowed": 22} here. And jq -r '.checks[] | "\(.name): \(.status)"' deps.json says what was covered: allow-list: ran, install-scripts: ran, publisher: not-run. A tool that reports a pass should report that last line too, since a pass from a check that didn’t run means little.
5. A Monorepo With Several Lockfiles
deps finds every lockfile in the project by itself, outside node_modules, vendor, virtual environments and the policy’s ignore paths. For this recipe we built a monorepo out of the case studies’ real lockfiles: web/ with TypeScript’s npm lockfile, api/ with Hugo’s go.mod and go.sum, and ml/ with TorchServe’s requirements, where the change also adds a requirements-neuron.txt that points pip at the AWS Neuron index. One run covers all three ecosystems: 54 changes, 48 of them failing.
One policy holds the rules for all of them. Prefix each rule with its ecosystem, and say in the reason which part of the repository asked for it:
{
"deps": {
"allow": [
{"package": "npm/esbuild", "install_scripts": true, "reason": "web build tool; its install script fetches the binary for this platform"},
{"package": "npm/esbuild-*", "reason": "esbuild's platform binaries (0.15 and earlier)"},
{"package": "npm/@esbuild/*", "reason": "esbuild's platform binaries"},
{"package": "golang/golang.org/x/*", "reason": "modules of the Go project"},
{"package": "golang/google.golang.org/*", "reason": "Google's Go API, gRPC and protobuf modules"},
{"package": "golang/cloud.google.com/*", "reason": "Google Cloud client libraries"},
{"package": "golang/gocloud.dev", "reason": "api: portable cloud storage for deploys, reviewed 2026-10"},
{"package": "pypi/neuronx-cc", "reason": "ml: AWS Neuron compiler for inf2 hosts"}
]
}
}
With it, 31 of the 54 changes are allowed and 17 still fail: the Go modules nobody has looked at yet, the new Python packages, and the new package index, which no allow rule can accept.
There’s also deps.lockfiles, which replaces the search with a fixed list. When we listed web/package-lock.json, api/go.mod and ml/requirements.txt, the new ml/requirements-neuron.txt wasn’t on the list. Up to 0.3.1 the gate didn’t read it, and the index line it adds didn’t show up at all: 47 changes and no not-checked. Since 0.3.2 the gate still looks for lockfiles outside the list, and a new or changed one fails as not-checked: 48 changes, with this one among them:
NOT CHECKED ml/requirements-neuron.txt changed
ml/requirements-neuron.txt is new, and it isn't in the policy's
deps.lockfiles, so Remixware didn't read it. Add it to deps.lockfiles to check
its packages, or add it to the policy's ignore to leave it out on purpose.
So a fixed list can’t hide a new lockfile any more, but it does mean adding each new one by hand. To leave out folders such as examples, ignore is still the simpler choice, since everything outside it is searched and read.
The reference lists every option and policy key, and scripts/deps_case_studies.sh in the Remixware repository runs all five recipes again.