Skip to content
Merged
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
85 changes: 85 additions & 0 deletions .github/workflows/spring-rules-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
name: Architecture Validator Spring Rules - Build
on:
push:
branches-ignore:
- main
paths:
- 'hexagonal-spring-rules/**'
- 'examples/architecture-validator/single-module-spring/**'
- '.github/workflows/**'
- '.github/actions/**'

jobs:
Calculate-Version:
uses: ./.github/workflows/semantic-version-calculate.yml
with:
fail_on_empty_version: false
fail_on_existing_tag: false
project_dir: hexagonal-spring-rules
secrets:
sem_rel_token_token: ${{ secrets.SEMANTIC_RELEASE_TOKEN }}

Determine-Release-Type:
needs: Calculate-Version
uses: ./.github/workflows/determine-release-type.yml

Build-Spring-Rules:
name: Build Architecture Validator Spring Rules
needs: Calculate-Version
permissions:
contents: write
issues: write
pull-requests: write

runs-on: ubuntu-latest

env:
GRADLE_PROPERTIES_FILE: ./hexagonal-spring-rules/gradle.properties
GRADLE_VERSION_REGEX: version\s*=\s*[0-9]\\+\.[0-9]\\+\.[0-9]\\+[a-zA-Z0-9.-]*
VERSION: ${{ needs.Calculate-Version.outputs.next_version }}

steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
fetch-depth: 0
persist-credentials: false

- name: Setup Java
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
with:
distribution: 'temurin'
java-version: 21

- name: Setup Gradle
uses: gradle/actions/setup-gradle@50e97c2cd7a37755bbfafc9c5b7cafaece252f6e # v6.1.0

- name: Update version in Gradle property file
if: ${{ env.VERSION != '' }}
run: |
echo "VERSION=${{ env.VERSION }}-PRERELEASE"
echo ===== Before - ${{ env.GRADLE_PROPERTIES_FILE }} =====
cat ${{ env.GRADLE_PROPERTIES_FILE }}
sed -i "s/${{ env.GRADLE_VERSION_REGEX }}/version = ${{ env.VERSION }}-PRERELEASE/g" ${{ env.GRADLE_PROPERTIES_FILE }}
echo ===== After - ${{ env.GRADLE_PROPERTIES_FILE }} =====
cat ${{ env.GRADLE_PROPERTIES_FILE }}

- name: Build and test Architecture Validator Spring Rules
run: |
cd hexagonal-spring-rules
chmod +x ./gradlew
./gradlew build --configuration-cache
env:
GITHUB_ACTOR: ${{ github.actor }}
GITHUB_TOKEN: ${{ secrets.SEDR_AUTOMATION_TOKEN }}

Workflow-Summary:
name: Workflow Summary
needs: [Build-Spring-Rules, Determine-Release-Type]
if: always()
runs-on: ubuntu-latest
steps:
- name: Output build completion
run: |
echo "Architecture Validator Spring Rules ${{ needs.Determine-Release-Type.outputs.release_type }} build workflow completed"
echo "::notice title=Build Complete::Architecture Validator Spring Rules ${{ needs.Determine-Release-Type.outputs.release_type }} build workflow completed"
165 changes: 165 additions & 0 deletions .github/workflows/spring-rules-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
name: Architecture Validator Spring Rules - Release
on:
push:
branches:
- main
paths:
- 'hexagonal-spring-rules/*.gradle'
- 'hexagonal-spring-rules/package.json'
- 'hexagonal-spring-rules/package-lock.json'
- 'hexagonal-spring-rules/release.config.js'
- 'hexagonal-spring-rules/CHANGELOG.md'
- 'hexagonal-spring-rules/gradle/**'
- 'hexagonal-spring-rules/src/main/**'

jobs:
Calculate-Version:
uses: ./.github/workflows/semantic-version-calculate.yml
with:
fail_on_empty_version: true
project_dir: hexagonal-spring-rules
DEBUG: true
secrets:
sem_rel_token_token: ${{ secrets.SEMANTIC_RELEASE_TOKEN }}

Security-Scan:
needs: Calculate-Version
uses: ./.github/workflows/security-scan.yml
with:
fail_on_fatal: true
project_name: 'hexagonal-spring-rules'
semantic_version: ${{ needs.Calculate-Version.outputs.next_version }}
reports_subpath: build/reports
secrets:
NVD_APIKEY_SEDR: ${{ secrets.NVD_APIKEY_SEDR }}

Build:
name: Build Architecture Validator Spring Rules
needs: Calculate-Version
permissions:
contents: write
issues: write
pull-requests: write

runs-on: ubuntu-latest

