Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 23 additions & 12 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,27 @@
**/target
**/__pycache__

**/.gradle
**/build

# Only the four packaged service JARs enter application build contexts.
!payguard-identity/target/
payguard-identity/target/*
!payguard-identity/target/payguard-identity-1.0.0-SNAPSHOT.jar
!payguard-wallet/target/
payguard-wallet/target/*
!payguard-wallet/target/payguard-wallet-1.0.0-SNAPSHOT.jar
!payguard-loan/target/
payguard-loan/target/*
!payguard-loan/target/payguard-loan-1.0.0-SNAPSHOT.jar
!payguard-collateral/target/
payguard-collateral/target/*
!payguard-collateral/target/payguard-collateral-1.0.0-SNAPSHOT.jar
!payguard-identity/build/
payguard-identity/build/*
!payguard-identity/build/libs/
payguard-identity/build/libs/*
!payguard-identity/build/libs/payguard-identity-1.0.0-SNAPSHOT.jar
!payguard-wallet/build/
payguard-wallet/build/*
!payguard-wallet/build/libs/
payguard-wallet/build/libs/*
!payguard-wallet/build/libs/payguard-wallet-1.0.0-SNAPSHOT.jar
!payguard-loan/build/
payguard-loan/build/*
!payguard-loan/build/libs/
payguard-loan/build/libs/*
!payguard-loan/build/libs/payguard-loan-1.0.0-SNAPSHOT.jar
!payguard-collateral/build/
payguard-collateral/build/*
!payguard-collateral/build/libs/
payguard-collateral/build/libs/*
!payguard-collateral/build/libs/payguard-collateral-1.0.0-SNAPSHOT.jar
6 changes: 3 additions & 3 deletions .github/workflows/_database-security.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
with:
distribution: temurin
java-version: '25'
cache: maven
cache: gradle
- name: Install native Ansible dependencies and offline test dependencies
run: |
python3 -m venv /tmp/payguard-ansible
Expand All @@ -38,7 +38,7 @@ jobs:
- name: Verify source contracts and build applications
run: |
python3 -m unittest discover -s infrastructure/tests -v
./mvnw -B -ntp -pl payguard-identity,payguard-wallet,payguard-loan,payguard-collateral -am test package
bash ./gradlew --no-daemon --build-cache :payguard-identity:test :payguard-wallet:test :payguard-loan:test :payguard-collateral:test :payguard-identity:bootJar :payguard-wallet:bootJar :payguard-loan:bootJar :payguard-collateral:bootJar
- name: Prepare isolated fixture and allocate only new explicit volumes
run: |
ansible-playbook -i localhost, -c local infrastructure/operations/prepare.yml -e fresh_target=true
Expand Down Expand Up @@ -73,7 +73,7 @@ jobs:
uses: actions/upload-artifact@v7
with:
name: native-infrastructure-java-reports
path: '**/target/surefire-reports/TEST-*.xml'
path: '**/build/test-results/test/TEST-*.xml'
if-no-files-found: warn
- name: Stop only this isolated fixture
if: always()
Expand Down
21 changes: 0 additions & 21 deletions .github/workflows/_publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,27 +12,6 @@ jobs:
- name: Checkout Repository
uses: actions/checkout@v7

- name: Set up JDK 25
uses: actions/setup-java@v6
with:
java-version: '25'
distribution: 'temurin'
cache: 'maven'

- name: Compile and Package (Skip Tests)
# Tests are skipped because they already passed in the _quality workflow
run: ./mvnw clean package -DskipTests

- name: Install Maven Modules for Image Builds
# Image builds run as separate Maven invocations and need reactor artifacts in the local repository.
run: ./mvnw -DskipTests -Dpmd.skip=true -Dspotbugs.skip=true install

- name: Verify Spring Boot plugin resolution
run: |
./mvnw -version
./mvnw help:effective-pom -pl payguard-wallet \
| grep -A8 -B2 spring-boot-maven-plugin

- name: Build Wallet Service Image
run: docker build -f payguard-wallet/Dockerfile -t payguard/wallet-service:${{ github.sha }} .

Expand Down
28 changes: 5 additions & 23 deletions .github/workflows/_quality.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,28 +20,10 @@ jobs:
with:
java-version: '25'
distribution: 'temurin'
cache: 'maven'
cache: 'gradle'

# 1. Spotless: Verifies code formatting without mutating the checkout
- name: Run Spotless
run: ./mvnw spotless:check
- name: Verify pre-push hook
run: python3 -m unittest discover -s git-hook -p 'test_*.py' -v

