apply plugin: 'org.springframework.boot' import org.apache.tools.ant.taskdefs.condition.Os repositories { maven { url = 'https://build.shibboleth.net/maven/releases' } maven { url = 'https://maven.pkg.github.com/jcefmaven/jcefmaven' } } configurations { developmentOnly runtimeClasspath { extendsFrom developmentOnly } } spotless { java { target 'src/**/java/**/*.java' targetExclude 'src/main/resources/static/**' googleJavaFormat(googleJavaFormatVersion).aosp().reorderImports(false) importOrder("java", "javax", "org", "com", "net", "io", "jakarta", "lombok", "me", "stirling") toggleOffOn() trimTrailingWhitespace() leadingTabsToSpaces() endWithNewline() } yaml { target '**/*.yml', '**/*.yaml' targetExclude 'src/main/resources/static/**' trimTrailingWhitespace() leadingTabsToSpaces() endWithNewline() } format 'gradle', { target '**/gradle/*.gradle', '**/*.gradle' targetExclude 'src/main/resources/static/**' trimTrailingWhitespace() leadingTabsToSpaces() endWithNewline() } } dependencies { if (System.getenv('STIRLING_PDF_DESKTOP_UI') != 'false' || (project.hasProperty('STIRLING_PDF_DESKTOP_UI') && project.getProperty('STIRLING_PDF_DESKTOP_UI') != 'false')) { implementation 'org.openjfx:javafx-controls:21' implementation 'org.openjfx:javafx-swing:21' } if (System.getenv('DISABLE_ADDITIONAL_FEATURES') != 'true' || (project.hasProperty('DISABLE_ADDITIONAL_FEATURES') && System.getProperty('DISABLE_ADDITIONAL_FEATURES') != 'true')) { implementation project(':proprietary') } implementation project(':common') implementation 'org.springframework.boot:spring-boot-starter-jetty' implementation 'com.posthog.java:posthog:1.2.0' implementation 'commons-io:commons-io:2.20.0' implementation "org.bouncycastle:bcprov-jdk18on:$bouncycastleVersion" implementation "org.bouncycastle:bcpkix-jdk18on:$bouncycastleVersion" implementation 'io.micrometer:micrometer-core:1.15.4' implementation 'com.google.zxing:core:3.5.3' implementation "org.commonmark:commonmark:$commonmarkVersion" // https://mvnrepository.com/artifact/org.commonmark/commonmark implementation "org.commonmark:commonmark-ext-gfm-tables:$commonmarkVersion" // General PDF dependencies 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 'com.opencsv:opencsv:5.12.0' // https://mvnrepository.com/artifact/com.opencsv/opencsv // Batik implementation 'org.apache.xmlgraphics:batik-all:1.19' // TwelveMonkeys runtimeOnly "com.twelvemonkeys.imageio:imageio-batik:$imageioVersion" runtimeOnly "com.twelvemonkeys.imageio:imageio-bmp:$imageioVersion" runtimeOnly "com.twelvemonkeys.imageio:imageio-jpeg:$imageioVersion" runtimeOnly "com.twelvemonkeys.imageio:imageio-tiff:$imageioVersion" runtimeOnly "com.twelvemonkeys.imageio:imageio-webp:$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-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-xwd:$imageioVersion" developmentOnly 'org.springframework.boot:spring-boot-devtools' } sourceSets { main { resources { srcDirs += ['../configs'] } java { if (System.getenv('STIRLING_PDF_DESKTOP_UI') == 'false') { exclude 'stirling/software/SPDF/UI/impl/**' } } } test { java { if (System.getenv('STIRLING_PDF_DESKTOP_UI') == 'false') { exclude 'stirling/software/SPDF/UI/impl/**' } } } } // Disable regular jar jar { enabled = false } // Configure and enable bootJar for this project bootJar { enabled = true duplicatesStrategy = DuplicatesStrategy.EXCLUDE zip64 = true // Don't include all dependencies directly like the old jar task did // from { // configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } // } // Exclude signature files to prevent "Invalid signature file digest" errors exclude 'META-INF/*.SF' exclude 'META-INF/*.DSA' exclude 'META-INF/*.RSA' exclude 'META-INF/*.EC' manifest { attributes( 'Implementation-Title': 'Stirling-PDF', 'Implementation-Version': project.version ) } } // Configure main class for Spring Boot springBoot { mainClass = 'stirling.software.SPDF.SPDFApplication' } // Frontend build tasks - only enabled with -PbuildWithFrontend=true def buildWithFrontend = project.hasProperty('buildWithFrontend') && project.property('buildWithFrontend') == 'true' def frontendDir = file('../../frontend') def frontendDistDir = file('../../frontend/dist') def resourcesStaticDir = file('src/main/resources/static') def generatedFrontendPaths = [ 'assets', 'index.html', 'locales', 'Login', 'classic-logo', 'modern-logo', 'og_images', 'samples', 'manifest-classic.json' ] tasks.register('npmInstall', Exec) { enabled = buildWithFrontend group = 'frontend' description = 'Install frontend dependencies' workingDir frontendDir commandLine = Os.isFamily(Os.FAMILY_WINDOWS) ? ['cmd', '/c', 'npm', 'ci', '--prefer-offline'] : ['npm', 'ci', '--prefer-offline'] inputs.file(new File(frontendDir, 'package.json')) inputs.file(new File(frontendDir, 'package-lock.json')) outputs.dir(new File(frontendDir, 'node_modules')) // Show live output standardOutput = System.out errorOutput = System.err // Skip if node_modules exists and is up-to-date onlyIf { def nodeModules = new File(frontendDir, 'node_modules') if (!nodeModules.exists()) { println "node_modules not found, will install..." return true } def packageJson = new File(frontendDir, 'package.json') def packageLock = new File(frontendDir, 'package-lock.json') def isOutdated = nodeModules.lastModified() < packageJson.lastModified() || nodeModules.lastModified() < packageLock.lastModified() if (isOutdated) { println "package.json or package-lock.json changed, will reinstall..." } else { println "node_modules is up-to-date, skipping npm install" } return isOutdated } doFirst { println "Installing npm dependencies in ${frontendDir}..." } } tasks.register('npmBuild', Exec) { enabled = buildWithFrontend group = 'frontend' description = 'Build frontend application' workingDir frontendDir commandLine = Os.isFamily(Os.FAMILY_WINDOWS) ? ['cmd', '/c', 'npm', 'run', 'build'] : ['npm', 'run', 'build'] dependsOn npmInstall inputs.dir(new File(frontendDir, 'src')) inputs.file(new File(frontendDir, 'package.json')) outputs.dir(frontendDistDir) // Show live output standardOutput = System.out errorOutput = System.err // Override VITE_API_BASE_URL to use relative paths for production builds // This ensures JARs work regardless of how they're deployed (direct, proxied, etc.) environment 'VITE_API_BASE_URL', '/' doFirst { println "Building frontend application for production (VITE_API_BASE_URL=/)" } } tasks.register('copyFrontendAssets', Copy) { enabled = buildWithFrontend group = 'frontend' description = 'Copy frontend build to static resources' dependsOn npmBuild from(frontendDistDir) { // Exclude files that conflict with backend static resources exclude 'robots.txt' // Backend already has this exclude 'favicon.ico' // Backend already has this } into resourcesStaticDir duplicatesStrategy = DuplicatesStrategy.INCLUDE // Let frontend overwrite when needed doFirst { println "Copying frontend build from ${frontendDistDir} to ${resourcesStaticDir}..." println "Backend static resources will be preserved" } doLast { println "Frontend assets copied successfully!" } } tasks.register('cleanFrontendAssets', Delete) { group = 'frontend' description = 'Remove previously generated frontend assets from static resources' delete generatedFrontendPaths.collect { new File(resourcesStaticDir, it) } } // Ensure copyFrontendAssets runs after spotless tasks tasks.named('copyFrontendAssets').configure { mustRunAfter tasks.matching { it.name.startsWith('spotless') } } if (buildWithFrontend) { println "Frontend build enabled - JAR will include React frontend" processResources.dependsOn copyFrontendAssets } else { println "Frontend build disabled - JAR will be backend-only" // When not building the UI, ensure any stale frontend assets are removed processResources.dependsOn cleanFrontendAssets } bootJar.dependsOn ':common:jar' bootJar.dependsOn ':proprietary:jar'