A Jenkins Pipeline plugin for sentinel mutation testing. Provides two composable pipeline steps (sentinelRun + sentinelReport) that users combine with standard Jenkins Declarative Pipeline constructs for distributed execution.
- Pipeline-only (no Freestyle support)
- API style: Composable steps with
SENTINEL_*environment variable configuration
| Item | Choice |
|---|---|
| Jenkins | 2.479.x LTS minimum |
| Java | 17 |
| Build | Maven (jenkins-plugin-parent POM) |
| sentinel | Pre-installed on all nodes, configurable path |
Two composable Pipeline steps:
sentinelRun— Runs sentinel. Reads config fromSENTINEL_*env vars, step params override. Auto-stashes results forsentinelReport. AcceptspartitionIndexfor distributed execution.sentinelReport— Unstashes results, merges partitions (ifSENTINEL_PARTITION_TOTALset), generates reports, parsesmutations.xml, attaches build action, applies threshold judgment.
- Composable steps, not orchestrator: Users compose
sentinelRun+sentinelReportwith standard Jenkinsparallel,node,agentdirectives. No closure-based orchestration. - Environment variable configuration: All sentinel CLI options configurable via
SENTINEL_*env vars in Declarative Pipelineenvironment {}block. Step params override env vars. - Merge and report are separate sentinel commands:
--merge-partitiononly merges and exits;--output-dirruns separately for report generation. - Plugin owns threshold judgment: Never pass
--thresholdto sentinel. Plugin parsesmutations.xmland sets build result to FAILURE/UNSTABLE. - Source code required on report node: HTML reports embed source code. User must
checkout scmbeforesentinelReport. - Result parsing: Parse
mutations.xml(PITest-compatible XML) from--output-diroutput. Never parse stdout or workspace internals. - stash/unstash handled by plugin:
sentinelRunauto-stashes,sentinelReportauto-unstashes. Users don't manage stash names. - Workspace path separation: Plugin auto-assigns unique workspace paths per partition (
.sentinel-1,.sentinel-2, ...). - partitionIndex is a step param, not env var: Each parallel stage specifies its own
partitionIndex.SENTINEL_PARTITION_TOTALis shared via env var.
# Basic build
mvn clean verify
# Build with all static analysis
mvn clean verify -Pstatic-analysis
# Run individual analysis
mvn checkstyle:check
mvn spotbugs:check
mvn pmd:check- No wildcard imports, no unused imports
- No tabs, no trailing whitespace, newline at EOF
- Max 700 lines per file
- Braces required on all blocks (
if,for, etc.) - No magic numbers in production code (tests exempt)
equals()must pair withhashCode()
- JUnit 5 + AssertJ + Mockito
- One test class per production class
- Descriptive method names:
validConfigPasses(),throwsOnMissingFile() - AssertJ fluent:
assertThat(...).isEqualTo(),isCloseTo(val, within(0.01)) - Error assertions:
assertThatThrownBy(...).isInstanceOf().hasMessageContaining() @TempDirfor file system tests, static final constants for test data
All integrated via static-analysis Maven profile:
| Tool | Phase | Purpose |
|---|---|---|
| Maven Enforcer | validate | Build rules (Java 17, Maven version) |
| Error Prone | compile | Compile-time bug detection |
| Modernizer | compile | Legacy API detection |
| JaCoCo | test | Code coverage |
| Checkstyle | verify | Code style |
| SpotBugs | verify | Bytecode bug detection |
| PMD | verify | Code quality patterns |
| OWASP Dependency-Check | verify | Dependency vulnerability scan |
| Javadoc | verify | API doc completeness |
io.jenkins.plugins.sentinel
├── SentinelGlobalConfiguration # Global config (sentinel path)
├── SentinelRunStep # sentinelRun step (env var config, auto-stash)
├── SentinelReportStep # sentinelReport step (unstash, merge, report, threshold)
├── SentinelEnvironment # SENTINEL_* env var mapping, naming conventions
├── SentinelCommandBuilder # Builds sentinel CLI commands
├── SentinelRunner # Executes sentinel CLI via Jenkins launcher
├── SentinelPostProcessor # Merge, report generation, threshold judgment
├── SentinelResultParser # Parses mutations.xml
├── SentinelBuildAction # Build page: summary widget, report page (RunAction2)
├── SentinelProjectAction # Project page: mutation score trend chart
├── SentinelProjectActionFactory # TransientActionFactory for SentinelProjectAction
├── config/
│ ├── SentinelConfiguration # Config data class
│ ├── SentinelConfigValidator # Config validation
│ └── ThresholdAction # Enum: FAILURE, UNSTABLE
└── model/
├── SentinelResult # Result data model
├── MutationScore # Mutation score model
└── FileMutationResult # Per-file result model
- All step/model classes must be
Serializable(CPS pipeline requirement) @DataBoundConstructorwith no-arg constructor,@DataBoundSetterfor all optional fields (env vars provide defaults)load()in GlobalConfiguration constructor is standard Jenkins pattern (SpotBugs excluded)- Jelly templates use
escape-by-default='true'for XSS protection listener.getLogger()is standard Jenkins logging patternTransientActionFactoryfor project-level actions (no manual registration needed)RunAction2for build actions that need persistedRunreference- ECharts charts: pass JSON via
data-*attributes, load via<st:adjunct includes="io.jenkins.plugins.echarts"/>
- Pipeline steps have many public fields (maps to sentinel CLI options) — GodClass/TooManyFields excluded
- StepExecution.run() requires
throws Exception— SignatureDeclareThrowsException excluded - Inner classes in steps need outer reference for CPS context — SE_INNER_CLASS excluded
SentinelRunStep.partitionIndex: Integer (1-based). Combined withSENTINEL_PARTITION_TOTALenv var to form--partition index/total.SentinelConfiguration.getPartitionSpec(): derives"index/total"string frompartitionIndex+partitionTotal.SentinelEnvironment: single source of truth for env var names, stash names, workspace paths.- MutationScore formula:
killed / (killed + survived) * 100(skipped excluded from denominator) - Threshold is optional; if set,
thresholdActionis required (paired validation)
- 3-stage build:
dev(JDK+Maven) →build(compile) →package(minimal, .hpi only) docker build --target devfor development environment- Maven cache:
--mount=type=cache,target=/root/.m2/repository
- Always summarize what you plan to implement and get approval before writing code.
- All sentinel CLI options must be configurable via
SENTINEL_*env vars and step parameters. - Never pass
--thresholdto sentinel — plugin handles threshold judgment from mutations.xml. - Report node = pipeline's current agent. Partition nodes = allocated by user via standard Jenkins
agent/nodedirectives. - When writing new code or modifying existing code:
- Run the
simplifyskill for code review. - Run all static analysis including Javadoc (
mvn clean verify -Pstatic-analysis) and fix all issues. - Update
README.mdto reflect any user-facing changes.
- Run the