# 2. CheckStyle: Enforces coding standards
- name: Run CheckStyle
run: ./mvnw checkstyle:check

# Install reactor modules so independent analysis invocations can resolve local artifacts.
- name: Install Maven Modules
run: ./mvnw -DskipTests -Dpmd.skip=true -Dspotbugs.skip=true install

# 3. PMD: Scans for programming flaws
- name: Run PMD
run: ./mvnw pmd:check

# 4. SpotBugs: Looks for bugs in Java bytecode
- name: Run SpotBugs
run: ./mvnw spotbugs:check

# 5. Unit Tests: Ensures the core domain logic is intact
- name: Run Unit Tests
run: ./mvnw clean verify
- name: Run formatting, static analysis, and tests
run: bash ./gradlew --no-daemon --build-cache check
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

# Generated build output
target/
build/
.gradle/

# Local workflow guidance
GIT_WORKFLOW_CONVENTION.md
Expand All @@ -18,4 +20,4 @@ CLAUDE.md
payguard-identity/NEW IDEAS.MD
payguard-identity/ARCHITECTURE.md
payguard-identity/IDENTITY_CHECKLIST.MD
NO_LLM_FOR_CODING.MD
NO_LLM_FOR_CODING.MD
14 changes: 11 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ First clone the repository and build it:
```bash
git clone https://github.com/<your-org>/payguard.git
cd payguard
./mvnw -B -ntp clean verify
bash ./gradlew --build-cache build
```

Then start the infrastructure (PostgreSQL and Vault) and the applications:
Expand Down Expand Up @@ -209,15 +209,23 @@ Each service is an independently deployable module.

## Requirements

PayGuard runs on the JVM. Use JDK **25**, Docker and Compose, Ansible and OpenSSL. The Maven wrapper is included.
PayGuard runs on the JVM. Use JDK **25**, Docker and Compose, Ansible and OpenSSL. The Gradle wrapper is included.
Database integration tests start real PostgreSQL containers.

## Build and local rehearsal

```bash
./mvnw -B -ntp clean verify
bash ./gradlew --build-cache build
```

Install the repository's pre-push hook once:

```bash
bash git-hook/install.sh
```

The hook validates committed changes. Commit build and workflow changes before pushing.

For the Compose, Vault Agent, Ansible and Flyway CLI deployment,
follow [the infrastructure runbook](docs/infrastructure.md).
It gives the exact preparation, initialization, migration and test commands.
Expand Down
214 changes: 214 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
import com.diffplug.gradle.spotless.SpotlessExtension
import com.github.spotbugs.snom.SpotBugsExtension
import com.github.spotbugs.snom.SpotBugsTask

plugins {
base
alias(libs.plugins.boot.application) apply false
alias(libs.plugins.spotless)
alias(libs.plugins.spotbugs) apply false
}

spotless {
format("buildFiles") {
target("*.gradle.kts", "gradle/*.toml", "buildSrc/**/*.gradle.kts", "payguard-*/build.gradle.kts", "payguard-*/settings.gradle.kts")
targetExclude("buildSrc/build/**", "buildSrc/.gradle/**")
trimTrailingWhitespace()
endWithNewline()
}
}

subprojects {
pluginManager.withPlugin("java-library") {
dependencies {
"testRuntimeOnly"(rootProject.libs.org.junit.platform.junit.platform.launcher)
}
tasks.withType<Test>().configureEach {
useJUnitPlatform()
}
}

pluginManager.withPlugin(rootProject.libs.plugins.spotless.get().pluginId) {
extensions.configure<SpotlessExtension> {
java {
target("src/**/*.java")
targetExclude("src/**/generated/**")
googleJavaFormat(rootProject.libs.versions.google.java.format.get()).aosp()
formatAnnotations()
}
}
}

pluginManager.withPlugin("checkstyle") {
extensions.configure<CheckstyleExtension> {
toolVersion = rootProject.libs.versions.checkstyle.get()
configFile = rootProject.file("config/checkstyle/checkstyle.xml")
}
tasks.withType<Checkstyle>().configureEach {
exclude { "/generated/" in it.file.invariantSeparatorsPath }
}
}

pluginManager.withPlugin("pmd") {
extensions.configure<PmdExtension> {
toolVersion = rootProject.libs.versions.pmd.get()
ruleSets = listOf("category/java/errorprone.xml")
}
tasks.withType<Pmd>().configureEach {
exclude { "/generated/" in it.file.invariantSeparatorsPath }
}
}

pluginManager.withPlugin(rootProject.libs.plugins.spotbugs.get().pluginId) {
extensions.configure<SpotBugsExtension> {
toolVersion.set(rootProject.libs.versions.spotbugs.asProvider())
excludeFilter.set(rootProject.file("config/spotbugs/exclude.xml"))
}
tasks.withType<SpotBugsTask>().configureEach {
reports.create("html") { required.set(true) }
}
}
}