env:
GRADLE_PROPERTIES_FILE: ./hexagonal-spring-rules/gradle.properties
GRADLE_VERSION_REGEX: version\s*=\s*[0-9]\\+\.[0-9]\\+\.[0-9]\\+[a-zA-Z0-9.-]*
VERSION: ${{ needs.Calculate-Version.outputs.next_version }}

steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
fetch-depth: 0
persist-credentials: false

- name: Setup Java
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
with:
distribution: 'temurin'
java-version: 21

- name: Setup Gradle
uses: gradle/actions/setup-gradle@50e97c2cd7a37755bbfafc9c5b7cafaece252f6e # v6.1.0

- name: Update version in Gradle property file
run: |
echo "VERSION=${{ env.VERSION }}"
echo ===== Before - ${{ env.GRADLE_PROPERTIES_FILE }} =====
cat ${{ env.GRADLE_PROPERTIES_FILE }}
sed -i "s/${{ env.GRADLE_VERSION_REGEX }}/version = ${{ env.VERSION }}/g" ${{ env.GRADLE_PROPERTIES_FILE }}
echo ===== After - ${{ env.GRADLE_PROPERTIES_FILE }} =====
cat ${{ env.GRADLE_PROPERTIES_FILE }}

- name: Build and test Architecture Validator Spring Rules
run: |
cd hexagonal-spring-rules
chmod +x ./gradlew
./gradlew build --configuration-cache
env:
GITHUB_ACTOR: ${{ github.actor }}
GITHUB_TOKEN: ${{ secrets.SEDR_AUTOMATION_TOKEN }}

Release-Spring-Rules:
name: Release Architecture Validator Spring Rules
needs: [Calculate-Version, Build, Security-Scan]
if: ${{ needs.Calculate-Version.outputs.next_version != '' }}
uses: ./.github/workflows/semantic-version-apply.yml
with:
project_dir: hexagonal-spring-rules
secrets:
sem_rel_token_token: ${{ secrets.SEMANTIC_RELEASE_TOKEN }}

Publish:
name: Publish Architecture Validator Spring Rules
needs: [Calculate-Version, Release-Spring-Rules]
if: ${{ needs.Calculate-Version.outputs.next_version != '' }}
runs-on: ubuntu-latest
permissions:
contents: read
packages: write

env:
GRADLE_PROPERTIES_FILE: ./hexagonal-spring-rules/gradle.properties
GRADLE_VERSION_REGEX: version\s*=\s*[0-9]\\+\.[0-9]\\+\.[0-9]\\+[a-zA-Z0-9.-]*
VERSION: ${{ needs.Calculate-Version.outputs.next_version }}

steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
fetch-depth: 0
ref: ${{ github.ref_name }}

- name: Setup Java
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
with:
distribution: 'temurin'
java-version: 21

- name: Setup Gradle
uses: gradle/actions/setup-gradle@50e97c2cd7a37755bbfafc9c5b7cafaece252f6e # v6.1.0

- name: Update version in Gradle property file
run: |
sed -i "s/${{ env.GRADLE_VERSION_REGEX }}/version = ${{ env.VERSION }}/g" ${{ env.GRADLE_PROPERTIES_FILE }}

- name: Publish to Maven Central or GitHub Packages
run: |
cd hexagonal-spring-rules
chmod +x ./gradlew
if echo "$MAVEN_OR_GITHUB" | grep -iq "^maven$"; then
echo "Publishing Spring Rules to Maven Central via jReleaser"
./gradlew publishMavenJavaPublicationToStagingDeployRepository --no-daemon --no-configuration-cache
./gradlew jreleaserDeploy --no-daemon --no-configuration-cache
else
echo "Publishing Spring Rules to GitHub Packages"
./gradlew publishMavenJavaPublicationToGitHubPackagesRepository --no-daemon --no-configuration-cache
fi
env:
MAVEN_OR_GITHUB: ${{ vars.MAVEN_OR_GITHUB }}
GITHUB_ACTOR: ${{ github.actor }}
GITHUB_TOKEN: ${{ secrets.SEDR_AUTOMATION_TOKEN }}
JRELEASER_MAVENCENTRAL_USERNAME: ${{ secrets.MAVEN_CENTRAL_USERNAME }}
JRELEASER_MAVENCENTRAL_PASSWORD: ${{ secrets.MAVEN_CENTRAL_PASSWORD }}
JRELEASER_GPG_SECRET_KEY: ${{ secrets.SIGNING_KEY }}
JRELEASER_GPG_PASSPHRASE: ${{ secrets.SIGNING_PASSWORD }}
JRELEASER_GPG_PUBLIC_KEY: ${{ secrets.SIGNING_PUBLIC_KEY }}

