forked from apache/beam
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbuild.gradle
367 lines (318 loc) · 12 KB
/
build.gradle
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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* License); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
plugins {
id 'base' // Define common lifecycle tasks and artifact types
id 'org.ajoberstar.grgit' // Publish website to asf-git branch.
}
apply plugin: org.apache.beam.gradle.BeamModulePlugin
applyPythonNature()
def dockerImageTag = 'beam-website'
def dockerWorkDir = "/opt"
def buildDir = "${project.rootDir}/build/website"
def dockerSourceDir = "$dockerWorkDir/website/www"
def dockerBuildDir = "$dockerWorkDir/build/website"
def commitedChanges = false
def gitboxUrl = project.findProperty('gitPublishRemote') ?: 'https://gitbox.apache.org/repos/asf/beam.git'
def shell = { cmd ->
println cmd
exec {
executable 'sh'
args '-c', cmd
}
}
def envdir = "${buildDir}/gradleenv"
task setupVirtualenvWithDependencies {
doLast {
exec {
commandLine "python${project.ext.pythonVersion}", '-m', 'venv', "${envdir}"
}
exec {
executable 'sh'
args '-c', ". ${envdir}/bin/activate && pip install beautifulsoup4"
}
}
outputs.dirs(envdir)
}
task buildDockerImage(type: Exec) {
commandLine 'docker', 'build', '-t', dockerImageTag, '.'
}
task createDockerContainer(type: Exec) {
dependsOn buildDockerImage
standardOutput = new ByteArrayOutputStream()
ext.containerId = {
return standardOutput.toString().trim()
}
gradle.taskGraph.whenReady {
def extraOptions = ''
if (gradle.taskGraph.hasTask(":${project.name}:serveWebsite")) {
// Publish port 1313 where Hugo serves website from
extraOptions = "--publish 127.0.0.1:1313:1313"
}
if (System.getenv().getOrDefault('NODE_NAME', '').startsWith('websites')) {
// Jenkins websites node: run as root. Files written to /repo will get
// "jenkins" user ownership. BEAM-9737
} else {
// Otherwise: run as current user, so as to not require sudo to clean up
// build/ directory.
extraOptions += " -u \$(id -u):\$(id -g)"
}
commandLine '/bin/bash', '-c',
"docker create -v $project.rootDir:$dockerWorkDir $extraOptions $dockerImageTag sh -c 'trap \"exit 0\" INT; while true; do sleep 30; done;'"
}
}
task startDockerContainer(type: Exec) {
dependsOn createDockerContainer
ext.containerId = {
return createDockerContainer.containerId()
}
commandLine 'docker', 'start',
"${->createDockerContainer.containerId()}" // Lazily evaluate containerId.
}
// Clone Docsy submodule which is our Hugo theme
task initGitSubmodules(type: Exec) {
commandLine 'docker', 'exec',
"${->startDockerContainer.containerId()}", 'git', 'submodule', 'update', '--init', '--recursive'
}
// Install yarn dependencies
task installDependencies(type: Exec) {
commandLine 'docker', 'exec', '--workdir', "$dockerSourceDir",
"${->startDockerContainer.containerId()}", 'yarn', 'install'
}
// Run build_code_samples.sh to fetch Beam project content
// which is used by code_sample shortcodes to inject snippets into codeblocks
task buildCodeSamples(type: Exec) {
commandLine 'docker', 'exec', '--workdir', "$dockerSourceDir",
"${->startDockerContainer.containerId()}", 'yarn', 'build_code_samples'
}
task setupDockerContainer(type: Exec) {
dependsOn startDockerContainer
finalizedBy initGitSubmodules, installDependencies, buildCodeSamples
ext.containerId = {
return startDockerContainer.containerId()
}
// Create the config to point to a GitHub or Colab blob in the repo, e.g. apache/beam/blob/master
commandLine 'docker', 'exec',
"${->startDockerContainer.containerId()}", '/bin/bash', '-c',
"""echo '[params]\n branch_repo = "${getBranchRepo()}"' > /tmp/_config_branch_repo.toml"""
}
task stopAndRemoveDockerContainer(type: Exec) {
commandLine 'docker', 'rm', '-f', "${->createDockerContainer.containerId()}"
}
task cleanWebsite(type: Delete) {
delete buildDir
}
clean.dependsOn cleanWebsite
class BuildTaskConfiguration {
String name
boolean useTestConfig = false
String baseUrl = ''
String dockerWorkDir = ''
}
def createBuildTask = {
BuildTaskConfiguration config = it as BuildTaskConfiguration
task "build${config.name}Website" (type:Exec) {
dependsOn setupDockerContainer
finalizedBy stopAndRemoveDockerContainer
def configs = "$dockerSourceDir/site/config.toml"
def baseUrlFlag = config.baseUrl ? "--baseURL /${config.baseUrl}" : ""
commandLine 'docker', 'exec',
"${->setupDockerContainer.containerId()}", '/bin/bash', '-c',
"""cd $dockerSourceDir && \
yarn build \
-d $dockerBuildDir/generated-${config.name.toLowerCase()}-content \
--config $configs \
$baseUrlFlag
"""
}
}
// task buildLocalWebsite
createBuildTask(
name:'Local',
)
task buildWebsite(dependsOn:buildLocalWebsite)
build.dependsOn buildWebsite
// task buildGcsWebsite
createBuildTask(
name:'Gcs',
baseUrl: getBaseUrl(),
)
// task buildApacheWebsite
createBuildTask(
name:'Apache',
)
/**
* Use the pull request ID in the URL path, or the git branch when building locally.
* This allows staging multiple work trees without clobbering eachother, i.e.
* for shared GCS staging, or for locally comparing the differences between
* branches.
*/
def getBaseUrl() {
def pullRequestId = System.getenv('ghprbPullId')?.trim()
if (pullRequestId) { return pullRequestId }
// grgit is null if building outside of git (i.e. from source archive)
def buildContext = grgit ? grgit.branch.current().getName() : 'archive'
return "${System.getProperty('user.name')}-${buildContext}"
}
/**
* Gets the branch repository where the new files are located. This is used to point
* all the GitHub and Colab files to the pull request's branch when testing, while
* pointing them to the apache/beam master branch on production. This is done so tests
* and staged versions work even when the files don't exist in master yet.
*/
def getBranchRepo() {
// Jenkins stores the GitHub author in $ghprbPullAuthorLogin
def author = System.env.ghprbPullAuthorLogin
if (author == null) {
// If the author is not defined, it's most probably running locally.
if (grgit != null) {
// If on git, try to infer the author from the remote URL
for (remote in grgit.remote.list()) {
if (remote.getName() == 'origin') {
// remote.url = '[email protected]:author/beam.git'
author = remote.url.split(':')[1].split('/')[0]
break
}
}
}
}
// Jenkins stores the branch in both of the following environment variables.
def branch = System.env.ghprbSourceBranch ?: System.env.GIT_BRANCH
if (branch == null && grgit) {
branch = grgit.branch.current().getName()
}
// Return the author's branch repo, otherwise default to the master repo.
if (author && branch) {
return "${author}/beam/blob/${branch}"
}
return "apache/beam/blob/master"
}
def buildContentDir(name) {
"${project.rootDir}/build/website/generated-${name.toLowerCase()}-content"
}
task serveWebsite(type: Exec) {
dependsOn setupDockerContainer
finalizedBy stopAndRemoveDockerContainer
commandLine 'docker', 'exec',
"${->setupDockerContainer.containerId()}", '/bin/bash', '-c',
"""cd $dockerSourceDir && \
yarn develop \
--bind="0.0.0.0" \
--config $dockerSourceDir/site/config.toml,/tmp/_config_branch_repo.toml
"""
}
task testWebsite(type: Exec) {
// dependsOn setupDockerContainer, 'buildWebsite'
finalizedBy stopAndRemoveDockerContainer
commandLine 'docker', 'exec',
"${->setupDockerContainer.containerId()}", '/bin/bash', '-c',
"$dockerSourceDir/check-links.sh $dockerBuildDir/generated-local-content"
}
testWebsite.dependsOn 'buildLocalWebsite'
task preCommit {
dependsOn testWebsite
}
// Creates a new commit on asf-site branch
task commitWebsite {
doLast {
assert grgit : "Cannot commit website outside of git repository"
assert file("${buildContentDir('apache')}/index.html").exists()
// Generated javadoc and pydoc content is not built or stored in this repo.
assert !file("${buildContentDir('apache')}/documentation/sdks/javadoc").exists()
assert !file("${buildContentDir('apache')}/documentation/sdks/pydoc").exists()
def git = grgit.open(currentDir: project.rootDir)
// get the latest commit on master
def latestCommit = grgit.log(maxCommits: 1)[0].abbreviatedId
shell "git fetch --force origin +asf-site:asf-site"
shell "git stash"
shell "git checkout asf-site"
git.checkout(branch: 'asf-site')
// Delete the previous content. These are asf-site branch paths.
git.remove(patterns: [ 'website/generated-content' ])
def repoContentDir = "${project.rootDir}/website/generated-content"
assert !file("${repoContentDir}/index.html").exists()
delete repoContentDir
// Copy the built content and add it.
copy {
from buildContentDir('apache')
into repoContentDir
}
assert file("${repoContentDir}/index.html").exists()
git.add(patterns: ['website/generated-content'])
def currentDate = new Date().format('yyyy/MM/dd HH:mm:ss')
String message = "Publishing website ${currentDate} at commit ${latestCommit}"
if (!git.status().staged.getAllChanges()) {
println 'No changes to commit'
} else {
println 'Creating commit for changes'
commitedChanges = true
git.commit(message: message)
}
}
}
/*
* Pushes the asf-site branch commits.
*
* This requires write access to the asf-site branch and can be run on
* Jenkins executors with the git-websites label.
*
* For more details on publishing, see:
* https://www.apache.org/dev/project-site.html
* https://github.com/apache/infrastructure-puppet/blob/deployment/modules/gitwcsub/files/config/gitwcsub.cfg
*
* You can test this locally with a forked repository by manually adding the
* website-publish remote pointing to your forked repository, for example:
* git remote add website-publish [email protected]:${GITUSER}/beam.git
* because the remote is only added if it doesn't exist. The remote needs
* to be added before every execution of the publishing.
*/
task publishWebsite {
doLast {
assert grgit : "Cannot publish website outside of git repository"
def git = grgit.open(currentDir: project.rootDir)
git.checkout(branch: 'asf-site')
if (!commitedChanges) {
println 'No changes to push'
return
}
// Because git.push() fails to authenticate, run git push directly.
shell "git push ${gitboxUrl} asf-site"
}
}
commitWebsite.dependsOn buildApacheWebsite
publishWebsite.dependsOn commitWebsite
/*
* Stages a pull request on GCS
* For example:
* ./gradlew :website:stageWebsite -PwebsiteBucket=foo
*/
task stageWebsite {
doLast {
def baseUrl = getBaseUrl()
assert baseUrl : 'Website staging requires a valid baseUrl'
def gcs_bucket = project.findProperty('websiteBucket') ?: 'apache-beam-website-pull-requests'
def gcs_path = "gs://${gcs_bucket}/${baseUrl}"
// Fixup the links to index.html files
shell ". ${envdir}/bin/activate && python append_index_html_to_internal_links.py ${buildContentDir('gcs')}"
// Copy the build website to GCS
shell "gsutil -m rsync -r -d ${buildContentDir('gcs')} ${gcs_path}"
println "Website published to http://${gcs_bucket}." +
"storage.googleapis.com/${baseUrl}/index.html"
}
}
stageWebsite.dependsOn setupVirtualenvWithDependencies
stageWebsite.dependsOn buildGcsWebsite