Contributing to apex-mutation-testing
August 18, 2026 · View on GitHub
We encourage the developer community to contribute to this repository. This guide has instructions to install, build, test and contribute to the framework.
Requirements
Installation
1) Download the repository
git clone git@github.com:scolladon/apex-mutation-testing.git
2) Install Dependencies
This will install all the tools needed to contribute
npm install
3) Build application
npm pack
Rebuild every time you made a change in the source and you need to test locally
Environment secrets
Secrets are read from the environment, never hardcoded and never committed to sfdx-project.json. Prefer tools that read a secret from the environment over ones that take it as a command-line argument, since arguments are visible to any process on the machine via ps. Where a third-party tool requires an argument anyway, pass the variable ("$MY_SECRET") rather than the literal value, so the secret stays out of shell history and out of this repository.
Locally, put them in a .env file at the repository root. It is git-ignored. Use the same variable names as the repository secrets, so a local run and a CI run resolve identically:
# .env — never commit this file
AER_LICENSE_KEY=…
Nothing loads .env automatically yet, so export it into your shell for now (set -a; . ./.env; set +a, or a tool such as direnv).
| Variable | Repository secret | Needed for |
|---|---|---|
AER_LICENSE_KEY | ✅ same name | Carrying the aer licence key into a run (see below) |
Note for CI: pull requests from forks cannot read repository secrets, so any job requiring a key must be restricted to non-fork branches or stay within the free tier.
The aer licence
The local Apex runtime (aer) reads AER_LICENSE_KEY from the environment, which is the recommended way to supply it in CI. It can also be registered once and stored on disk with aer license register <key> — prefer the environment variable, since a command-line argument is visible in ps and in CI logs.
What actually needs a licence:
aer testhas a free tier — 100 test methods per iteration, with an unlimited number of iterations. Ordinary mutation runs stay inside it, because each mutant executes only the test methods covering the mutated line. Only a baseline run over a large test perimeter can exceed it.aer serverrequires a licence. Without one it shuts down after five minutes.
The repository's key is for CI only. It is an open-source project licence, and aer validates that it is being used from a GitHub Action on a public repository — so it does not work on a developer machine, by design. Running aer server locally needs a developer licence; a trial is available by registering an email address at octoberswimmer.com.
Testing
Unit Testing
When developing, use vitest unit testing to provide test coverage for new functionality. To run the vitest tests use the following command from the root directory:
# just run test
npm run test:unit
To execute a particular test file or run tests matching a pattern, use the following commands:
# run a single test file
npx vitest run test/unit/path/to/file.test.ts
# run tests matching a pattern
npx vitest run -t "pattern"
NUT Testing
When developing, use Vitest NUT tests to provide command-level functional coverage with a mocked org. To run the NUT tests use the following command from the root directory:
# run test
npm run test:nut
E2E Testing
E2E tests run the full mutation testing pipeline against a real Salesforce org, produce an HTML report, and validate the output against a committed snapshot (test/e2e/index.html).
# Run locally (uses node ./bin/run.js + apex-mutation-testing org)
npm run test:e2e:run:local
# Validate snapshot matches
npm run test:e2e:validate
The validation step uses git diff --quiet test/e2e/ — any difference from the committed snapshot fails the check.
Known Flaky Mutant
The MemberVariableMutator mutation on String label = '' (line 2 of Mutation.cls) is non-deterministic. The Salesforce org sometimes returns CompileError and sometimes Survived for this mutation. The committed snapshot uses Survived as the stable baseline. If the e2e CI job fails only on this entry, re-run the job.
Editor Configurations
Configure your editor to use our lint and code style rules.
Code formatting
Biome Format, lint, and more in a fraction of a second.
Code linting
Biome Format, lint, and more in a fraction of a second.
Commit linting
This repository uses Commitlint to check our commit convention. Pre-commit git hook using husky and pull request check both the commit convention for each commit in a branch.
You can use an interactive command line to help you create supported commit message
npm run commit
Engine linting
This repository uses ls-engines to verify the running Node version is within the supported range and that engines.node stays consistent with the dependency graph.
It runs as a blocking pre-push git hook and as a pull request check.
Dependency policy
This repository is kept aligned with its three sibling plugins (sfdx-git-delta,
apex-mutation-testing, sf-git-merge-driver, dataset-loader), so the rules below are
identical in all four.
- Every dependency is pinned exactly — runtime and dev alike. No
^, no~, no ranges. A range in a runtime dependency becomes non-determinism for consumers, and a range in a dev dependency becomes drift between the four repositories. .npmrcsetssave-exact=true, sonpm install <package>records an exact version by default. This is the only mechanism enforcing the rule — keep the file.save-exactcannot be expressed inpackage.json: npm reads it from.npmrcor thenpm_config_save_exactenvironment variable, andpublishConfigapplies at publish time only.- Pins track current latest. Dependabot moves them; its
versioning-strategy: increaseraises a pinned requirement in place rather than widening it, so grouped updates stay exact. - npm 12 is required (
engines.npm: ">=12"), and no shrinkwrap is shipped. npm 12 excludesnpm-shrinkwrap.jsonfromnpm packeven when it is listed infiles, silently and with exit 0, so the mechanism is inert rather than merely unused. - There is deliberately no lint for this.
npm outdatedruns as a blocking check in CI and catches a pin that has fallen behind latest, but it cannot see a range that still resolves to latest. Adding a hand-edited range is caught in review, not by tooling.
What the pinning does and does not buy: it caps only the direct dependencies a consumer resolves. The transitive majority still floats, and capping those would mean declaring the whole chain directly.
PR linting
When a PR is ready for merge we use the PR name to create the squash and merge commit message. We use the commit convention to auto-generate the content and the type of each release It needs to follow our commit lint convention and it will be check at the PR level
Git Workflow
The process of submitting a pull request is straightforward and generally follows the same pattern each time:
- Fork the repo
- Create a feature branch
- Make your changes
- Rebase
- Check your submission
- Create a pull request
- Update the pull request
Fork the repo
Fork the scolladon/apex-mutation-testing repo. Clone your fork in your local workspace and configure your remote repository settings.
git clone git@github.com:<YOUR-USERNAME>/apex-mutation-testing.git
cd apex-mutation-testing
git remote add upstream git@github.com:scolladon/apex-mutation-testing.git
Create a feature branch
git checkout main
git pull origin main
git checkout -b feature/<name-of-the-feature>
Make your changes
Change the files, build, test, lint and commit your code using the following command:
git add <path/to/file/to/commit>
git commit ...
git push origin feature/<name-of-the-feature>
Commit your changes using a descriptive commit message
The above commands will commit the files into your feature branch. You can keep pushing new changes into the same branch until you are ready to create a pull request.
Rebase
Sometimes your feature branch will get stale on the main branch, and it will must a rebase. Do not use the github UI rebase to keep your commits signed. The following steps can help:
git checkout main
git pull upstream main
git checkout feature/<name-of-the-feature>
git rebase upstream/main
note: If no conflicts arise, these commands will apply your changes on top of the main branch. Resolve any conflicts.
Check your submission
Lint your changes
npm run lint
The above command may display lint issues not related to your changes. The recommended way to avoid lint issues is to configure your editor to warn you in real time as you edit the file.
the plugin lint all those things :
- typescript files
- folder structure
- plugin parameters
- plugin output
- dependencies
- dead code / configuration
Fixing all existing lint issues is a tedious task so please pitch in by fixing the ones related to the files you make changes to!
Run tests
Test your change by running the unit tests and integration tests. See the testing instructions.
Create a pull request
If you've never created a pull request before, follow these instructions. See pull request samples
Update the pull request
git fetch origin
git rebase origin/${base_branch}
# Then force push it
git push origin ${feature_branch} --force-with-lease
note: If your pull request needs more changes, keep working on your feature branch as described above.
CI validates prettifying, linting and tests
Collaborate on the pull request
We use Conventional Comments to ensure every comment expresses the intention and is easy to understand. Pull Request comments are not enforced, it is more a way to help the reviewers and contributors to collaborate on the pull request.
CLI parameters convention
The plugins uses sf cli parameters convention to define parameters for the CLI.
Testing the plugin from a pull request
Every pull request — including one opened from a fork — publishes a preview build of the plugin to pkg.pr.new. A bot comment on the pull request carries the exact install command:
sf plugins install https://pkg.pr.new/apex-mutation-testing@<commit>
Previews are addressed by commit, not by pull request: each push produces a new URL, so read the install command again rather than re-running an earlier one. Preview builds are removed once they have gone a month without a download, and after six months in any case — reinstall from a fresh comment if an old link stops resolving.
A preview from a fork is unreviewed contributor code, and installing a plugin executes arbitrary
code with your Salesforce org credentials in reach. Install those only in a disposable
environment. Fork pull requests get no comment — their token is read-only — so the URL must be
read from the preview job's log.
Previews publish only once the pkg-pr-new GitHub App is installed on the repository. Without it the preview job fails with a self-describing 404.
To go back to the released plugin:
sf plugins uninstall apex-mutation-testing
sf plugins install apex-mutation-testing@latest-rc