// Share declarations only among the modules that already used them, preserving their scopes.
configure(listOf(
project(":payguard-collateral"),
project(":payguard-gateway"),
project(":payguard-identity"),
project(":payguard-loan"),
project(":payguard-shared"),
project(":payguard-wallet"),
)) {
pluginManager.withPlugin("java-library") {
dependencies {
"api"(rootProject.libs.org.springframework.boot.spring.boot.starter.actuator)
}
}
}

configure(listOf(
project(":payguard-collateral"),
project(":payguard-gateway"),
project(":payguard-identity"),
project(":payguard-loan"),
project(":payguard-wallet"),
)) {
pluginManager.withPlugin("java-library") {
dependencies {
"api"(rootProject.libs.org.springframework.boot.spring.boot.starter.webmvc)
"testImplementation"(rootProject.libs.org.springframework.boot.spring.boot.starter.webmvc.test)
}
}
}

configure(listOf(
project(":payguard-collateral"),
project(":payguard-identity"),
project(":payguard-loan"),
project(":payguard-shared"),
)) {
pluginManager.withPlugin("java-library") {
dependencies {
"testImplementation"(rootProject.libs.org.flywaydb.flyway.database.postgresql)
"testImplementation"(rootProject.libs.org.testcontainers.testcontainers.postgresql)
}
}
}

configure(listOf(
project(":payguard-collateral"),
project(":payguard-identity"),
project(":payguard-loan"),
project(":payguard-wallet"),
)) {
pluginManager.withPlugin("java-library") {
dependencies {
"runtimeOnly"(rootProject.libs.org.postgresql.postgresql)
}
}
}

configure(listOf(
project(":payguard-collateral"),
project(":payguard-identity"),
project(":payguard-loan"),
)) {
pluginManager.withPlugin("java-library") {
dependencies {
"api"(rootProject.libs.org.springframework.boot.spring.boot.starter.kafka)
"testImplementation"(rootProject.libs.org.springframework.boot.spring.boot.starter.flyway)
"testImplementation"(rootProject.libs.org.testcontainers.testcontainers.junit.jupiter)
}
}
}

configure(listOf(
project(":payguard-collateral"),
project(":payguard-loan"),
project(":payguard-wallet"),
)) {
pluginManager.withPlugin("java-library") {
dependencies {
"api"(project(":payguard-shared"))
}
}
}

configure(listOf(
project(":payguard-gateway"),
project(":payguard-identity"),
project(":payguard-wallet"),
)) {
pluginManager.withPlugin("java-library") {
dependencies {
"api"(rootProject.libs.org.springframework.boot.spring.boot.starter.validation)
}
}
}

configure(listOf(
project(":payguard-collateral"),
project(":payguard-loan"),
)) {
pluginManager.withPlugin("java-library") {
dependencies {
"api"(rootProject.libs.org.springframework.boot.spring.boot.starter.data.jpa)
}
}
}

configure(listOf(
project(":payguard-identity"),
project(":payguard-wallet"),
)) {
pluginManager.withPlugin("java-library") {
dependencies {
"api"(rootProject.libs.org.springframework.boot.spring.boot.starter.jooq)
"api"(rootProject.libs.org.springframework.modulith.spring.modulith.starter.core)
"testImplementation"(rootProject.libs.org.springframework.modulith.spring.modulith.starter.test)
}
}
}

configure(listOf(
project(":payguard-proto"),
project(":payguard-shared"),
)) {
pluginManager.withPlugin("java-library") {
dependencies {
"testImplementation"(rootProject.libs.org.springframework.boot.spring.boot.starter.test)
}
}
}

configure(listOf(project(":payguard-identity"), project(":payguard-shared"))) {
pluginManager.withPlugin("java-library") {
dependencies {
"testImplementation"(rootProject.libs.org.postgresql.postgresql)
}
}
}

tasks.named("check") {
dependsOn(subprojects.map { "${it.path}:check" })
}
Loading
Loading