PluginBench
Skill
Review
Audit score 70

springboot-verification

affaan-m/ecc

Build, test, analyze, and security-scan Spring Boot projects before PR or deploy.

What is springboot-verification?

Verification loop for Spring Boot projects that orchestrates build, static analysis, unit/integration/API tests with coverage reporting, dependency and secrets scanning, and diff review. Run before pull requests, after major changes, or pre-deployment to catch issues early.

  • Parallel Maven/Gradle build with test skipping for fast compilation
  • Static analysis via SpotBugs, PMD, and Checkstyle
  • Unit tests with mocks, integration tests with Testcontainers, and API tests with MockMvc
  • Code coverage reporting (JaCoCo) with threshold validation
  • OWASP dependency CVE scanning and secrets detection in source and git history
  • Diff review checklist for logging, error handling, transactions, and config changes

How to install springboot-verification

npx skills add null --skill springboot-verification
Prerequisites
  • Maven 3.6+ or Gradle 7.0+
  • Java 11+
  • Git (for diff and secrets scanning)
  • Optional: Testcontainers Docker setup for integration tests
  • Optional: SpotBugs, PMD, Checkstyle, JaCoCo, OWASP Dependency-Check plugins configured in pom.xml or build.gradle
Claude Code
Cursor
Windsurf
Cline

How to use springboot-verification

  1. 1.Run Phase 1 (Build): `mvn clean verify -DskipTests` or `./gradlew clean assemble -x test`
  2. 2.Run Phase 2 (Static Analysis): `mvn spotbugs:check pmd:check checkstyle:check` or Gradle equivalents
  3. 3.Run Phase 3 (Tests + Coverage): `mvn test && mvn jacoco:report` and verify coverage meets threshold
  4. 4.Run Phase 4 (Security Scan): `mvn org.owasp:dependency-check-maven:check` and grep for hardcoded secrets
  5. 5.Run Phase 5 (Lint): `mvn spotless:apply` if configured
  6. 6.Run Phase 6 (Diff Review): `git diff --stat && git diff` and check against provided checklist
  7. 7.Generate output report summarizing pass/fail for each phase and overall readiness

Use cases

Good for
  • Pre-pull-request verification for Spring Boot microservices
  • Post-refactoring or dependency-upgrade validation
  • Pre-staging and pre-production deployment checks
  • Continuous verification loop during active development sessions
  • Coverage threshold enforcement (80%+) before merge
Who it's for
  • Spring Boot backend developers
  • DevOps engineers managing CI/CD pipelines
  • Teams enforcing code quality gates
  • Security-conscious projects requiring CVE and secrets scanning

springboot-verification FAQ

What if the build fails in Phase 1?

Stop immediately and fix compilation errors before proceeding to later phases. A broken build blocks all downstream verification.

How do I set up Testcontainers for integration tests?

Add the Testcontainers dependency to your pom.xml or build.gradle, annotate test classes with @Testcontainers, declare @Container static fields (e.g., PostgreSQLContainer), and use @DynamicPropertySource to inject connection details into Spring properties.

What coverage threshold should I enforce?

The skill recommends 80%+ line and branch coverage. Adjust based on your team's standards, but treat it as a gate before merge.

How do I detect secrets in my codebase?

Use grep patterns for common secrets (password=, sk-, api_key, secret), configure git-secrets for pre-commit hooks, and scan git history with `git secrets --scan`.

Can I run this in CI/CD?

Yes. Execute the phases sequentially in your pipeline (GitHub Actions, GitLab CI, Jenkins, etc.) and fail the build if any phase returns non-zero exit code.

Full instructions (SKILL.md)

Source of truth, from affaan-m/ecc.


name: springboot-verification description: "Verification loop for Spring Boot projects: build, static analysis, tests with coverage, security scans, and diff review before release or PR." metadata: origin: ECC

Spring Boot Verification Loop

Run before PRs, after major changes, and pre-deploy.

When to Activate

  • Before opening a pull request for a Spring Boot service
  • After major refactoring or dependency upgrades
  • Pre-deployment verification for staging or production
  • Running full build → lint → test → security scan pipeline
  • Validating test coverage meets thresholds

Phase 1: Build

mvn -T 4 clean verify -DskipTests
# or
./gradlew clean assemble -x test

If build fails, stop and fix.

Phase 2: Static Analysis

Maven (common plugins):

mvn -T 4 spotbugs:check pmd:check checkstyle:check

Gradle (if configured):

./gradlew checkstyleMain pmdMain spotbugsMain

Phase 3: Tests + Coverage

mvn -T 4 test
mvn jacoco:report   # verify 80%+ coverage
# or
./gradlew test jacocoTestReport

Report:

  • Total tests, passed/failed
  • Coverage % (lines/branches)