Determine-Release-Type:
needs: [Calculate-Version]
if: ${{ needs.Calculate-Version.outputs.next_version != '' }}
uses: ./.github/workflows/determine-release-type.yml

Workflow-Summary:
name: Workflow Summary
needs: [Build, Security-Scan, Publish, Release-Spring-Rules, Determine-Release-Type]
if: always()
runs-on: ubuntu-latest
steps:
- name: Output release completion
run: |
echo "Architecture Validator Spring Rules ${{ needs.Determine-Release-Type.outputs.release_type }} release workflow completed"
echo "::notice title=Release Complete::Architecture Validator Spring Rules ${{ needs.Determine-Release-Type.outputs.release_type }} release workflow completed"
35 changes: 35 additions & 0 deletions .github/workflows/spring-rules-security-scan.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: Architecture Validator Spring Rules - Security Scan
on:
schedule:
- cron: '0 8 * * 5' # Every Friday at 08:00 UTC
workflow_dispatch:

jobs:
Read-Version:
name: Read Current Version
runs-on: ubuntu-latest
outputs:
version: ${{ steps.read-version.outputs.version }}
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
fetch-depth: 1
persist-credentials: false

- name: Read version from gradle.properties
id: read-version
run: |
VERSION=$(grep -oP 'version\s*=\s*\K\S+' hexagonal-spring-rules/gradle.properties)
echo "version=$VERSION" >> "$GITHUB_OUTPUT"

Security-Scan:
needs: Read-Version
uses: ./.github/workflows/security-scan.yml
with:
project_name: 'hexagonal-spring-rules'
semantic_version: ${{ needs.Read-Version.outputs.version }}
fail_on_fatal: true
reports_subpath: build/reports
secrets:
NVD_APIKEY_SEDR: ${{ secrets.NVD_APIKEY_SEDR }}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
= Single-Module Spring Example
:toc: left

This example enables the companion Spring rule pack.
It demonstrates a direct dependency from a Spring service to a Spring repository in the adapter layer.

== Run

[source,bash]
----
./gradlew testArchitecture
----

== Expected violations

`OrderService` is a Spring `@Service` that depends directly on `OrderRepository`.
That violates the Spring-flavoured Hexagonal rules because the service bypasses an outbound port and reaches into the adapter layer directly.

== Reports

The Gradle HTML report is under `build/reports/architecture-validator/html/`.
The XML report is under `build/reports/architecture-validator/xml/`.

== Fix

Introduce an outbound port in `application.port.out`.
Make the service depend on that port.
Move the repository implementation behind an adapter that satisfies the port contract.
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
plugins {
id 'java'
alias(libs.plugins.architecture.validator.iff)
}

group = 'com.example.spring'
version = '0.0.1'

java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}

repositories {
mavenCentral()
}

dependencies {
implementation libs.spring.context.iff
testArchitectureImplementation libs.architecture.validator.hexagonal.spring.rules.iff
}

architectureValidator {
basePackage = 'com.example.spring'
ignoreFailures = true
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
## Generated by $ ./gradlew refreshVersionsCatalog

[plugins]

architecture-validator-iff = { id = "com.arc-e-tect.architecture-validator", version.ref = "architecture-validator-iff" }

[versions]

architecture-validator-iff = "0.4.1"
architecture-validator-hexagonal-spring-rules-iff = "1.0.0"
spring-context-iff = "6.2.8"
## ⬆ = "6.2.9"
## ⬆ = "6.2.10"
## ⬆ = "6.2.11"
## ⬆ = "6.2.12"
## ⬆ = "6.2.13"
## ⬆ = "6.2.14"
## ⬆ = "6.2.15"
## ⬆ = "6.2.16"
## ⬆ = "6.2.17"
## ⬆ = "6.2.18"
## ⬆ = "6.2.19"
## ⬆ = "7.0.0"
## ⬆ = "7.0.1"
## ⬆ = "7.0.2"
## ⬆ = "7.0.3"
## ⬆ = "7.0.4"
## ⬆ = "7.0.5"
## ⬆ = "7.0.6"
## ⬆ = "7.0.7"
## ⬆ = "7.0.8"

[libraries]

architecture-validator-hexagonal-spring-rules-iff = { module = "com.arc-e-tect.architecture-validator:architecture-validator-hexagonal-spring-rules", version.ref = "architecture-validator-hexagonal-spring-rules-iff" }
spring-context-iff = { module = "org.springframework:spring-context", version.ref = "spring-context-iff" }
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.1-bin.zip
networkTimeout=10000
retries=0
retryBackOffMs=500
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Loading
Loading