build.gradle.kts
6.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
// distribution — assembles the runnable vibe_erp fat-jar.
//
// This module is the only one that pulls every PBC and platform module
// together into a bootable Spring Boot application. It is referenced by
// the Dockerfile (stage 1) which produces the shipping image documented
// in the architecture spec, sections 10 and 11.
//
// **Web SPA bundling.** The `:web` subproject is a Gradle wrapper around
// the Vite/React build. We declare a `webStaticBundle` consumer
// configuration against it; the `bundleWebStatic` Sync task copies
// the dist/ directory into `${buildDir}/web-static/static/` and that
// parent directory is added to the main resources source set so Spring
// Boot's `classpath:/static/**` resource resolver picks the SPA up at
// runtime. The result is a single fat-jar containing both the JVM
// backend and the SPA — no nginx, no separate static-file server, no
// CORS.
plugins {
alias(libs.plugins.kotlin.jvm)
alias(libs.plugins.kotlin.spring)
alias(libs.plugins.spring.boot)
alias(libs.plugins.spring.dependency.management)
}
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(21))
}
}
dependencies {
implementation(project(":platform:platform-bootstrap"))
implementation(project(":platform:platform-persistence"))
implementation(project(":platform:platform-plugins"))
implementation(project(":platform:platform-security"))
implementation(project(":platform:platform-events"))
implementation(project(":platform:platform-metadata"))
implementation(project(":platform:platform-i18n"))
implementation(project(":platform:platform-workflow"))
implementation(project(":platform:platform-jobs"))
implementation(project(":platform:platform-files"))
implementation(project(":platform:platform-reports"))
implementation(project(":pbc:pbc-identity"))
implementation(project(":pbc:pbc-catalog"))
implementation(project(":pbc:pbc-partners"))
implementation(project(":pbc:pbc-inventory"))
implementation(project(":pbc:pbc-warehousing"))
implementation(project(":pbc:pbc-orders-sales"))
implementation(project(":pbc:pbc-orders-purchase"))
implementation(project(":pbc:pbc-finance"))
implementation(project(":pbc:pbc-production"))
implementation(project(":pbc:pbc-quality"))
implementation(libs.spring.boot.starter)
implementation(libs.spring.boot.starter.web)
implementation(libs.spring.boot.starter.data.jpa)
implementation(libs.spring.boot.starter.actuator)
implementation(libs.kotlin.stdlib)
implementation(libs.kotlin.reflect)
implementation(libs.jackson.module.kotlin)
runtimeOnly(libs.postgres)
runtimeOnly(libs.liquibase.core)
testImplementation(libs.spring.boot.starter.test)
}
// Configure Spring Boot's main class once, on the `springBoot` extension.
// This is the only place that drives BOTH bootJar and bootRun — setting
// mainClass on `tasks.bootJar` alone leaves `bootRun` unable to resolve
// the entry point because :distribution has no Kotlin sources of its own.
springBoot {
mainClass.set("org.vibeerp.platform.bootstrap.VibeErpApplicationKt")
// buildInfo() writes build metadata (group, artifact, version, build
// timestamp) into META-INF/build-info.properties at build time. Spring
// Boot's BuildInfoAutoConfiguration picks it up at runtime and exposes
// it as a BuildProperties bean that MetaController injects to fill in
// a real version/build time on GET /api/v1/_meta/info. Without this
// the fallback in MetaController was stuck at "0.1.0-SNAPSHOT".
buildInfo()
}
// The fat-jar produced here is what the Dockerfile copies into the
// runtime image as /app/vibe-erp.jar.
tasks.bootJar {
archiveFileName.set("vibe-erp.jar")
}
// `./gradlew :distribution:bootRun` — used by `make run` for local dev.
// Activates the dev profile so application-dev.yaml on the classpath is
// layered on top of application.yaml. Also depends on the reference
// plug-in's `installToDev` task so the JAR is staged in `plugins-dev/`
// before the app boots — that's where application-dev.yaml's
// `vibeerp.plugins.directory: ./plugins-dev` setting points.
tasks.bootRun {
systemProperty("spring.profiles.active", "dev")
// Run with the repo root as the working directory so the relative
// paths in application-dev.yaml (./plugins-dev, ./files-dev) resolve
// to the right place — the plug-in JAR is staged at
// <repo>/plugins-dev/, not <repo>/distribution/plugins-dev/.
workingDir = rootProject.layout.projectDirectory.asFile
dependsOn(":reference-customer:plugin-printing-shop:installToDev")
// Make sure the SPA dist/ is staged into the classpath before
// Spring Boot starts; otherwise an in-IDE bootRun with no prior
// build serves a 404 at the root path. processResources already
// depends on bundleWebStatic, but listing it explicitly here
// makes the relationship visible to anyone reading bootRun.
dependsOn("bundleWebStatic")
}
// ─── Web SPA bundling ────────────────────────────────────────────────
//
// Resolvable configuration that pulls the `webStaticBundle` artifact
// from `:web`. The `:web` subproject's outgoing configuration declares
// the dist/ directory as its single artifact, built by the npmBuild
// Exec task. We don't need attribute matchers because there's only
// one artifact in the configuration.
val webStaticBundle: Configuration by configurations.creating {
isCanBeConsumed = false
isCanBeResolved = true
}
dependencies {
webStaticBundle(project(mapOf("path" to ":web", "configuration" to "webStaticBundle")))
}
// Stage the SPA dist into ${buildDir}/web-static/static/ so the
// directory layout matches Spring Boot's classpath:/static/ convention.
// Adding the parent dir as a resource source set is what then makes
// the JAR's classpath include those files at the right path.
val bundleWebStatic by tasks.registering(Sync::class) {
group = "build"
description = "Stage the :web SPA bundle into the distribution resources tree."
from({ webStaticBundle })
into(layout.buildDirectory.dir("web-static/static"))
}
sourceSets.named("main") {
resources.srcDir(layout.buildDirectory.dir("web-static"))
}
tasks.named("processResources") {
dependsOn(bundleWebStatic)
}