Unit Tests

Test service logic in isolation with mocked dependencies:

@ExtendWith(MockitoExtension.class)
class UserServiceTest {

  @Mock private UserRepository userRepository;
  @InjectMocks private UserService userService;

  @Test
  void createUser_validInput_returnsUser() {
    var dto = new CreateUserDto("Alice", "alice@example.com");
    var expected = new User(1L, "Alice", "alice@example.com");
    when(userRepository.save(any(User.class))).thenReturn(expected);

    var result = userService.create(dto);

    assertThat(result.name()).isEqualTo("Alice");
    verify(userRepository).save(any(User.class));
  }

  @Test
  void createUser_duplicateEmail_throwsException() {
    var dto = new CreateUserDto("Alice", "existing@example.com");
    when(userRepository.existsByEmail(dto.email())).thenReturn(true);

    assertThatThrownBy(() -> userService.create(dto))
        .isInstanceOf(DuplicateEmailException.class);
  }
}

Integration Tests with Testcontainers

Test against a real database instead of H2:

@SpringBootTest
@Testcontainers
class UserRepositoryIntegrationTest {

  @Container
  static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:16-alpine")
      .withDatabaseName("testdb");

  @DynamicPropertySource
  static void configureProperties(DynamicPropertyRegistry registry) {
    registry.add("spring.datasource.url", postgres::getJdbcUrl);
    registry.add("spring.datasource.username", postgres::getUsername);
    registry.add("spring.datasource.password", postgres::getPassword);
  }

  @Autowired private UserRepository userRepository;

  @Test
  void findByEmail_existingUser_returnsUser() {
    userRepository.save(new User("Alice", "alice@example.com"));

    var found = userRepository.findByEmail("alice@example.com");

    assertThat(found).isPresent();
    assertThat(found.get().getName()).isEqualTo("Alice");
  }
}

API Tests with MockMvc

Test controller layer with full Spring context:

@WebMvcTest(UserController.class)
class UserControllerTest {

  @Autowired private MockMvc mockMvc;
  @MockBean private UserService userService;

  @Test
  void createUser_validInput_returns201() throws Exception {
    var user = new UserDto(1L, "Alice", "alice@example.com");
    when(userService.create(any())).thenReturn(user);

    mockMvc.perform(post("/api/users")
            .contentType(MediaType.APPLICATION_JSON)
            .content("""
                {"name": "Alice", "email": "alice@example.com"}
                """))
        .andExpect(status().isCreated())
        .andExpect(jsonPath("$.name").value("Alice"));
  }

  @Test
  void createUser_invalidEmail_returns400() throws Exception {
    mockMvc.perform(post("/api/users")
            .contentType(MediaType.APPLICATION_JSON)
            .content("""
                {"name": "Alice", "email": "not-an-email"}
                """))
        .andExpect(status().isBadRequest());
  }
}

Phase 4: Security Scan

# Dependency CVEs
mvn org.owasp:dependency-check-maven:check
# or
./gradlew dependencyCheckAnalyze

# Secrets in source
grep -rn "password\s*=\s*\"" src/ --include="*.java" --include="*.yml" --include="*.properties"
grep -rn "sk-\|api_key\|secret" src/ --include="*.java" --include="*.yml"

# Secrets (git history)
git secrets --scan  # if configured

Common Security Findings

# Check for System.out.println (use logger instead)
grep -rn "System\.out\.print" src/main/ --include="*.java"

# Check for raw exception messages in responses
grep -rn "e\.getMessage()" src/main/ --include="*.java"

# Check for wildcard CORS
grep -rn "allowedOrigins.*\*" src/main/ --include="*.java"

Phase 5: Lint/Format (optional gate)

mvn spotless:apply   # if using Spotless plugin
./gradlew spotlessApply

Phase 6: Diff Review

git diff --stat
git diff

Checklist:

  • No debugging logs left (System.out, log.debug without guards)
  • Meaningful errors and HTTP statuses
  • Transactions and validation present where needed
  • Config changes documented

Output Template

VERIFICATION REPORT
===================
Build:     [PASS/FAIL]
Static:    [PASS/FAIL] (spotbugs/pmd/checkstyle)
Tests:     [PASS/FAIL] (X/Y passed, Z% coverage)
Security:  [PASS/FAIL] (CVE findings: N)
Diff:      [X files changed]

Overall:   [READY / NOT READY]

Issues to Fix:
1. ...
2. ...

Continuous Mode

  • Re-run phases on significant changes or every 30–60 minutes in long sessions
  • Keep a short loop: mvn -T 4 test + spotbugs for quick feedback

Remember: Fast feedback beats late surprises. Keep the gate strict—treat warnings as defects in production systems.