Add a global pipeline job
This job will be used to trigger a global test suite, suitable
to run a global pre/post-submit of Bazel paving the way for
global pre submit from Gerrit.
This job trigger all jobs in the "Global" folder but that folder
is still empty.
Missing for #55:
- Add job in the Global folder
- Trigger from Gerrit
- Trigger the new Global/pipeline job instead of the legacy pipeline
- Installation of Bazel releases automatically
Change-Id: I69937b1531061ca1575564777c539bd97edd90db
diff --git a/jenkins/jobs/BUILD b/jenkins/jobs/BUILD
index cae77c6..5039ac9 100644
--- a/jenkins/jobs/BUILD
+++ b/jenkins/jobs/BUILD
@@ -27,7 +27,16 @@
project_url = "http://bazel.io",
substitutions = JOBS_SUBSTITUTIONS,
deps = glob(["%s.*.tpl" % job]),
-) for job in BAZEL_JOBS.keys()]
+) for job in BAZEL_JOBS.keys() if job != "Global/pipeline"]
+
+jenkins_job(
+ name = "Global/pipeline",
+ config = "global.xml.tpl",
+ deps = [
+ ":global.groovy",
+ ":configs/bootstrap.json",
+ ],
+)
# TODO(dmarting): activate Tensorflow on mac (missing dependencies)
bazel_github_job(
diff --git a/jenkins/jobs/global.groovy b/jenkins/jobs/global.groovy
new file mode 100644
index 0000000..5517dd8
--- /dev/null
+++ b/jenkins/jobs/global.groovy
@@ -0,0 +1,134 @@
+// Copyright (C) 2017 The Bazel Authors
+//
+// Licensed 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.
+
+// This pipeline control a global test for Bazel:
+// - Bootstrap bazel and do some basic tests
+// - Deploy the artifacts (site, releases) and send mails
+// - Run all downstream job
+
+stage("Startup global test") {
+ echo "Running global test for branch ${params.BRANCH} (refspec: ${params.REFSPEC})"
+}
+
+notifyStatus(mail_recipient) {
+ // First we bootstrap bazel on all platform
+ stage("Bootstrap on all platforms") {
+ bootstrapBazelAll(branch: params.BRANCH,
+ refspec: params.REFSPEC,
+ email: mail_recipient,
+ configuration: json_config,
+ restrict_configuration: restrict_configuration)
+ }
+
+ // Some basic tests
+ // TODO(dmarting): maybe we want to run it in parallel of other jobs?
+ stage("Test that all sources are in the //:srcs filegroup") {
+ node("linux-x86_64") {
+ recursiveGit(repository: "https://bazel.googlesource.com/bazel",
+ refspec: params.REFSPEC,
+ branch: params.BRANCH)
+ def bazel = fetchBazel("latest", "linux-x86_64")
+ sh(script: "./compile.sh srcs ${bazel}")
+ }
+ }
+}
+
+
+// Deployment steps
+if(params.BRANCH.matches('^(.*/)master$')) {
+ stage("Push website") {
+ node("deploy") {
+ recursiveGit(repository: "https://bazel.googlesource.com/bazel",
+ refspec: params.REFSPEC,
+ branch: params.BRANCH)
+ unstash "bazel--node=linux-x86_64--variation="
+ sh script: '''#!/bin/bash
+. scripts/ci/build.sh
+for i in $(find input -name \'*.bazel.build.tar\'); do
+ build_and_publish_site "$i" "$(basename $i .tar)" "build"
+done
+for i in $(find input -name \'*.bazel.build.tar.nobuild\'); do
+ build_and_publish_site "$i" "$(basename $i .tar.nobuild)" "nobuild"
+done
+'''
+ }
+ }
+} else if(params.BRANCH.matches('^refs/((heads/release-)|(tags/)).*$')) {
+ def r_name = ""
+ node("deploy") {
+ r_name = sh(script: "bash -c 'source scripts/release/common.sh; get_full_release_name'",
+ returnStdout: true)
+ if (!r_name.isEmpty()) {
+ stage("Push release") {
+ // unstash all the things
+ def conf = BazelConfiguration.flattenConfigurations(
+ BazelConfiguration.parse(json_config), restrict_configuration).keySet().toArray()
+ for (int k = 0; k < entrySet.size; k++) {
+ unstash "bazel--node=${conf[k].node}--variation=${conf[k].variation}"
+ }
+ // Delete files we do not need
+ sh "rm -f node=*/variation=*/bazel node=*/variation=*/*.bazel.build.tar*"
+ // Now the actual release
+ withEnv(["GCS_BUCKET=bazel",
+ "GIT_REPOSITORY_URL=https://github.com/bazelbuild/bazel"]) {
+ dir("output/ci") { -> }
+ sh '''#!/bin/bash
+# Credentials should not be displayed on the command line
+export GITHUB_TOKEN="$(cat "$GITHUB_TOKEN_FILE")"
+export APT_GPG_KEY_ID="$(cat "${APT_GPG_KEY_ID_FILE}")"
+source scripts/ci/build.sh
+
+args=()
+for i in node=*; do
+ for j in $i/variation=*/*; do
+ args+=("$(echo $i | cut -d = -f 2)" "$j")
+done
+
+bazel_release "${args[@]}"
+echo "${RELEASE_EMAIL_RECIPIENT}" | tee output/ci/recipient
+echo "${RELEASE_EMAIL_SUBJECT}" | tee output/ci/subject
+echo "${RELEASE_EMAIL_CONTENT}" | tee output/ci/content
+'''
+ if (r_name.contains("test")) {
+ echo "Test release, skipping announcement mail"
+ } else {
+ stage("Announcement mail") {
+ mail(subject: readFile("output/ci/subject"),
+ to: readFile("output/ci/recipient"),
+ replyTo: "bazel-ci@googlegroups.com",
+ body: readFile("output/ci/content"))
+ }
+ }
+ }
+ }
+ }
+ }
+ // TODO(dmarting): trigger bazel install everywhere in case of release.
+}
+
+// Then we run all the job in the Global folder but myself
+stage("Test downstream jobs") {
+ runAll(folder: "Global",
+ parameters: [
+ [$class: 'TextParameterValue',
+ name: 'EXTRA_BAZELRC',
+ value: "${params.EXTRA_BAZELRC}"],
+ [$class: 'StringParameterValue',
+ name: 'BRANCH',
+ value: "${params.BRANCH}"],
+ [$class: 'StringParameterValue',
+ name: 'REFSPEC',
+ value: "${params.REFSPEC}"]
+ ])
+}
diff --git a/jenkins/jobs/global.xml.tpl b/jenkins/jobs/global.xml.tpl
new file mode 100644
index 0000000..dcd7e5c
--- /dev/null
+++ b/jenkins/jobs/global.xml.tpl
@@ -0,0 +1,44 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<flow-definition>
+ <actions/>
+ <description>Global pipeline to bootstrap bazel and runs all downstream jobs</description>
+ <keepDependencies>false</keepDependencies>
+ <properties>
+ <org.jenkinsci.plugins.workflow.job.properties.DisableConcurrentBuildsJobProperty/>
+ <com.coravy.hudson.plugins.github.GithubProjectProperty>
+ <projectUrl>https://github.com/bazelbuild/bazel/</projectUrl>
+ <displayName></displayName>
+ </com.coravy.hudson.plugins.github.GithubProjectProperty>
+ <hudson.model.ParametersDefinitionProperty>
+ <parameterDefinitions>
+ <hudson.model.StringParameterDefinition>
+ <name>BRANCH</name>
+ <description>The branch to build</description>
+ <defaultValue>master</defaultValue>
+ </hudson.model.StringParameterDefinition>
+ <hudson.model.StringParameterDefinition>
+ <name>REFSPEC</name>
+ <description>The refspec to fetch</description>
+ <defaultValue>+refs/heads/*:refs/remotes/origin/*</defaultValue>
+ </hudson.model.StringParameterDefinition>
+ <hudson.model.TextParameterDefinition>
+ <name>EXTRA_BAZELRC</name>
+ <description>To inject new option to the .bazelrc file in downstream projects.</description>
+ <defaultValue></defaultValue>
+ </hudson.model.TextParameterDefinition>
+ </parameterDefinitions>
+ </hudson.model.ParametersDefinitionProperty>
+ <org.jenkinsci.plugins.workflow.job.properties.PipelineTriggersJobProperty>
+ <triggers/>
+ </org.jenkinsci.plugins.workflow.job.properties.PipelineTriggersJobProperty>
+ </properties>
+ <definition class="org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition">
+ <script>
+json_config = '''{{ raw_imports['//jenkins/jobs:configs/bootstrap.json'].replace('\\', '\\\\').replace("'", "\\'") }}'''
+restrict_configuration = {{ variables.RESTRICT_CONFIGURATION }}
+mail_recipient = "{{ variables.BAZEL_BUILD_RECIPIENT }}"
+{{ imports['//jenkins/jobs:global.groovy'] }}</script>
+ <sandbox>true</sandbox>
+ </definition>
+ <triggers/>
+</flow-definition>
diff --git a/jenkins/jobs/jobs.bzl b/jenkins/jobs/jobs.bzl
index 0b30083..8b8c431 100644
--- a/jenkins/jobs/jobs.bzl
+++ b/jenkins/jobs/jobs.bzl
@@ -65,6 +65,7 @@
BAZEL_STAGING_JOBS = {
"Bazel": ALL_PLATFORMS + BSD_PLATFORMS,
"Github-Trigger": UNIX_PLATFORMS,
+ "Global/pipeline": [],
"Bazel-Install": [],
"Bazel-Install-Trigger": [],
}
diff --git a/templating/main.py b/templating/main.py
index e8b960a..c3da715 100644
--- a/templating/main.py
+++ b/templating/main.py
@@ -68,11 +68,13 @@
return source, self.path, lambda: mtime == os.path.getmtime(self.path)
-def expand_template(template, variables, imports):
+def expand_template(template, variables, imports, raw_imports=None):
"""Expand a template."""
+ if raw_imports is None:
+ raw_imports = imports
env = jinja2.Environment(loader=OneFileLoader(template))
template = env.get_template(template)
- return template.render(imports=imports, variables=variables)
+ return template.render(imports=imports, variables=variables, raw_imports=raw_imports)
def quote_xml(d):
"""Returns a copy of d where all values where escaped for XML."""
@@ -99,10 +101,11 @@
sys.exit(-1)
variables[kv[0]] = kv[1]
imports = construct_imports(variables, flags.imports)
+ raw_imports = imports
if flags.escape_xml:
imports = quote_xml(imports)
variables = quote_xml(variables)
- result = expand_template(flags.template, variables, imports)
+ result = expand_template(flags.template, variables, imports, raw_imports)
with open(flags.output, "w") as f:
f.write(result)
if flags.executable: