This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
name: Build and Push Docker Image
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
branches:
|
||||
- develop
|
||||
- 'feature/*'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build-and-push:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Compute image name and tag
|
||||
id: compute-tag
|
||||
run: |
|
||||
REGISTRY=$(echo "${{ github.server_url }}" | sed 's|https\?://||')
|
||||
OWNER=$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]')
|
||||
ACTOR=$(echo "${{ github.actor }}" | tr '[:upper:]' '[:lower:]')
|
||||
IMAGE_NAME="javadocviewerservice"
|
||||
FULL_IMAGE="${REGISTRY}/${OWNER}/${IMAGE_NAME}"
|
||||
|
||||
REF="${{ github.ref_name }}"
|
||||
if [[ "${REF}" == v* ]]; then
|
||||
TAG="${REF#v}"
|
||||
TAGS="${FULL_IMAGE}:${TAG},${FULL_IMAGE}:latest"
|
||||
else
|
||||
TAG=$(echo "${REF}" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9._-]/-/g')
|
||||
TAGS="${FULL_IMAGE}:${TAG}"
|
||||
fi
|
||||
|
||||
echo "Registry: ${REGISTRY}"
|
||||
echo "Actor: ${ACTOR}"
|
||||
echo "Tags: ${TAGS}"
|
||||
|
||||
echo "registry=${REGISTRY}" >> "$GITHUB_OUTPUT"
|
||||
echo "actor=${ACTOR}" >> "$GITHUB_OUTPUT"
|
||||
echo "image=${FULL_IMAGE}" >> "$GITHUB_OUTPUT"
|
||||
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
|
||||
echo "tags=${TAGS}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Network Debug
|
||||
run: |
|
||||
curl -v https://git.mein-gateway.de/v2/
|
||||
|
||||
- name: Debug Secret
|
||||
run: |
|
||||
echo "Secret length: ${#TOKEN}"
|
||||
env:
|
||||
TOKEN: ${{ secrets.REGISTRY_TOKEN }}
|
||||
|
||||
- name: Log in to Gitea Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: git.mein-gateway.de
|
||||
username: thischwa
|
||||
password: ${{ secrets.REGISTRY_TOKEN }}
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
tags: ${{ steps.compute-tag.outputs.tags }}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**/target/
|
||||
!**/src/test/**/target/
|
||||
.kotlin
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea/
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### Eclipse ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
build/
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
|
||||
### Mac OS ###
|
||||
.DS_Store
|
||||
|
||||
## local ##
|
||||
/database/
|
||||
/javadoc-storage/
|
||||
/jdvs.yml
|
||||
/.claude/
|
||||
/info.txt
|
||||
@@ -0,0 +1,33 @@
|
||||
# JavadocViewerService
|
||||
|
||||
## Tech Stack
|
||||
|
||||
- **Java 21**
|
||||
- **Spring Boot 4.x** (spring-boot-starter-parent)
|
||||
- Spring MVC (spring-boot-starter-web)
|
||||
- Spring Data JPA (spring-boot-starter-data-jpa)
|
||||
- Thymeleaf (spring-boot-starter-thymeleaf)
|
||||
- Bean Validation (spring-boot-starter-validation)
|
||||
- Liquibase (spring-boot-starter-liquibase)
|
||||
- **H2** — file-based embedded database (`./database/jdvsdb`)
|
||||
- **Lombok** — boilerplate reduction (`@Data`, `@RequiredArgsConstructor`, `@Slf4j`)
|
||||
- **JGit 7.x** — Git operations (clone, fetch, tag resolution)
|
||||
- **Jackson YAML** — parsing of `jdvs.yml` config file
|
||||
- **Maven** — build tool
|
||||
|
||||
## Configuration
|
||||
|
||||
- `application.yml` — base configuration (datasource, JPA, default jdvs properties)
|
||||
- `jdvs.yml` — external runtime config (repositories, property overrides); imported via `spring.config.import`
|
||||
|
||||
## Package Structure
|
||||
|
||||
- `codes.thischwa.jdvs.config` — `@ConfigurationProperties` beans
|
||||
- `codes.thischwa.jdvs.service` — business logic (Git, Javadoc generation, scheduling)
|
||||
- `codes.thischwa.jdvs.jpa` — Spring Data repositories
|
||||
- `codes.thischwa.jdvs.model` — JPA entities
|
||||
- `codes.thischwa.jdvs.web` — MVC controllers and `WebMvcConfigurer`
|
||||
|
||||
## Coding
|
||||
|
||||
The generated code follows the principles of clean architecture and follows the SOLID principles. It is designed to be modular, testable, and maintainable. The code is written in a way that promotes readability and reduces complexity. It uses modern Java features and best practices to ensure efficient and reliable operation. To formate the code use che checkstyle definition in [google_custom_checks.xml](src/checkstyle/google_custom_checks.xml). Each changing of code must be verified with compiling.
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
# Stage 1: Build the JAR using Maven
|
||||
FROM maven:3.9-eclipse-temurin-21 AS builder
|
||||
|
||||
LABEL org.opencontainers.image.description="JavadocViewerService serves and manages Javadoc documentation from Git repositories."
|
||||
|
||||
WORKDIR /build
|
||||
|
||||
# Copy pom.xml and download dependencies first (for Docker cache)
|
||||
COPY pom.xml .
|
||||
RUN mvn dependency:go-offline
|
||||
|
||||
# Copy the full source tree and build the application
|
||||
COPY src ./src
|
||||
RUN mvn clean package -DskipTests
|
||||
|
||||
# Stage 2: Minimal runtime image
|
||||
FROM eclipse-temurin:21-jdk-jammy
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Install tini and Maven
|
||||
RUN apt-get update && apt-get install -y tini maven && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Copy the built jar from the builder stage
|
||||
COPY --from=builder /build/target/*.jar /app/jvs.jar
|
||||
|
||||
VOLUME ["/app/javadoc-storage", "/app/database"]
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
ENTRYPOINT ["/usr/bin/tini", "--"]
|
||||
CMD ["java", "-jar", "jvs.jar"]
|
||||
@@ -0,0 +1,16 @@
|
||||
MIT No Attribution
|
||||
|
||||
Copyright <YEAR> <COPYRIGHT HOLDER>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
software and associated documentation files (the "Software"), to deal in the Software
|
||||
without restriction, including without limitation the rights to use, copy, modify,
|
||||
merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
@@ -0,0 +1,77 @@
|
||||
# JavadocViewerService
|
||||
|
||||
## Preface
|
||||
|
||||
This project Spring Boot web service that automatically fetches, manages, and serves Javadoc documentation for Maven artifacts.
|
||||
|
||||
If you encounter any bugs or find missing features, feel free to report them on
|
||||
the [GitHub Issues page](https://github.com/th-schwarz/JavadocViewerService/issues).
|
||||
|
||||
## Features
|
||||
|
||||
- Retrieves Javadoc JARs from Maven Central or custom Maven repositories
|
||||
- Tracks multiple Maven artefacts with version detection (only the latest version is taken into account).
|
||||
- Delivers generated Javadoc content via a simple bootstrap web interface
|
||||
- Scheduled updates via a configurable cron expression
|
||||
|
||||
## Requirements
|
||||
|
||||
At least JRE-21 or docker
|
||||
|
||||
## Start
|
||||
### ... with Java
|
||||
|
||||
```bash
|
||||
java -jar jdvc-<version>.jar --spring.config.import=file:./jdvs.yml
|
||||
```
|
||||
|
||||
### .. with Docker
|
||||
|
||||
```yaml
|
||||
services:
|
||||
jvs-dockerized:
|
||||
image: git.mein-gateway.de/thischwa/javadocviewerservice:develop
|
||||
volumes:
|
||||
- /opt/javadocviewerservice-dockerized/jdvs.yml:/app/jdvs.yml:ro
|
||||
- /opt/javadocviewerservice-dockerized/database:/app/database
|
||||
- /opt/javadocviewerservice-dockerized/javadoc-storage:/app/javadoc-storage
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "127.0.0.1:8086:8080"
|
||||
```
|
||||
|
||||
Volume mapping:
|
||||
|
||||
| Mount | Purpose |
|
||||
|---|---|
|
||||
| `jdvs.yml:/app/jdvs.yml:ro` | Injects the repository configuration (which artifacts to track) as read-only — the only file you need to edit |
|
||||
| `database:/app/database` | Persists the H2 database so tracked version metadata survives restarts and image updates |
|
||||
| `javadoc-storage:/app/javadoc-storage` | Persists generated Javadoc HTML so it is not lost on container recreation |
|
||||
|
||||
|
||||
## Configuration
|
||||
|
||||
### `application.yml`
|
||||
|
||||
Key defaults:
|
||||
|
||||
| Property | Default | Description |
|
||||
|---|----------------------------------|------------------------------------|
|
||||
| `jdvs.base-dir` | `./javadoc-storage` | Storage root for generated Javadoc |
|
||||
| `jdvs.cron` | `0 0/30 * * * ?` | Update schedule (every 30 min) |
|
||||
| `jdvs.clean-on-start` | `false` | Wipe storage and DB on startup |
|
||||
| `jdvs.run-on-start` | `false` | Run update immediately on startup |
|
||||
| `jdvs.maven-central-url` | `https://repo1.maven.org/maven2` | Default Maven repository |
|
||||
|
||||
### `jdvs.yml`
|
||||
|
||||
Defines the repositories to track and individual settings (all key defaults can be overridden):
|
||||
|
||||
```yaml
|
||||
jdvs:
|
||||
repositories:
|
||||
- name: my-lib
|
||||
group-id: com.example
|
||||
artifact-id: my-lib
|
||||
# maven-repo-url: https://custom.repo/maven # optional, defaults to Maven Central
|
||||
```
|
||||
@@ -0,0 +1,144 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>codes.thischwa</groupId>
|
||||
<artifactId>jdvc</artifactId>
|
||||
<version>0.2.0-SNAPSHOT</version>
|
||||
|
||||
<name>JavadocViewerService</name>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>4.0.5</version>
|
||||
<relativePath />
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<java.version>21</java.version>
|
||||
<maven.compiler.source>${java.version}</maven.compiler.source>
|
||||
<maven.compiler.target>${java.version}</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<issueManagement>
|
||||
<url>https://git.mein-gateway.de/thischwa/JavadocViewerService/issues</url>
|
||||
<system>Gitea Issues</system>
|
||||
</issueManagement>
|
||||
|
||||
<scm>
|
||||
<developerConnection>scm:git:https://git.mein-gateway.de/thischwa/JavadocViewerService.git</developerConnection>
|
||||
<connection>scm:git:https://git.mein-gateway.de/thischwa/JavadocViewerService.git</connection>
|
||||
<url>https://git.mein-gateway.de/thischwa/JavadocViewerService</url>
|
||||
<tag>HEAD</tag>
|
||||
</scm>
|
||||
|
||||
<distributionManagement>
|
||||
<repository>
|
||||
<id>mygitea</id>
|
||||
<url>https://git.mein-gateway.de/api/packages/thischwa/maven</url>
|
||||
<releases>
|
||||
<enabled>true</enabled>
|
||||
</releases>
|
||||
</repository>
|
||||
<snapshotRepository>
|
||||
<id>mygitea</id>
|
||||
<url>https://git.mein-gateway.de/api/packages/thischwa/maven</url>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
</snapshotRepository>
|
||||
</distributionManagement>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-thymeleaf</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-liquibase</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jgit</groupId>
|
||||
<artifactId>org.eclipse.jgit</artifactId>
|
||||
<version>7.2.1.202505142326-r</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.dataformat</groupId>
|
||||
<artifactId>jackson-dataformat-yaml</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<annotationProcessorPaths>
|
||||
<path>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-sources</id>
|
||||
<goals>
|
||||
<goal>jar-no-fork</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<!-- build the fat jar -->
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,484 @@
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE module PUBLIC
|
||||
"-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
|
||||
"https://checkstyle.org/dtds/configuration_1_3.dtd">
|
||||
|
||||
<!--
|
||||
Checkstyle configuration that checks the Google coding conventions from Google Java Style
|
||||
that can be found at https://google.github.io/styleguide/javaguide.html
|
||||
|
||||
Checkstyle is very configurable. Be sure to read the documentation at
|
||||
http://checkstyle.org (or in your downloaded distribution).
|
||||
|
||||
To completely disable a check, just comment it out or delete it from the file.
|
||||
To suppress certain violations please review suppression filters.
|
||||
|
||||
Authors: Max Vetrenko, Mauryan Kansara, Ruslan Diachenko, Roman Ivanov.
|
||||
-->
|
||||
<!-- version: 13.1.0 -->
|
||||
|
||||
|
||||
<module name="Checker">
|
||||
|
||||
<property name="charset" value="UTF-8"/>
|
||||
|
||||
<property name="severity" value="${org.checkstyle.google.severity}" default="warning"/>
|
||||
|
||||
<property name="fileExtensions" value="java, properties, xml"/>
|
||||
<!-- Excludes all 'module-info.java' files -->
|
||||
<!-- See https://checkstyle.org/filefilters/index.html -->
|
||||
<module name="BeforeExecutionExclusionFileFilter">
|
||||
<property name="fileNamePattern" value="module\-info\.java$"/>
|
||||
</module>
|
||||
|
||||
<module name="SuppressWarningsFilter"/>
|
||||
|
||||
<!-- https://checkstyle.org/filters/suppressionfilter.html -->
|
||||
<module name="SuppressionFilter">
|
||||
<property name="file" value="${org.checkstyle.google.suppressionfilter.config}"
|
||||
default="checkstyle-suppressions.xml" />
|
||||
<property name="optional" value="true"/>
|
||||
</module>
|
||||
|
||||
<!-- https://checkstyle.org/filters/suppresswithnearbytextfilter.html -->
|
||||
<module name="SuppressWithNearbyTextFilter">
|
||||
<property name="nearbyTextPattern"
|
||||
value="CHECKSTYLE.SUPPRESS\: (\w+) for ([+-]\d+) lines"/>
|
||||
<property name="checkPattern" value="$1"/>
|
||||
<property name="lineRange" value="$2"/>
|
||||
</module>
|
||||
|
||||
<!-- Checks for whitespace -->
|
||||
<!-- See http://checkstyle.org/checks/whitespace/index.html -->
|
||||
<module name="FileTabCharacter">
|
||||
<property name="eachLine" value="true"/>
|
||||
</module>
|
||||
|
||||
<module name="LineLength">
|
||||
<property name="fileExtensions" value="java"/>
|
||||
<property name="max" value="140"/>
|
||||
<property name="ignorePattern"
|
||||
value="^package.*|^import.*|href\s*=\s*"[^"]*"|http://|https://|ftp://"/>
|
||||
</module>
|
||||
<!-- Suppression to prevent LineLength Check from flagging lines in Text-blocks -->
|
||||
<module name="SuppressWithPlainTextCommentFilter">
|
||||
<property name="checkFormat" value="LineLength"/>
|
||||
<property name="offCommentFormat" value='^.*"""\s*$'/>
|
||||
<property name="onCommentFormat" value='^\s*"""\s*(?:[,;]|.+)$'/>
|
||||
</module>
|
||||
<module name="TreeWalker">
|
||||
<module name="OuterTypeFilename"/>
|
||||
<module name="MatchXpath">
|
||||
<property name="id" value="singleLineCommentStartWithSpace"/>
|
||||
<property name="query"
|
||||
value="//SINGLE_LINE_COMMENT[./COMMENT_CONTENT[not(starts-with(@text, ' '))
|
||||
and not(starts-with(@text, '/'))
|
||||
and not(@text = '\n') and not(ends-with(@text, '//\n'))]]"/>
|
||||
<message key="matchxpath.match" value="''//'' must be followed by a whitespace."/>
|
||||
</module>
|
||||
<module name="IllegalTokenText">
|
||||
<property name="tokens" value="STRING_LITERAL, CHAR_LITERAL, TEXT_BLOCK_CONTENT"/>
|
||||
<property name="format"
|
||||
value="\\u00(08|09|0(a|A)|0(c|C)|0(d|D)|20|22|27|5(C|c))|\\(0(10|11|12|14|15|40|42|47)|134)"/>
|
||||
<property name="message"
|
||||
value="Consider using special escape sequence instead of octal value or Unicode escaped value."/>
|
||||
</module>
|
||||
<module name="AvoidEscapedUnicodeCharacters">
|
||||
<property name="allowEscapesForControlCharacters" value="true"/>
|
||||
<property name="allowByTailComment" value="true"/>
|
||||
<property name="allowNonPrintableEscapes" value="true"/>
|
||||
</module>
|
||||
<module name="AvoidStarImport"/>
|
||||
<module name="OneTopLevelClass"/>
|
||||
<module name="NoLineWrap">
|
||||
<property name="tokens" value="PACKAGE_DEF, IMPORT, STATIC_IMPORT"/>
|
||||
</module>
|
||||
<module name="NeedBraces">
|
||||
<property name="tokens"
|
||||
value="LITERAL_DO, LITERAL_ELSE, LITERAL_FOR, LITERAL_IF, LITERAL_WHILE"/>
|
||||
</module>
|
||||
<module name="LeftCurly">
|
||||
<property name="id" value="LeftCurlyEol"/>
|
||||
<property name="tokens"
|
||||
value="ANNOTATION_DEF, CLASS_DEF, CTOR_DEF, ENUM_CONSTANT_DEF, ENUM_DEF,
|
||||
INTERFACE_DEF, LAMBDA, LITERAL_CATCH,
|
||||
LITERAL_DO, LITERAL_ELSE, LITERAL_FINALLY, LITERAL_FOR, LITERAL_IF,
|
||||
LITERAL_SWITCH, LITERAL_SYNCHRONIZED, LITERAL_TRY, LITERAL_WHILE, METHOD_DEF,
|
||||
OBJBLOCK, STATIC_INIT, RECORD_DEF, COMPACT_CTOR_DEF"/>
|
||||
</module>
|
||||
<module name="LeftCurly">
|
||||
<property name="id" value="LeftCurlyNl"/>
|
||||
<property name="option" value="nl"/>
|
||||
<property name="tokens"
|
||||
value="LITERAL_CASE, LITERAL_DEFAULT"/>
|
||||
</module>
|
||||
<module name="SuppressionXpathSingleFilter">
|
||||
<!-- LITERAL_CASE, LITERAL_DEFAULT are reused in SWITCH_RULE -->
|
||||
<property name="id" value="LeftCurlyNl"/>
|
||||
<property name="query" value="//SWITCH_RULE/SLIST"/>
|
||||
</module>
|
||||
<module name="RightCurly">
|
||||
<property name="id" value="RightCurlySame"/>
|
||||
<property name="tokens"
|
||||
value="LITERAL_TRY, LITERAL_CATCH, LITERAL_IF, LITERAL_ELSE,
|
||||
LITERAL_DO"/>
|
||||
</module>
|
||||
<module name="SuppressionXpathSingleFilter">
|
||||
<property name="id" value="RightCurlySame"/>
|
||||
<property name="query" value="//RCURLY[parent::SLIST[parent::LITERAL_CATCH
|
||||
and not(parent::LITERAL_CATCH/following-sibling::*)]]"/>
|
||||
</module>
|
||||
<module name="RightCurly">
|
||||
<property name="id" value="RightCurlyAlone"/>
|
||||
<property name="option" value="alone"/>
|
||||
<property name="tokens"
|
||||
value="CLASS_DEF, METHOD_DEF, CTOR_DEF, LITERAL_FOR, LITERAL_WHILE, STATIC_INIT,
|
||||
INSTANCE_INIT, ANNOTATION_DEF, ENUM_DEF, INTERFACE_DEF, RECORD_DEF,
|
||||
COMPACT_CTOR_DEF, LITERAL_SWITCH, LITERAL_CASE, LITERAL_FINALLY,
|
||||
LITERAL_CATCH"/>
|
||||
</module>
|
||||
<module name="SuppressionXpathSingleFilter">
|
||||
<!-- suppression is required till https://github.com/checkstyle/checkstyle/issues/7541 -->
|
||||
<property name="id" value="RightCurlyAlone"/>
|
||||
<property name="query" value="//RCURLY[parent::SLIST[count(./*)=1
|
||||
and not(parent::LITERAL_CATCH)]
|
||||
or (preceding-sibling::*[last()][self::LCURLY]
|
||||
and not(parent::SLIST/parent::LITERAL_CATCH))
|
||||
or (parent::SLIST/parent::LITERAL_CATCH
|
||||
and parent::SLIST/parent::LITERAL_CATCH/following-sibling::*)]"/>
|
||||
</module>
|
||||
<module name="WhitespaceAfter">
|
||||
<property name="tokens"
|
||||
value="COMMA, SEMI, TYPECAST, ELLIPSIS, LITERAL_YIELD, LITERAL_CASE, ANNOTATIONS"/>
|
||||
</module>
|
||||
<module name="WhitespaceAround">
|
||||
<property name="allowEmptyConstructors" value="true"/>
|
||||
<property name="allowEmptyLambdas" value="true"/>
|
||||
<property name="allowEmptyMethods" value="true"/>
|
||||
<property name="allowEmptyTypes" value="true"/>
|
||||
<property name="allowEmptyLoops" value="true"/>
|
||||
<property name="allowEmptySwitchBlockStatements" value="true"/>
|
||||
<property name="ignoreEnhancedForColon" value="false"/>
|
||||
<property name="tokens"
|
||||
value="ASSIGN, BAND, BAND_ASSIGN, BOR, BOR_ASSIGN, BSR, BSR_ASSIGN, BXOR,
|
||||
BXOR_ASSIGN, COLON, DIV, DIV_ASSIGN, DO_WHILE, EQUAL, GE, GT, LAMBDA, LAND,
|
||||
LCURLY, LE, LITERAL_CATCH, LITERAL_DO, LITERAL_ELSE, LITERAL_FINALLY,
|
||||
LITERAL_FOR, LITERAL_IF, LITERAL_RETURN, LITERAL_SWITCH, LITERAL_SYNCHRONIZED,
|
||||
LITERAL_TRY, LITERAL_WHILE, LOR, LT, MINUS, MINUS_ASSIGN, MOD, MOD_ASSIGN,
|
||||
NOT_EQUAL, PLUS, PLUS_ASSIGN, QUESTION, RCURLY, SL, SLIST, SL_ASSIGN, SR,
|
||||
SR_ASSIGN, STAR, STAR_ASSIGN, LITERAL_ASSERT,
|
||||
TYPE_EXTENSION_AND, LITERAL_WHEN"/>
|
||||
<message key="ws.notFollowed"
|
||||
value="WhitespaceAround: ''{0}'' is not followed by whitespace. Empty blocks
|
||||
may only be represented as '{}' when not part of a multi-block statement (4.1.3)"/>
|
||||
<message key="ws.notPreceded"
|
||||
value="WhitespaceAround: ''{0}'' is not preceded with whitespace."/>
|
||||
</module>
|
||||
<module name="SuppressionXpathSingleFilter">
|
||||
<property name="checks" value="WhitespaceAround"/>
|
||||
<property name="query" value="//*[self::LITERAL_IF or self::LITERAL_ELSE or
|
||||
self::STATIC_INIT]/SLIST[count(./*)=1]
|
||||
| //*[self::STATIC_INIT or self::LITERAL_TRY or self::LITERAL_IF]
|
||||
//*[self::RCURLY][parent::SLIST[count(./*)=1]]
|
||||
| //SLIST[count(./*)=1][parent::LITERAL_TRY and
|
||||
not(following-sibling::*)]
|
||||
| //SLIST[count(./*)=1][parent::LITERAL_CATCH and
|
||||
not(parent::LITERAL_CATCH/following-sibling::*)]"/>
|
||||
</module>
|
||||
<module name="RegexpSinglelineJava">
|
||||
<property name="format" value="\{[ ]+\}"/>
|
||||
<property name="message" value="Empty blocks should have no spaces. Empty blocks
|
||||
may only be represented as '{}' when not part of a
|
||||
multi-block statement (4.1.3)"/>
|
||||
</module>
|
||||
<module name="OneStatementPerLine"/>
|
||||
<module name="MultipleVariableDeclarations"/>
|
||||
<module name="ArrayTypeStyle"/>
|
||||
<module name="JavadocLeadingAsteriskAlign"/>
|
||||
<module name="JavadocMissingLeadingAsterisk"/>
|
||||
<module name="JavadocContentLocation"/>
|
||||
<module name="MissingSwitchDefault"/>
|
||||
<module name="FallThrough"/>
|
||||
<module name="UpperEll"/>
|
||||
<module name="ModifierOrder"/>
|
||||
<module name="TextBlockGoogleStyleFormatting"/>
|
||||
<module name="EmptyLineSeparator">
|
||||
<property name="tokens"
|
||||
value="PACKAGE_DEF, IMPORT, STATIC_IMPORT, CLASS_DEF, INTERFACE_DEF, ENUM_DEF,
|
||||
STATIC_INIT, INSTANCE_INIT, METHOD_DEF, CTOR_DEF, VARIABLE_DEF, RECORD_DEF,
|
||||
COMPACT_CTOR_DEF"/>
|
||||
<property name="allowNoEmptyLineBetweenFields" value="true"/>
|
||||
<property name="allowMultipleEmptyLines" value="false"/>
|
||||
</module>
|
||||
<module name="SeparatorWrap">
|
||||
<property name="id" value="SeparatorWrapDot"/>
|
||||
<property name="tokens" value="DOT"/>
|
||||
<property name="option" value="nl"/>
|
||||
</module>
|
||||
<module name="SeparatorWrap">
|
||||
<property name="id" value="SeparatorWrapComma"/>
|
||||
<property name="tokens" value="COMMA"/>
|
||||
<property name="option" value="EOL"/>
|
||||
</module>
|
||||
<module name="SeparatorWrap">
|
||||
<!-- ELLIPSIS is EOL until https://github.com/google/styleguide/issues/259 -->
|
||||
<property name="id" value="SeparatorWrapEllipsis"/>
|
||||
<property name="tokens" value="ELLIPSIS"/>
|
||||
<property name="option" value="EOL"/>
|
||||
</module>
|
||||
<module name="SeparatorWrap">
|
||||
<!-- ARRAY_DECLARATOR is EOL until https://github.com/google/styleguide/issues/258 -->
|
||||
<property name="id" value="SeparatorWrapArrayDeclarator"/>
|
||||
<property name="tokens" value="ARRAY_DECLARATOR"/>
|
||||
<property name="option" value="EOL"/>
|
||||
</module>
|
||||
<module name="SeparatorWrap">
|
||||
<property name="id" value="SeparatorWrapMethodRef"/>
|
||||
<property name="tokens" value="METHOD_REF"/>
|
||||
<property name="option" value="nl"/>
|
||||
</module>
|
||||
<module name="PackageName">
|
||||
<property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Package name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="TypeName">
|
||||
<property name="format" value="^[A-Z][a-zA-Z0-9]*(?:[0-9](?:_[0-9]+)*)?$"/>
|
||||
<property name="tokens" value="CLASS_DEF"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Type name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="TypeName">
|
||||
<property name="tokens" value="INTERFACE_DEF, ENUM_DEF,
|
||||
ANNOTATION_DEF, RECORD_DEF"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Type name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="MemberName">
|
||||
<property name="format"
|
||||
value="^(?![a-z]$)(?![a-z][A-Z])[a-z][a-zA-Z0-9]*(?:_[0-9]+)*$"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Member name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="ParameterName">
|
||||
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Parameter name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="LambdaParameterName">
|
||||
<property name="format" value="^(_|[a-z]([a-z0-9][a-zA-Z0-9]*)?)$"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Lambda parameter name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="CatchParameterName">
|
||||
<property name="format" value="^(_|[a-z]([a-z0-9][a-zA-Z0-9]*)?)$"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Catch parameter name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="LocalVariableName">
|
||||
<property name="format" value="^(_|[a-z]([a-z0-9][a-zA-Z0-9]*)?)$"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Local variable name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="PatternVariableName">
|
||||
<property name="format" value="^(_|[a-z]([a-z0-9][a-zA-Z0-9]*)?)$"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Pattern variable name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="ClassTypeParameterName">
|
||||
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Class type name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="RecordComponentName">
|
||||
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Record component name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="RecordTypeParameterName">
|
||||
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Record type name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="MethodTypeParameterName">
|
||||
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Method type name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="InterfaceTypeParameterName">
|
||||
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Interface type name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="NoFinalizer"/>
|
||||
<module name="GenericWhitespace">
|
||||
<message key="ws.followed"
|
||||
value="GenericWhitespace ''{0}'' is followed by whitespace."/>
|
||||
<message key="ws.preceded"
|
||||
value="GenericWhitespace ''{0}'' is preceded with whitespace."/>
|
||||
<message key="ws.illegalFollow"
|
||||
value="GenericWhitespace ''{0}'' should followed by whitespace."/>
|
||||
<message key="ws.notPreceded"
|
||||
value="GenericWhitespace ''{0}'' is not preceded with whitespace."/>
|
||||
</module>
|
||||
<module name="Indentation">
|
||||
<property name="basicOffset" value="2"/>
|
||||
<property name="braceAdjustment" value="2"/>
|
||||
<property name="caseIndent" value="2"/>
|
||||
<property name="throwsIndent" value="4"/>
|
||||
<property name="lineWrappingIndentation" value="4"/>
|
||||
<property name="arrayInitIndent" value="2"/>
|
||||
</module>
|
||||
|
||||
<module name="AbbreviationAsWordInName">
|
||||
<property name="ignoreFinal" value="false"/>
|
||||
<property name="allowedAbbreviationLength" value="0"/>
|
||||
<property name="tokens"
|
||||
value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, ANNOTATION_DEF, ANNOTATION_FIELD_DEF,
|
||||
PARAMETER_DEF, VARIABLE_DEF, METHOD_DEF, PATTERN_VARIABLE_DEF, RECORD_DEF,
|
||||
RECORD_COMPONENT_DEF"/>
|
||||
</module>
|
||||
<module name="NoWhitespaceBeforeCaseDefaultColon"/>
|
||||
<module name="OverloadMethodsDeclarationOrder"/>
|
||||
<module name="ConstructorsDeclarationGrouping"/>
|
||||
<module name="VariableDeclarationUsageDistance"/>
|
||||
<module name="CustomImportOrder">
|
||||
<property name="sortImportsInGroupAlphabetically" value="true"/>
|
||||
<property name="separateLineBetweenGroups" value="true"/>
|
||||
<property name="customImportOrderRules" value="STATIC###THIRD_PARTY_PACKAGE"/>
|
||||
<property name="tokens" value="IMPORT, STATIC_IMPORT, PACKAGE_DEF"/>
|
||||
</module>
|
||||
<module name="MethodParamPad">
|
||||
<property name="tokens"
|
||||
value="CTOR_DEF, LITERAL_NEW, METHOD_CALL, METHOD_DEF, CTOR_CALL,
|
||||
SUPER_CTOR_CALL, ENUM_CONSTANT_DEF, RECORD_DEF, RECORD_PATTERN_DEF"/>
|
||||
</module>
|
||||
<module name="NoWhitespaceBefore">
|
||||
<property name="tokens"
|
||||
value="COMMA, SEMI, POST_INC, POST_DEC, DOT,
|
||||
LABELED_STAT, METHOD_REF, ELLIPSIS"/>
|
||||
<property name="allowLineBreaks" value="true"/>
|
||||
</module>
|
||||
<module name="SuppressionXpathSingleFilter">
|
||||
<property name="checks" value="NoWhitespaceBefore"/>
|
||||
<property name="query"
|
||||
value="//ELLIPSIS[preceding-sibling::TYPE/ANNOTATIONS[ANNOTATION[LPAREN]
|
||||
or not(following-sibling::*)]]"/>
|
||||
</module>
|
||||
<module name="ParenPad">
|
||||
<property name="tokens"
|
||||
value="ANNOTATION, ANNOTATION_FIELD_DEF, CTOR_CALL, CTOR_DEF, DOT, ENUM_CONSTANT_DEF,
|
||||
EXPR, LITERAL_CATCH, LITERAL_DO, LITERAL_FOR, LITERAL_IF, LITERAL_NEW,
|
||||
LITERAL_SWITCH, LITERAL_SYNCHRONIZED, LITERAL_WHILE, METHOD_CALL,
|
||||
METHOD_DEF, QUESTION, RESOURCE_SPECIFICATION, SUPER_CTOR_CALL, LAMBDA,
|
||||
RECORD_DEF, RECORD_PATTERN_DEF"/>
|
||||
</module>
|
||||
<module name="OperatorWrap">
|
||||
<property name="option" value="NL"/>
|
||||
<property name="tokens"
|
||||
value="BAND, BOR, BSR, BXOR, DIV, EQUAL, GE, GT, LAND, LE, LITERAL_INSTANCEOF, LOR,
|
||||
LT, MINUS, MOD, NOT_EQUAL, PLUS, QUESTION, SL, SR, STAR, METHOD_REF,
|
||||
TYPE_EXTENSION_AND "/>
|
||||
</module>
|
||||
<module name="AnnotationLocation">
|
||||
<property name="id" value="AnnotationLocationMostCases"/>
|
||||
<property name="tokens"
|
||||
value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF,
|
||||
RECORD_DEF, COMPACT_CTOR_DEF, PACKAGE_DEF"/>
|
||||
</module>
|
||||
<module name="AnnotationLocation">
|
||||
<property name="id" value="AnnotationLocationVariables"/>
|
||||
<property name="tokens" value="VARIABLE_DEF"/>
|
||||
<property name="allowSamelineMultipleAnnotations" value="true"/>
|
||||
</module>
|
||||
<module name="NonEmptyAtclauseDescription"/>
|
||||
<module name="InvalidJavadocPosition"/>
|
||||
<module name="JavadocTagContinuationIndentation"/>
|
||||
<module name="SummaryJavadoc">
|
||||
<property name="forbiddenSummaryFragments"
|
||||
value="^@return the *|^This method returns |^A [{]@code [a-zA-Z0-9]+[}]( is a )|^[a-z]"/>
|
||||
</module>
|
||||
<module name="JavadocParagraph">
|
||||
<property name="allowNewlineParagraph" value="false"/>
|
||||
</module>
|
||||
<module name="RequireEmptyLineBeforeBlockTagGroup"/>
|
||||
<module name="AtclauseOrder">
|
||||
<property name="tagOrder" value="@param, @return, @throws, @deprecated"/>
|
||||
<property name="target"
|
||||
value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF, VARIABLE_DEF"/>
|
||||
</module>
|
||||
<module name="JavadocMethod">
|
||||
<property name="accessModifiers" value="public"/>
|
||||
<property name="allowMissingParamTags" value="true"/>
|
||||
<property name="allowMissingReturnTag" value="true"/>
|
||||
<property name="allowedAnnotations" value="Override, Test"/>
|
||||
<property name="tokens" value="METHOD_DEF, CTOR_DEF, ANNOTATION_FIELD_DEF, COMPACT_CTOR_DEF"/>
|
||||
</module>
|
||||
<module name="MissingJavadocMethod">
|
||||
<property name="scope" value="protected"/>
|
||||
<property name="allowMissingPropertyJavadoc" value="true"/>
|
||||
<property name="allowedAnnotations" value="Override, Test"/>
|
||||
<property name="tokens" value="METHOD_DEF, CTOR_DEF, ANNOTATION_FIELD_DEF,
|
||||
COMPACT_CTOR_DEF"/>
|
||||
</module>
|
||||
<module name="SuppressionXpathSingleFilter">
|
||||
<property name="checks" value="MissingJavadocMethod"/>
|
||||
<property name="query" value="//*[self::METHOD_DEF or self::CTOR_DEF
|
||||
or self::ANNOTATION_FIELD_DEF or self::COMPACT_CTOR_DEF]
|
||||
[ancestor::*[self::INTERFACE_DEF or self::CLASS_DEF
|
||||
or self::RECORD_DEF or self::ENUM_DEF]
|
||||
[not(./MODIFIERS/LITERAL_PUBLIC)]]"/>
|
||||
</module>
|
||||
<module name="MissingJavadocType">
|
||||
<property name="scope" value="protected"/>
|
||||
<property name="tokens"
|
||||
value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF,
|
||||
RECORD_DEF, ANNOTATION_DEF"/>
|
||||
<property name="excludeScope" value="nothing"/>
|
||||
</module>
|
||||
<module name="MethodName">
|
||||
<property name="format"
|
||||
value="^(?![a-z]$)(?![a-z][A-Z])[a-z][a-z0-9]*(?:[A-Z][a-z0-9]*)*(?:_[0-9]+)*$"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Method name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="SuppressionXpathSingleFilter">
|
||||
<property name="checks" value="MethodName"/>
|
||||
<property name="query" value="//METHOD_DEF[
|
||||
./MODIFIERS/ANNOTATION//IDENT[contains(@text, 'Test')]
|
||||
]/IDENT"/>
|
||||
<property name="message" value="'[a-z][a-z0-9][a-zA-Z0-9]*(?:_[a-z][a-z0-9][a-zA-Z0-9]*)*'"/>
|
||||
</module>
|
||||
<module name="SingleLineJavadoc"/>
|
||||
<module name="TodoComment">
|
||||
<property name="format" value="^[ \t]*(?!TODO:)(?i:TODO)\b:?"/>
|
||||
<message key="todo.match"
|
||||
value="''TODO:'' must be written in all caps and followed by a colon."/>
|
||||
</module>
|
||||
<module name="EmptyCatchBlock">
|
||||
<property name="commentFormat" value="\w+"/>
|
||||
</module>
|
||||
<module name="CommentsIndentation">
|
||||
<property name="tokens" value="SINGLE_LINE_COMMENT, BLOCK_COMMENT_BEGIN"/>
|
||||
</module>
|
||||
<!-- https://checkstyle.org/filters/suppressionxpathfilter.html -->
|
||||
<module name="SuppressionXpathFilter">
|
||||
<property name="file" value="${org.checkstyle.google.suppressionxpathfilter.config}"
|
||||
default="checkstyle-xpath-suppressions.xml" />
|
||||
<property name="optional" value="true"/>
|
||||
</module>
|
||||
<module name="SuppressWarningsHolder" />
|
||||
<module name="SuppressionCommentFilter">
|
||||
<property name="offCommentFormat" value="CHECKSTYLE.OFF\: ([\w\|]+)" />
|
||||
<property name="onCommentFormat" value="CHECKSTYLE.ON\: ([\w\|]+)" />
|
||||
<property name="checkFormat" value="$1" />
|
||||
</module>
|
||||
<module name="SuppressWithNearbyCommentFilter">
|
||||
<property name="commentFormat" value="CHECKSTYLE.SUPPRESS\: ([\w\|]+)"/>
|
||||
<!-- $1 refers to the first match group in the regex defined in commentFormat -->
|
||||
<property name="checkFormat" value="$1"/>
|
||||
<!-- The check is suppressed in the next line of code after the comment -->
|
||||
<property name="influenceFormat" value="1"/>
|
||||
</module>
|
||||
</module>
|
||||
</module>
|
||||
@@ -0,0 +1,21 @@
|
||||
package codes.thischwa.jdvs;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableScheduling
|
||||
@Slf4j
|
||||
public class JdvsApplication {
|
||||
public static void main(String[] args) {
|
||||
|
||||
try {
|
||||
SpringApplication.run(JdvsApplication.class, args);
|
||||
} catch (Exception e) {
|
||||
log.error("Unexpected exception, Spring Boot stops! Message: {}", e.getMessage());
|
||||
System.exit(10);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package codes.thischwa.jdvs.config;
|
||||
|
||||
import codes.thischwa.jdvs.model.config.JdvsConfig;
|
||||
import codes.thischwa.jdvs.jpa.GitRepositoryRepository;
|
||||
import codes.thischwa.jdvs.model.GitRepository;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class RepoConfigLoader {
|
||||
private final JdvsConfig jdvsConfig;
|
||||
private final GitRepositoryRepository repository;
|
||||
|
||||
public void loadConfig() {
|
||||
if (jdvsConfig.getRepositories() != null) {
|
||||
for (JdvsConfig.RepoConfig repoCfg : jdvsConfig.getRepositories()) {
|
||||
if (repository.findByName(repoCfg.getName()).isEmpty()) {
|
||||
GitRepository repo = new GitRepository();
|
||||
repo.setName(repoCfg.getName());
|
||||
repository.save(repo);
|
||||
log.info("Repository {} added from configuration.", repoCfg.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package codes.thischwa.jdvs.jpa;
|
||||
|
||||
import codes.thischwa.jdvs.model.GitRepository;
|
||||
import java.util.Optional;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface GitRepositoryRepository extends JpaRepository<GitRepository, Long> {
|
||||
Optional<GitRepository> findByName(String name);
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package codes.thischwa.jdvs.model;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import java.time.LocalDateTime;
|
||||
import lombok.Data;
|
||||
|
||||
@Entity
|
||||
@Data
|
||||
public class GitRepository {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(nullable = false, unique = true)
|
||||
private String name;
|
||||
|
||||
private String lastTag;
|
||||
|
||||
private LocalDateTime updated;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package codes.thischwa.jdvs.model.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.client.RestClient;
|
||||
|
||||
@Configuration
|
||||
public class AppConfig {
|
||||
|
||||
@Bean
|
||||
public RestClient restClient() {
|
||||
return RestClient.create();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package codes.thischwa.jdvs.model.config;
|
||||
|
||||
import java.util.List;
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "git")
|
||||
@Data
|
||||
public class GitConfig {
|
||||
private List<String> javadocPaths;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package codes.thischwa.jdvs.model.config;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import lombok.Data;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "jdvs")
|
||||
@Data
|
||||
public class JdvsConfig {
|
||||
private String baseDir;
|
||||
private boolean cleanOnStart;
|
||||
private boolean runOnStart;
|
||||
private String mavenCentralUrl;
|
||||
private List<RepoConfig> repositories;
|
||||
|
||||
public String getEffectiveMavenRepoUrl(RepoConfig cfg) {
|
||||
return Objects.requireNonNullElse(cfg.getMavenRepoUrl(), mavenCentralUrl);
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class RepoConfig {
|
||||
private String name;
|
||||
private String groupId;
|
||||
private String artifactId;
|
||||
private String mavenRepoUrl;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package codes.thischwa.jdvs.service;
|
||||
|
||||
import codes.thischwa.jdvs.config.RepoConfigLoader;
|
||||
import codes.thischwa.jdvs.jpa.GitRepositoryRepository;
|
||||
import codes.thischwa.jdvs.model.config.JdvsConfig;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Comparator;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.context.event.ApplicationReadyEvent;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class ApplicationStartupListener {
|
||||
|
||||
private final GitRepositoryRepository repository;
|
||||
private final JdvsConfig jdvsConfig;
|
||||
private final RepoConfigLoader repoConfigLoader;
|
||||
private final UpdateScheduler updateScheduler;
|
||||
|
||||
@EventListener(ApplicationReadyEvent.class)
|
||||
public void onApplicationReady() {
|
||||
if (jdvsConfig.isCleanOnStart()) {
|
||||
log.info("'jdvs.clean-on-start' is enabled – cleaning base directory and database.");
|
||||
cleanBaseDir();
|
||||
repository.deleteAll();
|
||||
}
|
||||
repoConfigLoader.loadConfig();
|
||||
if (jdvsConfig.isRunOnStart()) {
|
||||
log.info("'jdvs.run-on-start' is enabled – running initial update.");
|
||||
updateScheduler.updateAll();
|
||||
}
|
||||
}
|
||||
|
||||
private void cleanBaseDir() {
|
||||
Path baseDir = Path.of(jdvsConfig.getBaseDir());
|
||||
if (!Files.exists(baseDir)) {
|
||||
return;
|
||||
}
|
||||
try (var stream = Files.walk(baseDir)) {
|
||||
stream.sorted(Comparator.reverseOrder()).forEach(path -> {
|
||||
try {
|
||||
Files.delete(path);
|
||||
} catch (IOException e) {
|
||||
log.warn("Failed to delete path: {}", path, e);
|
||||
}
|
||||
});
|
||||
log.info("Deleted base directory: {}", baseDir);
|
||||
} catch (IOException e) {
|
||||
log.error("Failed to walk base directory: {}", baseDir, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
package codes.thischwa.jdvs.service;
|
||||
|
||||
import codes.thischwa.jdvs.model.config.JdvsConfig;
|
||||
import codes.thischwa.jdvs.model.config.JdvsConfig.RepoConfig;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.Optional;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.client.RestClient;
|
||||
import org.springframework.web.client.RestClientException;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.InputSource;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class MavenJavadocService {
|
||||
|
||||
private final JdvsConfig jdvsConfig;
|
||||
private final RestClient restClient;
|
||||
|
||||
public Optional<String> fetchLatestVersion(String repoName) {
|
||||
RepoConfig cfg = findConfig(repoName);
|
||||
String metadataUrl = baseUrl(cfg) + "/maven-metadata.xml";
|
||||
log.info("Fetching Maven metadata from {}", metadataUrl);
|
||||
try {
|
||||
String xml = restClient.get().uri(metadataUrl).retrieve().body(String.class);
|
||||
Document doc = parseXml(xml);
|
||||
Optional<String> version = resolveVersionTag(doc);
|
||||
if (version.isPresent()) {
|
||||
log.info("Latest version of {} is {}", repoName, version.get());
|
||||
return version;
|
||||
}
|
||||
log.warn("Could not determine latest version for {}", repoName);
|
||||
} catch (ParserConfigurationException | SAXException | IOException e) {
|
||||
log.error("Failed to parse Maven metadata for {}", repoName, e);
|
||||
} catch (RestClientException e) {
|
||||
log.error("Failed to fetch Maven metadata for {}", repoName, e);
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
public boolean generateJavadoc(String repoName, String version) {
|
||||
RepoConfig cfg = findConfig(repoName);
|
||||
String resolvedVersion = version.endsWith("-SNAPSHOT")
|
||||
? resolveSnapshotVersion(cfg, version)
|
||||
: version;
|
||||
String jarUrl = baseUrl(cfg) + "/" + version + "/"
|
||||
+ cfg.getArtifactId() + "-" + resolvedVersion + "-javadoc.jar";
|
||||
log.info("Downloading Javadoc JAR from {}", jarUrl);
|
||||
|
||||
Path outputDir = Path.of(jdvsConfig.getBaseDir(), "javadoc", repoName);
|
||||
try {
|
||||
Files.createDirectories(outputDir);
|
||||
extractJavadocJar(jarUrl, outputDir);
|
||||
log.info("Javadoc successfully extracted for {} to {}", repoName, outputDir);
|
||||
return true;
|
||||
} catch (IOException | RestClientException e) {
|
||||
log.error("Failed to generate Javadoc for {} version {}", repoName, version, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void extractJavadocJar(String jarUrl, Path outputDir) throws IOException {
|
||||
byte[] jarBytes = restClient.get().uri(jarUrl).retrieve().body(byte[].class);
|
||||
if (jarBytes == null) {
|
||||
throw new IOException("Empty response for " + jarUrl);
|
||||
}
|
||||
Path safeOutputDir = outputDir.toAbsolutePath().normalize();
|
||||
try (ZipInputStream zip = new ZipInputStream(new ByteArrayInputStream(jarBytes))) {
|
||||
ZipEntry entry;
|
||||
while ((entry = zip.getNextEntry()) != null) {
|
||||
String name = entry.getName();
|
||||
if (!entry.isDirectory() && !name.startsWith("META-INF/")) {
|
||||
Path dest = safeOutputDir.resolve(name).normalize();
|
||||
if (!dest.startsWith(safeOutputDir)) {
|
||||
throw new IOException("Zip slip detected: " + name);
|
||||
}
|
||||
Files.createDirectories(dest.getParent());
|
||||
Files.copy(zip, dest, StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
zip.closeEntry();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String resolveSnapshotVersion(RepoConfig cfg, String version) {
|
||||
String metadataUrl = baseUrl(cfg) + "/" + version + "/maven-metadata.xml";
|
||||
log.info("Resolving SNAPSHOT version from {}", metadataUrl);
|
||||
try {
|
||||
String xml = restClient.get().uri(metadataUrl).retrieve().body(String.class);
|
||||
Document doc = parseXml(xml);
|
||||
NodeList nodes = doc.getElementsByTagName("snapshotVersion");
|
||||
for (int i = 0; i < nodes.getLength(); i++) {
|
||||
org.w3c.dom.Element el = (org.w3c.dom.Element) nodes.item(i);
|
||||
String classifier = firstText(el.getElementsByTagName("classifier"));
|
||||
String ext = firstText(el.getElementsByTagName("extension"));
|
||||
String value = firstText(el.getElementsByTagName("value"));
|
||||
if ("javadoc".equals(classifier) && "jar".equals(ext) && value != null) {
|
||||
log.info("Resolved SNAPSHOT version to {}", value);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
} catch (ParserConfigurationException | SAXException | IOException e) {
|
||||
log.warn("Could not parse SNAPSHOT metadata for {}, falling back to {}",
|
||||
cfg.getArtifactId(), version, e);
|
||||
} catch (RestClientException e) {
|
||||
log.warn("Could not fetch SNAPSHOT metadata for {}, falling back to {}",
|
||||
cfg.getArtifactId(), version, e);
|
||||
}
|
||||
return version;
|
||||
}
|
||||
|
||||
private String baseUrl(RepoConfig cfg) {
|
||||
String groupPath = cfg.getGroupId().replace('.', '/');
|
||||
return jdvsConfig.getEffectiveMavenRepoUrl(cfg) + "/" + groupPath + "/" + cfg.getArtifactId();
|
||||
}
|
||||
|
||||
private RepoConfig findConfig(String repoName) {
|
||||
return jdvsConfig.getRepositories().stream()
|
||||
.filter(r -> repoName.equals(r.getName()))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new IllegalArgumentException("No config found for: " + repoName));
|
||||
}
|
||||
|
||||
private static Optional<String> resolveVersionTag(Document doc) {
|
||||
return Optional.ofNullable(firstText(doc.getElementsByTagName("release")))
|
||||
.or(() -> Optional.ofNullable(firstText(doc.getElementsByTagName("latest"))));
|
||||
}
|
||||
|
||||
private static String firstText(NodeList nodes) {
|
||||
return nodes.getLength() > 0 ? nodes.item(0).getTextContent() : null;
|
||||
}
|
||||
|
||||
private static Document parseXml(String xml)
|
||||
throws ParserConfigurationException, SAXException, IOException {
|
||||
return DocumentBuilderFactory.newInstance()
|
||||
.newDocumentBuilder()
|
||||
.parse(new InputSource(new StringReader(xml)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package codes.thischwa.jdvs.service;
|
||||
|
||||
import codes.thischwa.jdvs.jpa.GitRepositoryRepository;
|
||||
import codes.thischwa.jdvs.model.GitRepository;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class UpdateScheduler {
|
||||
|
||||
private final GitRepositoryRepository repository;
|
||||
private final MavenJavadocService mavenJavadocService;
|
||||
|
||||
@Scheduled(cron = "${jdvs.cron}")
|
||||
public void updateAll() {
|
||||
log.info("Starting scheduled update of repositories...");
|
||||
List<GitRepository> repos = repository.findAll();
|
||||
for (GitRepository repo : repos) {
|
||||
updateRepo(repo);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateRepo(GitRepository repo) {
|
||||
log.info("Checking repository: {}", repo.getName());
|
||||
Optional<String> latestVersion = mavenJavadocService.fetchLatestVersion(repo.getName());
|
||||
if (latestVersion.isPresent()) {
|
||||
String version = latestVersion.get();
|
||||
if (!version.equals(repo.getLastTag())) {
|
||||
log.info("New version {} found for {}. Generating Javadoc...", version, repo.getName());
|
||||
if (mavenJavadocService.generateJavadoc(repo.getName(), version)) {
|
||||
repo.setLastTag(version);
|
||||
repo.setUpdated(LocalDateTime.now());
|
||||
repository.save(repo);
|
||||
}
|
||||
} else {
|
||||
log.info("Repository {} is already up to date ({}).", repo.getName(), version);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package codes.thischwa.jdvs.web;
|
||||
|
||||
import codes.thischwa.jdvs.jpa.GitRepositoryRepository;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
@Controller
|
||||
@RequiredArgsConstructor
|
||||
public class BadgeController {
|
||||
|
||||
private static final String BADGE_URL_PREFIX =
|
||||
"https://img.shields.io/badge/javadoc%40JdVS-";
|
||||
|
||||
private static final String BADGE_URL_SUFFIX = "-green";
|
||||
|
||||
private final GitRepositoryRepository repository;
|
||||
|
||||
@GetMapping("/badge/{repositoryName}")
|
||||
public String badge(@PathVariable String repositoryName) {
|
||||
String tag =
|
||||
repository
|
||||
.findByName(repositoryName)
|
||||
.map(r -> r.getLastTag() != null ? r.getLastTag() : "unknown")
|
||||
.orElseThrow(
|
||||
() ->
|
||||
new ResponseStatusException(
|
||||
HttpStatus.NOT_FOUND, "Repository not found: " + repositoryName));
|
||||
return "redirect:" + BADGE_URL_PREFIX + tag + BADGE_URL_SUFFIX;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package codes.thischwa.jdvs.web;
|
||||
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* Controller for favicon.ico, just for reducing 404 errors in the logs.
|
||||
*/
|
||||
@RestController
|
||||
public class FaviconController {
|
||||
|
||||
@GetMapping("favicon.ico")
|
||||
void returnNoFavicon() {
|
||||
// see class comment
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package codes.thischwa.jdvs.web;
|
||||
|
||||
import codes.thischwa.jdvs.jpa.GitRepositoryRepository;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
|
||||
@Controller
|
||||
@RequiredArgsConstructor
|
||||
public class UiController {
|
||||
private final GitRepositoryRepository repository;
|
||||
|
||||
@GetMapping("/")
|
||||
public String index(Model model) {
|
||||
model.addAttribute("repos", repository.findAll());
|
||||
return "index";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package codes.thischwa.jdvs.web;
|
||||
|
||||
import codes.thischwa.jdvs.model.config.JdvsConfig;
|
||||
import java.nio.file.Path;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@Configuration
|
||||
@RequiredArgsConstructor
|
||||
public class WebConfig implements WebMvcConfigurer {
|
||||
private final JdvsConfig jdvsConfig;
|
||||
|
||||
@Override
|
||||
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||
String javadocPath = Path.of(jdvsConfig.getBaseDir(), "javadoc").toAbsolutePath().toUri().toString();
|
||||
if (!javadocPath.endsWith("/")) {
|
||||
javadocPath += "/";
|
||||
}
|
||||
registry.addResourceHandler("/**")
|
||||
.addResourceLocations(javadocPath);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
jdvs:
|
||||
base-dir: ./javadoc-storage
|
||||
cron: 0 0/30 * * * ?
|
||||
clean-on-start: false
|
||||
run-on-start: false
|
||||
maven-central-url: https://repo1.maven.org/maven2
|
||||
|
||||
git:
|
||||
javadoc-paths:
|
||||
- target/reports/apidocs
|
||||
- target/apidocs
|
||||
- target/site/apidocs
|
||||
|
||||
## import of the individual configuration settings
|
||||
spring:
|
||||
config:
|
||||
import: optional:file:./jdvs.yml
|
||||
|
||||
datasource:
|
||||
url: jdbc:h2:file:./database/jdvsdb
|
||||
driverClassName: org.h2.Driver
|
||||
username: sa
|
||||
password: ""
|
||||
jpa:
|
||||
database-platform: org.hibernate.dialect.H2Dialect
|
||||
hibernate:
|
||||
ddl-auto: none
|
||||
@@ -0,0 +1,11 @@
|
||||
|
||||
___ _ _ _ _____
|
||||
|_ | | | | | / ___|
|
||||
| | __| | | | \ `--.
|
||||
| |/ _` | | | |`--. \
|
||||
/\__/ / (_| \ \_/ /\__/ /
|
||||
\____/ \__,_|\___/\____/
|
||||
|
||||
Version: ${application.version}
|
||||
:: Spring Boot${spring-boot.formatted-version} ::
|
||||
running on java ${java.version}
|
||||
@@ -0,0 +1,27 @@
|
||||
databaseChangeLog:
|
||||
- changeSet:
|
||||
id: 1
|
||||
author: th-schwarz
|
||||
changes:
|
||||
- createTable:
|
||||
tableName: git_repository
|
||||
columns:
|
||||
- column:
|
||||
name: id
|
||||
type: BIGINT
|
||||
autoIncrement: true
|
||||
constraints:
|
||||
primaryKey: true
|
||||
nullable: false
|
||||
- column:
|
||||
name: name
|
||||
type: VARCHAR(255)
|
||||
constraints:
|
||||
nullable: false
|
||||
unique: true
|
||||
- column:
|
||||
name: last_tag
|
||||
type: VARCHAR(255)
|
||||
- column:
|
||||
name: updated
|
||||
type: TIMESTAMP
|
||||
@@ -0,0 +1,3 @@
|
||||
databaseChangeLog:
|
||||
- include:
|
||||
file: db/changelog/001-init-schema.yaml
|
||||
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration debug="true">
|
||||
|
||||
<appender name="current"
|
||||
class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{HH:mm:ss.SSS} [%t] %-5level %logger{50} - %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<logger name="org.apache.http" level="info"/>
|
||||
<logger name="org.springframework.boot.autoconfigure.logging" level="info"/>
|
||||
<logger name="org.springframework.context" level="info"/>
|
||||
<logger name="org.eclipse.jgit" level="info"/>
|
||||
<logger name="com.zaxxer.hikari" level="info"/>
|
||||
|
||||
<root level="debug">
|
||||
<appender-ref ref="current"/>
|
||||
</root>
|
||||
|
||||
</configuration>
|
||||
@@ -0,0 +1,35 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<title>Javadoc Viewer Service</title>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container mt-5">
|
||||
<h1>Project Javadoc Overview</h1>
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Latest Version</th>
|
||||
<th>Last Update</th>
|
||||
<th>Javadoc link</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="repo : ${repos}">
|
||||
<td th:text="${repo.name}">Project</td>
|
||||
<td th:text="${repo.lastTag}">-</td>
|
||||
<td th:text="${#temporals.format(repo.updated, 'dd.MM.yyyy HH:mm')}">-</td>
|
||||
<td>
|
||||
<a th:if="${repo.lastTag != null}" th:href="@{/{name}/index.html(name=${repo.name})}">
|
||||
<img th:src="@{/badge/{name}(name=${repo.name})}" alt="javadoc badge">
|
||||
</a>
|
||||
<span th:if="${repo.lastTag == null}" class="badge bg-secondary">Not yet generated</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user