plugins { id "java" id "org.springframework.boot" version "3.4.4" id "io.spring.dependency-management" version "1.1.7" id "org.springdoc.openapi-gradle-plugin" version "1.9.0" id "io.swagger.swaggerhub" version "1.3.2" id "edu.sc.seis.launch4j" version "3.0.6" id "com.diffplug.spotless" version "7.0.3" id "com.github.jk1.dependency-license-report" version "2.9" //id "nebula.lint" version "19.0.3" id("org.panteleyev.jpackageplugin") version "1.6.1" id "org.sonarqube" version "6.1.0.5360" } import com.github.jk1.license.render.* import org.gradle.internal.os.OperatingSystem import java.nio.file.Files import java.time.Year ext { springBootVersion = "3.4.4" pdfboxVersion = "3.0.4" imageioVersion = "3.12.0" lombokVersion = "1.18.38" bouncycastleVersion = "1.80" springSecuritySamlVersion = "6.4.4" openSamlVersion = "4.3.2" tempJrePath = null } group = "stirling.software" version = "0.45.6" java { // 17 is lowest but we support and recommend 21 sourceCompatibility = JavaVersion.VERSION_17 } repositories { mavenCentral() maven { url = "https://build.shibboleth.net/maven/releases" } maven { url = "https://maven.pkg.github.com/jcefmaven/jcefmaven" } } licenseReport { renderers = [new JsonReportRenderer()] allowedLicensesFile = new File("$projectDir/allowed-licenses.json") } sourceSets { main { java { if (System.getenv("DOCKER_ENABLE_SECURITY") == "false") { exclude "stirling/software/SPDF/config/security/**" exclude "stirling/software/SPDF/controller/api/DatabaseController.java" exclude "stirling/software/SPDF/controller/api/UserController.java" exclude "stirling/software/SPDF/controller/api/H2SQLCondition.java" exclude "stirling/software/SPDF/controller/web/AccountWebController.java" exclude "stirling/software/SPDF/controller/web/DatabaseWebController.java" exclude "stirling/software/SPDF/model/ApiKeyAuthenticationToken.java" exclude "stirling/software/SPDF/model/AttemptCounter.java" exclude "stirling/software/SPDF/model/Authority.java" exclude "stirling/software/SPDF/model/BackupNotFoundException.java" exclude "stirling/software/SPDF/model/PersistentLogin.java" exclude "stirling/software/SPDF/model/SessionEntity.java" exclude "stirling/software/SPDF/model/User.java" exclude "stirling/software/SPDF/repository/**" } else { exclude "stirling/software/SPDF/config/anonymous/**" } if (System.getenv("STIRLING_PDF_DESKTOP_UI") == "false") { exclude "stirling/software/SPDF/UI/impl/**" } } } test { java { if (System.getenv("DOCKER_ENABLE_SECURITY") == "false") { exclude "stirling/software/SPDF/config/security/**" exclude "stirling/software/SPDF/controller/api/UserControllerTest.java" exclude "stirling/software/SPDF/controller/api/DatabaseControllerTest.java" exclude "stirling/software/SPDF/controller/web/AccountWebControllerTest.java" exclude "stirling/software/SPDF/controller/web/DatabaseWebControllerTest.java" exclude "stirling/software/SPDF/model/ApiKeyAuthenticationTokenTest.java" exclude "stirling/software/SPDF/model/AttemptCounterTest.java" exclude "stirling/software/SPDF/model/AuthorityTest.java" exclude "stirling/software/SPDF/model/PersistentLoginTest.java" exclude "stirling/software/SPDF/model/SessionEntityTest.java" exclude "stirling/software/SPDF/model/UserTest.java" exclude "stirling/software/SPDF/repository/**" } if (System.getenv("STIRLING_PDF_DESKTOP_UI") == "false") { exclude "stirling/software/SPDF/UI/impl/**" } } } } openApi { apiDocsUrl = "http://localhost:8080/v1/api-docs" outputDir = file("$projectDir") outputFileName = "SwaggerDoc.json" waitTimeInSeconds = 60 // Increase the wait time to 60 seconds } //0.11.5 to 2024.11.5 static def getMacVersion(String version) { def currentYear = Year.now().getValue() def versionParts = version.split("\\.", 2) return "${currentYear}.${versionParts.length > 1 ? versionParts[1] : versionParts[0]}" } jpackage { input = "build/libs" destination = "${projectDir}/build/jpackage" mainJar = "Stirling-PDF-${project.version}.jar" appName = "Stirling-PDF" appVersion = project.version // appVersion = "2005.45.1" vendor = "Stirling-Software" appDescription = "Stirling PDF - Your Local PDF Editor" icon = "src/main/resources/static/favicon.ico" verbose = true // mainClass = "org.springframework.boot.loader.launch.JarLauncher" // JVM Options javaOptions = [ "-DBROWSER_OPEN=true", "-DSTIRLING_PDF_DESKTOP_UI=true", "-Djava.awt.headless=false", "-Dapple.awt.UIElement=true", "--add-opens=java.base/java.lang=ALL-UNNAMED", "--add-opens=java.desktop/java.awt.event=ALL-UNNAMED", "--add-opens=java.desktop/sun.awt=ALL-UNNAMED", "--add-opens=java.desktop/sun.awt.X11=ALL-UNNAMED", "--add-opens=java.desktop/sun.awt.windows=ALL-UNNAMED", "--add-opens=java.desktop/sun.lwawt=ALL-UNNAMED", "--add-opens=java.desktop/sun.lwawt.macosx=ALL-UNNAMED", ] // Windows-specific configuration windows { launcherAsService = false appVersion = project.version winConsole = false winMenu = true // Creates start menu entry winShortcut = true // Creates desktop shortcut winShortcutPrompt = true // Lets user choose whether to create shortcuts winDirChooser = true // Allows users to choose installation directory winPerUserInstall = false winMenuGroup = "Stirling Software" winUpgradeUuid = "2a43ed0c-b8c2-40cf-89e1-751129b87641" // Unique identifier for updates winHelpUrl = "https://github.com/Stirling-Tools/Stirling-PDF" winUpdateUrl = "https://github.com/Stirling-Tools/Stirling-PDF/releases" type = "exe" installDir = "C:/Program Files/Stirling-PDF" } // macOS-specific configuration mac { appVersion = getMacVersion(project.version.toString()) icon = "src/main/resources/static/favicon.icns" type = "dmg" macPackageIdentifier = "Stirling-PDF" macPackageName = "Stirling-PDF" macAppCategory = "public.app-category.productivity" macSign = false // Enable signing macAppStore = false // Not targeting App Store initially // // Add license and other documentation to DMG // /*macDmgContent = [ // "README.md", // "LICENSE", // "CHANGELOG.md" // ]*/ // // // Enable Mac-specific entitlements // //macEntitlements = "entitlements.plist" // You'll need to create this file } // Linux-specific configuration linux { appVersion = project.version icon = "src/main/resources/static/favicon.png" type = "deb" // Can also use "rpm" for Red Hat-based systems // Debian package configuration //linuxPackageName = "stirlingpdf" linuxDebMaintainer = "support@stirlingpdf.com" linuxMenuGroup = "Office;PDF;Productivity" linuxAppCategory = "Office" linuxAppRelease = "1" linuxPackageDeps = true installDir = "/opt/Stirling-PDF" // RPM-specific settings //linuxRpmLicenseType = "MIT" } // Common additional options //jLinkOptions = [ // "--strip-debug", // "--compress=2", // "--no-header-files", // "--no-man-pages" //] // Add any additional modules required /*addModules = [ "java.base", "java.desktop", "java.logging", "java.sql", "java.xml", "jdk.crypto.ec" ]*/ // Add copyright and license information copyright = "Copyright © 2024 Stirling Software" licenseFile = "LICENSE" } tasks.register('jpackageMacX64') { group = 'distribution' description = 'Packages app for MacOS x86_64' println "Running jpackageMacX64 task" if (OperatingSystem.current().isMacOsX()) { println "MacOS detected. Downloading temp JRE." dependsOn("downloadTempJre") } else { return } doLast { def jrePath = project.ext.tempJrePath if (!jrePath) { throw new GradleException("JRE path not found.") } def outputStream = new ByteArrayOutputStream() def errorStream = new ByteArrayOutputStream() def result = exec { commandLine 'jpackage', '--type', 'dmg', '--name', 'Stirling-PDF (x86_64)', '--input', 'build/libs', '--main-jar', "Stirling-PDF-${project.version}.jar", '--main-class', 'org.springframework.boot.loader.launch.JarLauncher', '--runtime-image', file(jrePath + "/zulu-17.jre/Contents/Home"), '--dest', 'build/jpackage/x86_64', '--icon', 'src/main/resources/static/favicon.icns', '--app-version', getMacVersion(project.version.toString()), '--mac-package-name', 'Stirling-PDF (x86_64)', '--mac-package-identifier', 'Stirling-PDF (x86_64)', '--mac-app-category', 'public.app-category.productivity' standardOutput = outputStream errorOutput = errorStream ignoreExitValue = true } def stdout = outputStream.toString("UTF-8") def stderr = errorStream.toString("UTF-8") if (!stdout.isBlank()) { println "jpackage stdout:\n$stdout" } if (result.exitValue != 0) { throw new GradleException("jpackage failed with exit code ${result.exitValue}.\n\n$stderr") } } } //jpackage.finalizedBy(jpackageMacX64) tasks.register('downloadTempJre') { group = 'distribution' description = 'Downloads and extracts a temporary JRE' doLast { try { def jreUrl = 'https://cdn.azul.com/zulu/bin/zulu17.56.15-ca-jre17.0.14-macosx_x64.tar.gz' def tmpDir = Files.createTempDirectory('zulu-jre').toFile() def jreArchive = new File(tmpDir, 'jre.tar.gz') def jreDir = new File(tmpDir, 'jre') println "🔽 Downloading JRE to $jreArchive..." jreArchive.withOutputStream { out -> new URI(jreUrl).toURL().withInputStream { from -> out << from } } println "📦 Extracting JRE to $jreDir..." jreDir.mkdirs() providers.exec { commandLine 'tar', '-xzf', jreArchive.absolutePath, '-C', jreDir.absolutePath, '--strip-components=1' }.result.get() println "✅ JRE ready at: $jreDir" ext.tempJrePath = jreDir.absolutePath project.ext.tempJrePath = jreDir.absolutePath } catch (Exception e) { println "Failed to download JRE. ${e.getLocalizedMessage()}" cleanTempJre } } } tasks.register('cleanTempJre') { dependsOn('jpackageMacX64') group = 'distribution' description = 'Deletes the temporary JRE' doLast { def path = project.ext.tempJrePath if (path && new File("$path").exists()) { println "Cleaning up temporary JRE: $path" new File("$path").parentFile.deleteDir() } } } launch4j { icon = "${projectDir}/src/main/resources/static/favicon.ico" outfile="Stirling-PDF.exe" if(System.getenv("STIRLING_PDF_DESKTOP_UI") == 'true') { headerType = "gui" } else { headerType = "console" } jarTask = tasks.bootJar errTitle="Encountered error, Do you have Java 21?" downloadUrl="https://download.oracle.com/java/21/latest/jdk-21_windows-x64_bin.exe" if(System.getenv("STIRLING_PDF_DESKTOP_UI") == 'true') { variables=["BROWSER_OPEN=true", "STIRLING_PDF_DESKTOP_UI=true"] } else { variables=["BROWSER_OPEN=true"] } jreMinVersion="17" mutexName="Stirling-PDF" windowTitle="Stirling-PDF" messagesStartupError="An error occurred while starting Stirling-PDF" // messagesJreNotFoundError="This application requires a Java Runtime Environment, Please download Java 17." messagesJreVersionError="You are running the wrong version of Java, Please download Java 21." messagesLauncherError="Java is corrupted. Please uninstall and then install Java 21." messagesInstanceAlreadyExists="Stirling-PDF is already running." } spotless { java { target project.fileTree('src').include('**/*.java') googleJavaFormat("1.25.2").aosp().reorderImports(false) importOrder("java", "javax", "org", "com", "net", "io", "jakarta", "lombok", "me", "stirling") toggleOffOn() trimTrailingWhitespace() leadingTabsToSpaces() endWithNewline() } } sonar { properties { property "sonar.projectKey", "Stirling-Tools_Stirling-PDF" property "sonar.organization", "stirling-tools" property "sonar.exclusions", "**/build-wrapper-dump.json, src/main/java/org/apache/**, src/main/resources/static/pdfjs/**, src/main/resources/static/pdfjs-legacy/**, src/main/resources/static/js/thirdParty/**" property "sonar.coverage.exclusions", "src/main/java/org/apache/**, src/main/resources/static/pdfjs/**, src/main/resources/static/pdfjs-legacy/**, src/main/resources/static/js/thirdParty/**" property "sonar.cpd.exclusions", "src/main/java/org/apache/**, src/main/resources/static/pdfjs/**, src/main/resources/static/pdfjs-legacy/**, src/main/resources/static/js/thirdParty/**" } } //gradleLint { // rules=['unused-dependency'] // } tasks.wrapper { gradleVersion = "8.12" distributionType = Wrapper.DistributionType.ALL } //tasks.withType(JavaCompile) { // options.compilerArgs << "-Xlint:deprecation" //} configurations.all { // Remove all commons-logging dependencies so that only spring-jcl is used exclude group: 'commons-logging', module: 'commons-logging' // Exclude Tomcat exclude group: "org.springframework.boot", module: "spring-boot-starter-tomcat" } dependencies { //tmp for security bumps implementation 'ch.qos.logback:logback-core:1.5.18' implementation 'ch.qos.logback:logback-classic:1.5.18' // Exclude vulnerable BouncyCastle version used in tableau configurations.all { exclude group: 'org.bouncycastle', module: 'bcpkix-jdk15on' exclude group: 'org.bouncycastle', module: 'bcutil-jdk15on' exclude group: 'org.bouncycastle', module: 'bcmail-jdk15on' } if (System.getenv("STIRLING_PDF_DESKTOP_UI") != "false") { implementation "me.friwi:jcefmaven:132.3.1" implementation "org.openjfx:javafx-controls:21" implementation "org.openjfx:javafx-swing:21" } //security updates implementation "org.springframework:spring-webmvc:6.2.5" implementation("io.github.pixee:java-security-toolkit:1.2.1") // Exclude Tomcat and include Jetty implementation("org.springframework.boot:spring-boot-starter-web:$springBootVersion") implementation "org.springframework.boot:spring-boot-starter-jetty:$springBootVersion" implementation "org.springframework.boot:spring-boot-starter-thymeleaf:$springBootVersion" implementation 'com.posthog.java:posthog:1.2.0' implementation 'com.googlecode.owasp-java-html-sanitizer:owasp-java-html-sanitizer:20240325.1' if (System.getenv("DOCKER_ENABLE_SECURITY") != "false") { implementation 'org.springframework.boot:spring-boot-starter-actuator' implementation 'io.micrometer:micrometer-registry-prometheus' implementation "org.springframework.boot:spring-boot-starter-security:$springBootVersion" implementation "org.thymeleaf.extras:thymeleaf-extras-springsecurity5:3.1.3.RELEASE" implementation "org.springframework.boot:spring-boot-starter-data-jpa:$springBootVersion" implementation "org.springframework.boot:spring-boot-starter-oauth2-client:$springBootVersion" implementation "org.springframework.session:spring-session-core:3.4.2" implementation "org.springframework:spring-jdbc:6.2.5" implementation 'com.unboundid.product.scim2:scim2-sdk-client:2.3.5' // Don't upgrade h2database runtimeOnly "com.h2database:h2:2.3.232" runtimeOnly "org.postgresql:postgresql:42.7.5" constraints { implementation "org.opensaml:opensaml-core:$openSamlVersion" implementation "org.opensaml:opensaml-saml-api:$openSamlVersion" implementation "org.opensaml:opensaml-saml-impl:$openSamlVersion" } implementation "org.springframework.security:spring-security-saml2-service-provider:$springSecuritySamlVersion" // implementation 'org.springframework.security:spring-security-core:$springSecuritySamlVersion' implementation 'com.coveo:saml-client:5.0.0' } implementation 'org.snakeyaml:snakeyaml-engine:2.9' testImplementation "org.springframework.boot:spring-boot-starter-test:$springBootVersion" // Batik implementation "org.apache.xmlgraphics:batik-all:1.18" // TwelveMonkeys runtimeOnly "com.twelvemonkeys.imageio:imageio-batik:$imageioVersion" runtimeOnly "com.twelvemonkeys.imageio:imageio-bmp:$imageioVersion" // runtimeOnly "com.twelvemonkeys.imageio:imageio-hdr:$imageioVersion" // runtimeOnly "com.twelvemonkeys.imageio:imageio-icns:$imageioVersion" // runtimeOnly "com.twelvemonkeys.imageio:imageio-iff:$imageioVersion" runtimeOnly "com.twelvemonkeys.imageio:imageio-jpeg:$imageioVersion" // runtimeOnly "com.twelvemonkeys.imageio:imageio-pcx:$imageioVersion@ // runtimeOnly "com.twelvemonkeys.imageio:imageio-pict:$imageioVersion" // runtimeOnly "com.twelvemonkeys.imageio:imageio-pnm:$imageioVersion" // runtimeOnly "com.twelvemonkeys.imageio:imageio-psd:$imageioVersion" // runtimeOnly "com.twelvemonkeys.imageio:imageio-sgi:$imageioVersion" // runtimeOnly "com.twelvemonkeys.imageio:imageio-tga:$imageioVersion" // runtimeOnly "com.twelvemonkeys.imageio:imageio-thumbsdb:$imageioVersion" runtimeOnly "com.twelvemonkeys.imageio:imageio-tiff:$imageioVersion" runtimeOnly "com.twelvemonkeys.imageio:imageio-webp:$imageioVersion" // runtimeOnly "com.twelvemonkeys.imageio:imageio-xwd:$imageioVersion" // Image metadata extractor implementation "com.drewnoakes:metadata-extractor:2.19.0" implementation "commons-io:commons-io:2.19.0" implementation "org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0" //general PDF // https://mvnrepository.com/artifact/com.opencsv/opencsv implementation ("com.opencsv:opencsv:5.10") implementation ("org.apache.pdfbox:pdfbox:$pdfboxVersion") implementation "org.apache.pdfbox:preflight:$pdfboxVersion" implementation ("org.apache.pdfbox:xmpbox:$pdfboxVersion") // https://mvnrepository.com/artifact/technology.tabula/tabula implementation ('technology.tabula:tabula:1.0.5') { exclude group: "org.slf4j", module: "slf4j-simple" exclude group: "org.bouncycastle", module: "bcprov-jdk15on" exclude group: "com.google.code.gson", module: "gson" } implementation 'org.apache.pdfbox:jbig2-imageio:3.0.4' implementation "org.bouncycastle:bcprov-jdk18on:$bouncycastleVersion" implementation "org.bouncycastle:bcpkix-jdk18on:$bouncycastleVersion" implementation "org.springframework.boot:spring-boot-starter-actuator:$springBootVersion" implementation "io.micrometer:micrometer-core:1.14.6" implementation group: "com.google.zxing", name: "core", version: "3.5.3" // https://mvnrepository.com/artifact/org.commonmark/commonmark implementation "org.commonmark:commonmark:0.24.0" implementation "org.commonmark:commonmark-ext-gfm-tables:0.24.0" // https://mvnrepository.com/artifact/com.bucket4j/bucket4j_jdk17 implementation "com.bucket4j:bucket4j_jdk17-core:8.14.0" implementation "com.fathzer:javaluator:3.0.5" implementation 'com.vladsch.flexmark:flexmark-html2md-converter:0.64.8' developmentOnly("org.springframework.boot:spring-boot-devtools:$springBootVersion") compileOnly "org.projectlombok:lombok:$lombokVersion" annotationProcessor "org.projectlombok:lombok:$lombokVersion" testRuntimeOnly 'org.mockito:mockito-inline:5.2.0' } tasks.withType(JavaCompile).configureEach { options.encoding = "UTF-8" dependsOn "spotlessApply" } compileJava { options.compilerArgs << "-parameters" } task writeVersion { def propsFile = file("$projectDir/src/main/resources/version.properties") def propsDir = propsFile.parentFile doLast { if (!propsDir.exists()) { propsDir.mkdirs() } def props = new Properties() props.setProperty("version", version) props.store(propsFile.newWriter(), null) } } processResources.dependsOn(writeVersion) swaggerhubUpload { // dependsOn = generateOpenApiDocs // Depends on your task generating Swagger docs api = "Stirling-PDF" // The name of your API on SwaggerHub owner = "${System.getenv().getOrDefault('SWAGGERHUB_USER', 'Frooodle')}" // Your SwaggerHub username (or organization name) version = project.version // The version of your API inputFile = file("SwaggerDoc.json") // The path to your Swagger docs token = "${System.getenv("SWAGGERHUB_API_KEY")}" // Your SwaggerHub API key, passed as an environment variable oas = "3.0.0" // The version of the OpenAPI Specification you"re using } jar { enabled = false manifest { attributes "Implementation-Title": "Stirling-PDF", "Implementation-Version": project.version } } tasks.named("test") { useJUnitPlatform() } task printVersion { doLast { println project.version } } task printMacVersion { doLast { println getMacVersion(project.version.toString()) } } tasks.named('generateOpenApiDocs') { doNotTrackState("Tracking state is not supported for this task") }