Skip to content

Commit 40106be

Browse files
chakru-rasikowitz
andauthored
build: optimizations for incremental builds and faster CI (#13033)
Co-authored-by: Andrew Sikowitz <[email protected]>
1 parent 87af4b9 commit 40106be

File tree

23 files changed

+457
-284
lines changed

23 files changed

+457
-284
lines changed

.github/workflows/docker-unified.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -447,7 +447,7 @@ jobs:
447447
- name: Pre-build artifacts for docker image
448448
run: |
449449
./gradlew :datahub-frontend:dist -x test -x yarnTest -x yarnLint --parallel
450-
mv ./datahub-frontend/build/distributions/datahub-frontend-*.zip datahub-frontend.zip
450+
mv ./datahub-frontend/build/stage/main .
451451
- name: Build and push
452452
uses: ./.github/actions/docker-custom-build-and-push
453453
with:

build.gradle

+2-2
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ plugins {
9191
// TODO id "org.gradlex.java-ecosystem-capabilities" version "1.0"
9292
}
9393

94-
apply from: "gradle/docker/docker.gradle"
94+
apply from: "gradle/docker/docker-utils.gradle"
9595

9696
project.ext.spec = [
9797
'product' : [
@@ -112,12 +112,12 @@ project.ext.spec = [
112112

113113
project.ext.externalDependency = [
114114
'akkaHttp': "com.typesafe.akka:akka-http-core_$playScalaVersion:10.2.10", // max version due to licensing
115+
'akkaParsing': "com.typesafe.akka:akka-parsing_$playScalaVersion:10.2.10", // akka-parsing is part of akka-http, so use akka http version
115116
'akkaActor': "com.typesafe.akka:akka-actor_$playScalaVersion:$akkaVersion",
116117
'akkaStream': "com.typesafe.akka:akka-stream_$playScalaVersion:$akkaVersion",
117118
'akkaActorTyped': "com.typesafe.akka:akka-actor-typed_$playScalaVersion:$akkaVersion",
118119
'akkaSlf4j': "com.typesafe.akka:akka-slf4j_$playScalaVersion:$akkaVersion",
119120
'akkaJackson': "com.typesafe.akka:akka-serialization-jackson_$playScalaVersion:$akkaVersion",
120-
'akkaParsing': "com.typesafe.akka:akka-parsing_$playScalaVersion:$akkaVersion",
121121
'akkaProtobuf': "com.typesafe.akka:akka-protobuf-v3_$playScalaVersion:$akkaVersion",
122122
'antlr4Runtime': 'org.antlr:antlr4-runtime:4.9.3',
123123
'antlr4': 'org.antlr:antlr4:4.9.3',

datahub-frontend/build.gradle

+13-23
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
plugins {
22
id 'scala'
3-
id 'com.palantir.docker'
43
id 'org.gradle.playframework'
54
}
65

76
apply from: '../gradle/versioning/versioning.gradle'
87
apply from: './play.gradle'
98
apply from: '../gradle/coverage/java-coverage.gradle'
9+
apply from: '../gradle/docker/docker.gradle'
1010

1111
ext {
1212
docker_repo = 'datahub-frontend-react'
@@ -34,7 +34,7 @@ model {
3434
}
3535

3636
task myTar(type: Tar) {
37-
compression = Compression.GZIP
37+
compression = Compression.NONE
3838

3939
from("${buildDir}/stage")
4040

@@ -66,23 +66,27 @@ distributions {
6666
}
6767
}
6868

69+
task unversionZip(type: Copy, dependsOn: [':datahub-web-react:distZip', distZip]) {
70+
71+
from ("${buildDir}/distributions")
72+
include "datahub-frontend-${version}.zip"
73+
into "${buildDir}/distributions"
74+
rename "datahub-frontend-${version}.zip", "datahub-frontend.zip"
75+
}
76+
6977
docker {
78+
dependsOn(stageMainDist)
7079
name "${docker_registry}/${docker_repo}:v${version}"
71-
version "v${version}"
7280
dockerfile file("${rootProject.projectDir}/docker/${docker_dir}/Dockerfile")
81+
files "${buildDir}/stage"
7382
files fileTree(rootProject.projectDir) {
7483
include '.dockerignore'
7584
include 'docker/monitoring/*'
7685
include "docker/${docker_dir}/*"
7786
}.exclude {
7887
i -> (!i.file.name.endsWith(".dockerignore") && i.file.isHidden())
7988
}
80-
tag("Debug", "${docker_registry}/${docker_repo}:debug")
81-
82-
// platform('linux/arm64', 'linux/amd64')
83-
buildx(true)
84-
load(true)
85-
push(false)
89+
additionalTag("Debug", "${docker_registry}/${docker_repo}:debug")
8690

8791
// Add build args if they are defined (needed for some CI or enterprise environments)
8892
def dockerBuildArgs = [:]
@@ -101,20 +105,6 @@ docker {
101105
}
102106
}
103107

104-
task unversionZip(type: Copy, dependsOn: [':datahub-web-react:distZip', dist]) {
105-
from ("${buildDir}/distributions")
106-
include "datahub-frontend-${version}.zip"
107-
into "${buildDir}/docker/"
108-
rename "datahub-frontend-${version}.zip", "datahub-frontend.zip"
109-
}
110-
tasks.getByPath(":datahub-frontend:docker").dependsOn(unversionZip)
111-
112-
task cleanLocalDockerImages {
113-
doLast {
114-
rootProject.ext.cleanLocalDockerImages(docker_registry, docker_repo, "${version}")
115-
}
116-
}
117-
dockerClean.finalizedBy(cleanLocalDockerImages)
118108

119109
// gradle 8 fixes
120110
tasks.getByName('createDatahub-frontendTarDist').dependsOn 'stageMainDist'

datahub-graphql-core/build.gradle

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
plugins {
22
id 'java'
3-
id "io.github.kobylynskyi.graphql.codegen" version "4.1.1"
3+
id "io.github.kobylynskyi.graphql.codegen" version "5.10.0"
44
}
55

66
apply from: '../gradle/coverage/java-coverage.gradle'
@@ -41,6 +41,7 @@ graphqlCodegen {
4141
generateApis = true
4242
generateParameterizedFieldsResolvers = false
4343
modelValidationAnnotation = "@javax.annotation.Nonnull"
44+
addGeneratedAnnotation = false // Skips timestamps in generated files which forces re-compile
4445
customTypesMapping = [
4546
Long: "Long",
4647
Float: "Float"

datahub-upgrade/build.gradle

+3-16
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
plugins {
22
id 'org.springframework.boot'
33
id 'java'
4-
id 'com.palantir.docker'
54
}
65

76
apply from: "../gradle/versioning/versioning.gradle"
87
apply from: "../gradle/coverage/java-coverage.gradle"
8+
apply from: "../gradle/docker/docker.gradle"
99

1010
ext {
1111
docker_registry = rootProject.ext.docker_registry == 'linkedin' ? 'acryldata' : docker_registry
@@ -169,8 +169,8 @@ task runNoCode(type: Exec) {
169169
}
170170

171171
docker {
172+
dependsOn(bootJar)
172173
name "${docker_registry}/${docker_repo}:v${version}"
173-
version "v${version}"
174174
dockerfile file("${rootProject.projectDir}/docker/${docker_repo}/Dockerfile")
175175
files bootJar.outputs.files
176176
files fileTree(rootProject.projectDir) {
@@ -181,12 +181,7 @@ docker {
181181
}.exclude {
182182
i -> (!i.file.name.endsWith(".dockerignore") && i.file.isHidden())
183183
}
184-
tag("Debug", "${docker_registry}/${docker_repo}:debug")
185-
186-
// platform('linux/arm64', 'linux/amd64')
187-
buildx(true)
188-
load(true)
189-
push(false)
184+
additionalTag("Debug", "${docker_registry}/${docker_repo}:debug")
190185

191186
// Add build args if they are defined (needed for some CI or enterprise environments)
192187
def dockerBuildArgs = [:]
@@ -204,12 +199,4 @@ docker {
204199
buildArgs(dockerBuildArgs)
205200
}
206201
}
207-
tasks.getByPath(":datahub-upgrade:docker").dependsOn([bootJar])
208-
209-
task cleanLocalDockerImages {
210-
doLast {
211-
rootProject.ext.cleanLocalDockerImages(docker_registry, docker_repo, "${version}")
212-
}
213-
}
214-
dockerClean.finalizedBy(cleanLocalDockerImages)
215202

datahub-web-react/.gitignore

+4-1
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,7 @@ yarn-error.log*
2727

2828
# gql codegen
2929
*.generated.ts
30-
/.vscode
30+
/.vscode
31+
32+
.yarn-test-sentinel
33+
.yarn-lint-sentinel

datahub-web-react/build.gradle

+28-2
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,19 @@ task yarnServe(type: YarnTask, dependsOn: [yarnInstall, yarnGenerate]) {
8080
task yarnTest(type: YarnTask, dependsOn: [yarnInstall, yarnGenerate]) {
8181
// Explicitly runs in non-watch mode.
8282
args = ['run', project.hasProperty('withCoverage') ? 'test-coverage' : 'test', 'run']
83+
def test_sentinel = "${buildDir}/.yarn-test-sentinel"
84+
outputs.file(test_sentinel)
85+
inputs.files(project.fileTree(dir: 'src', include: ['**/*.ts', '**/*.tsx']))
86+
doLast {
87+
// touch a file with name yarn-lint.txt in the build directory
88+
def file = file(test_sentinel)
89+
if (!file.exists()) {
90+
file.createNewFile()
91+
} else {
92+
file.setLastModified(System.currentTimeMillis())
93+
}
94+
}
95+
outputs.cacheIf { true }
8396
}
8497

8598
task yarnLint(type: YarnTask, dependsOn: [yarnInstall, yarnGenerate]) {
@@ -90,6 +103,19 @@ test.dependsOn([yarnInstall, yarnTest, yarnLint])
90103

91104
task yarnLintFix(type: YarnTask, dependsOn: [yarnInstall, yarnGenerate]) {
92105
args = ['run', 'lint-fix']
106+
def lint_sentinel = "${buildDir}/.yarn-lint-sentinel"
107+
outputs.file(lint_sentinel)
108+
inputs.files(project.fileTree(dir: 'src', include: ['**/*.ts', '**/*.tsx']))
109+
doLast {
110+
// touch a file with name yarn-lint.txt in the build directory
111+
def file = file(lint_sentinel)
112+
if (!file.exists()) {
113+
file.createNewFile()
114+
} else {
115+
file.setLastModified(System.currentTimeMillis())
116+
}
117+
}
118+
outputs.cacheIf { true }
93119
}
94120

95121
task yarnBuild(type: YarnTask, dependsOn: [yarnInstall, yarnGenerate]) {
@@ -130,9 +156,9 @@ distZip {
130156
}
131157

132158
jar {
133-
dependsOn distZip
159+
dependsOn yarnBuild
134160
into('public') {
135-
from zipTree(distZip.outputs.files.first())
161+
from 'dist'
136162
}
137163
archiveClassifier = 'assets'
138164
}

docker/build.gradle

+94-10
Original file line numberDiff line numberDiff line change
@@ -80,11 +80,25 @@ ext {
8080
preserveVolumes: true
8181
]
8282
]
83+
84+
// only for debug variants of quickstart to enable <variant>Reload tasks.
85+
// The actual service name needs the profile to be appended, <container-name>-<profile>
86+
moduleToContainer = [
87+
':metadata-service:war': 'datahub-gms',
88+
':datahub-frontend': 'frontend',
89+
':datahub-upgrade': 'system-update',
90+
':metadata-jobs:mce-consumer-job': 'datahub-mce-consumer',
91+
':metadata-jobs:mae-consumer-job': 'datahub-mae-consumer',
92+
93+
]
8394
}
8495

8596
// Register all quickstart tasks
8697
quickstart_configs.each { taskName, config ->
87-
tasks.register(taskName)
98+
tasks.register(taskName) {
99+
group = 'quickstart'
100+
}
101+
88102
}
89103

90104
// Dynamically create all quickstart tasks and configurations
@@ -150,12 +164,57 @@ quickstart_configs.each { taskName, config ->
150164
}
151165
}
152166

167+
// Register all quickstart tasks
168+
quickstart_configs.each { taskName, config ->
169+
tasks.register("prepareAll${taskName}"){
170+
group = 'quickstart-ci'
171+
}
172+
}
173+
174+
quickstart_configs.each { taskName, config ->
175+
if (config.modules) {
176+
tasks.getByName("prepareAll${taskName}").dependsOn(
177+
config.modules.collect { it + ':dockerPrepare' }
178+
)
179+
}
180+
}
181+
182+
quickstart_configs.each { taskName, config ->
183+
tasks.register("buildImagesFromCache${taskName}") {
184+
group = 'quickstart-ci'
185+
}
186+
}
187+
188+
quickstart_configs.each { taskName, config ->
189+
if (config.modules) {
190+
tasks.getByName("buildImagesFromCache${taskName}").dependsOn(
191+
config.modules.collect { it + ':dockerFromCache' }
192+
)
193+
}
194+
}
195+
196+
197+
quickstart_configs.each { taskName, config ->
198+
tasks.register("buildImages${taskName}") {
199+
group = 'quickstart-ci'
200+
}
201+
}
202+
203+
quickstart_configs.each { taskName, config ->
204+
if (config.modules) {
205+
tasks.getByName("buildImages${taskName}").dependsOn(
206+
config.modules.collect { it + ':dockerTag' }
207+
)
208+
}
209+
}
210+
153211
tasks.register('minDockerCompose2.20', Exec) {
154212
executable 'bash'
155213
args '-c', 'echo -e "$(docker compose version --short)\n2.20"|sort --version-sort --check=quiet --reverse'
156214
}
157215

158216
tasks.register('quickstartNuke') {
217+
group = 'quickstart'
159218
doFirst {
160219
quickstart_configs.each { taskName, config ->
161220
dockerCompose."${taskName}".removeVolumes = !config.preserveVolumes
@@ -165,6 +224,7 @@ tasks.register('quickstartNuke') {
165224
}
166225

167226
tasks.register('quickstartDown') {
227+
group = 'quickstart'
168228
finalizedBy(tasks.withType(ComposeDownForced))
169229
}
170230

@@ -173,12 +233,36 @@ tasks.withType(ComposeUp).configureEach {
173233
dependsOn tasks.named("minDockerCompose2.20")
174234
}
175235

176-
task debugReload(type: Exec) {
177-
def cmd = ['docker compose -p datahub --profile debug'] + ['-f', compose_base] + [
178-
'restart',
179-
'datahub-gms-debug',
180-
'system-update-debug',
181-
'frontend-debug'
182-
]
183-
commandLine 'bash', '-c', cmd.join(" ")
184-
}
236+
// Register all quickstart Reload tasks. For quickstartDebug, the reload task is DebugReload. (Taskname without quickstart prefix)
237+
quickstart_configs.each { taskName, config ->
238+
if (config.isDebug) {
239+
def reloadTaskName = taskName.replaceFirst(/^quickstart/, "")
240+
tasks.register("${reloadTaskName}Reload", Exec) {
241+
dependsOn tasks.named("prepareAll${taskName}")
242+
group = 'quickstart'
243+
description = "Build and reload only changed containers for the ${taskName} task"
244+
doFirst {
245+
def executedTasks = project.gradle.taskGraph.allTasks.findAll { it.state.executed }
246+
def containersToRestart = []
247+
248+
moduleToContainer.each { modulePath, containerName ->
249+
def moduleProject = project.project(modulePath)
250+
def dockerPrepareTask = moduleProject.tasks.findByName('dockerPrepare')
251+
252+
if (dockerPrepareTask && executedTasks.contains(dockerPrepareTask) && !dockerPrepareTask.state.upToDate) {
253+
containersToRestart << "${containerName}-${config.profile}"
254+
}
255+
}
256+
257+
// Only restart containers that had their modules rebuilt
258+
if (containersToRestart) {
259+
def cmd = ["docker compose -p datahub --profile ${config.profile}"] + ['-f', compose_base] + ['restart'] + containersToRestart
260+
commandLine 'bash', '-c', cmd.join(" ")
261+
} else {
262+
// If no containers need restart, make this a no-op
263+
commandLine 'bash', '-c', 'echo "No containers need restarting - all modules are up to date"'
264+
}
265+
}
266+
}
267+
}
268+
}

0 commit comments

Comments
 (0)