name: Java - Architecture Validation
on: workflow_call: inputs: runner: description: 'Runner type' required: false type: string default: 'ubuntu-latest' container_image: description: >- Run the job inside this image (e.g. ghcr.io/codehunters-io/ci-base-images:1.0.0). Empty = plain runner with actions/setup-java. The image already carries the JDK, so the per-run download is skipped and the version is pinned to the tag. Gradle still comes from ./gradlew, and setup-gradle still runs for its dependency cache. required: false type: string default: '' java_version: description: 'Java version (ignored when container_image is set)' required: false type: string default: '21' java_distribution: description: 'Java distribution' required: false type: string default: 'temurin' upload_diagrams: description: 'Upload architecture diagrams as artifact' required: false type: boolean default: false artifact_retention_days: description: 'Days to retain architecture artifacts' required: false type: number default: 30
outputs: arch_test_result: description: 'Architecture test result (success/failure)' value: ${{ jobs.architecture.outputs.arch_test_result }} diagrams_artifact_name: description: 'Name of uploaded diagrams artifact' value: ${{ jobs.architecture.outputs.diagrams_artifact }}
# Least privilege for what this workflow actually does. Without it the# job inherits whatever the calling repository's default happens to be,# which on older repositories is write-all.permissions: contents: read packages: read
jobs: architecture: name: Architecture Validation runs-on: ${{ inputs.runner }} # `container:` takes an object or nothing at all, and there is no way to # write "no container" as a string. fromJSON('null') is how you say it. container: ${{ inputs.container_image != '' && fromJSON(format('{{"image":"{0}"}}', inputs.container_image)) || fromJSON('null') }} timeout-minutes: 15
outputs: arch_test_result: ${{ steps.arch-test.outcome }} diagrams_artifact: ${{ steps.diagrams-info.outputs.name }}
steps: - name: Checkout uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0
- name: Setup JDK ${{ inputs.java_version }} if: inputs.container_image == '' uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1 with: distribution: ${{ inputs.java_distribution }} java-version: ${{ inputs.java_version }}
- name: Report toolchain (container) if: inputs.container_image != '' run: | echo "Container: ${{ inputs.container_image }}" java -version
- name: Setup Gradle uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6.3.0 with: cache-read-only: ${{ github.ref != 'refs/heads/main' && github.ref != 'refs/heads/master' }}
# ============================================ # ARCHUNIT ARCHITECTURE TESTS # ============================================ - name: Run architecture tests id: arch-test env: GH_PACKAGES_USERNAME: ${{ secrets.GH_PACKAGES_USERNAME }} GH_PACKAGES_TOKEN: ${{ secrets.GH_PACKAGES_TOKEN }} run: | echo "Running ArchUnit architecture validation..." ./gradlew test --no-daemon --build-cache --parallel
echo "" >> $GITHUB_STEP_SUMMARY echo "### Architecture Tests" >> $GITHUB_STEP_SUMMARY echo "ArchUnit validation passed." >> $GITHUB_STEP_SUMMARY
# ============================================ # STRUCTURIZR C4 DIAGRAMS # ============================================ - name: Export Structurizr C4 diagrams id: structurizr run: | if ./gradlew tasks --all --no-daemon 2>/dev/null | grep -q "structurizrExport"; then echo "Running Structurizr C4 export..." ./gradlew structurizrExport --no-daemon --build-cache
echo "" >> $GITHUB_STEP_SUMMARY echo "### Structurizr C4 Diagrams" >> $GITHUB_STEP_SUMMARY echo "C4 diagrams exported to PlantUML and Mermaid formats." >> $GITHUB_STEP_SUMMARY else echo "structurizrExport task not found, skipping." fi
# ============================================ # PLANTUML CLASS/PACKAGE DIAGRAMS # ============================================ - name: Generate class and package diagrams id: class-diagrams run: | if ./gradlew tasks --all --no-daemon 2>/dev/null | grep -q "classDiagram"; then echo "Generating PlantUML class and package diagrams..." ./gradlew classDiagram packageDiagram --no-daemon --build-cache || true
echo "" >> $GITHUB_STEP_SUMMARY echo "### Class & Package Diagrams" >> $GITHUB_STEP_SUMMARY echo "PlantUML diagrams generated." >> $GITHUB_STEP_SUMMARY else echo "classDiagram task not found, skipping." fi
# ============================================ # UPLOAD ARCHITECTURE ARTIFACTS # ============================================ - name: Get diagrams artifact info id: diagrams-info if: inputs.upload_diagrams run: | echo "name=architecture-diagrams-${{ github.run_id }}" >> $GITHUB_OUTPUT
- name: Upload architecture diagrams if: inputs.upload_diagrams && always() uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: ${{ steps.diagrams-info.outputs.name }} path: architecture/ if-no-files-found: warn retention-days: ${{ inputs.artifact_retention_days }}Java (Spring Boot)·Reusable workflow·on: workflow_call
Java Architecture
Java - Architecture Validation
.github/workflows/java-architecture.yml