Skip to content

Commit 6c90820

Browse files
authored
ci(gcb): add --docker flag (#6122)
* ci(gcb): add --docker flag This PR adds the `-d | --docker` flags to run the specified build using the local docker. This let's users run our GCB builds locally in an environment that's very similar (exact? pretty close) to the GCB environment. This also makes debugging easier. Users can start a bash shell in the specified docker container by passing the `-s | --docker_shell` flag. Examples: ``` build.sh cmake-install # Runs build on GCB build.sh cmake-install --local # Runs build on your local machine build.sh cmake-install --docker # Runs build in docker build.sh cmake-install --docker-shell # Starts shell in docker ``` These docker builds create a "build-out" directory on the host system to store all their output (similar to our current "cmake-out" directory, but not cmake-specific). All their build output is stored in a build-specific dir w/in build-out/.
1 parent 6b6a75d commit 6c90820

3 files changed

Lines changed: 79 additions & 13 deletions

File tree

.bazelignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
build-out/
12
ci/
23
cmake-out/
34
cmake-build-debug/

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
.build/
33
_build/
44
build-output/
5+
build-out/
56
cmake-out/
67

78
# Common bazel output directories

ci/cloudbuild/build.sh

Lines changed: 77 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,22 @@
1717
# ## Run builds using Google Cloud Build ##
1818
#
1919
# This script runs the builds defined in `ci/cloudbuild/builds/` on the local
20-
# machine (if `--local` is specified) or in the cloud using Google Cloud Build
21-
# (the default). A single argument indicating the build name is required. The
22-
# distro indicates the `<distro>.Dockerfile` to use for the build (ignored if
23-
# using `--local`). The distro is looked up from the specified build's trigger
24-
# file (`ci/cloudbuild/triggers/<build-name>.yaml`), but the distro can be
20+
# machine (if `--local` is specified), in the local docker (if `--docker` is
21+
# specified), or in the cloud using Google Cloud Build (the default). A single
22+
# argument indicating the build name is required. The distro indicates the
23+
# `<distro>.Dockerfile` to use for the build (ignored if using `--local`). The
24+
# distro is looked up from the specified build's trigger file
25+
# (`ci/cloudbuild/triggers/<build-name>.yaml`), but the distro can be
2526
# overridden with the optional `--distro=<arg>` flag.
2627
#
2728
# Usage: build.sh [options] <build-name>
2829
#
2930
# Options:
30-
# --distro=<name> The distro name to use
31-
# -p|--project=<name> The Cloud Project ID to use
31+
# --distro=<name> The distro name to use
3232
# -l|--local Run the build in the local environment
33+
# -d|--docker Run the build in a local docker
34+
# -s|--docker-shell Run a shell in the build's docker container
35+
# -p|--project=<name> The Cloud Project ID to use
3336
# -h|--help Print this help message
3437
#
3538
# Build names:
@@ -48,15 +51,17 @@ function print_usage() {
4851

4952
# Use getopt to parse and normalize all the args.
5053
PARSED="$(getopt -a \
51-
--options="p:lh" \
52-
--longoptions="distro:,project:,local,help" \
54+
--options="p:ldsh" \
55+
--longoptions="distro:,project:,local,docker,docker-shell,help" \
5356
--name="${PROGRAM_NAME}" \
5457
-- "$@")"
5558
eval set -- "${PARSED}"
5659

5760
DISTRO=""
5861
PROJECT_ID=""
5962
LOCAL_BUILD="false"
63+
DOCKER_BUILD="false"
64+
DOCKER_SHELL="false"
6065
while true; do
6166
case "$1" in
6267
--distro)
@@ -71,6 +76,15 @@ while true; do
7176
LOCAL_BUILD="true"
7277
shift
7378
;;
79+
-d | --docker)
80+
DOCKER_BUILD="true"
81+
shift
82+
;;
83+
-s | --docker-shell)
84+
DOCKER_BUILD="true"
85+
DOCKER_SHELL="true"
86+
shift
87+
;;
7488
-h | --help)
7589
print_usage
7690
exit 0
@@ -83,14 +97,25 @@ while true; do
8397
done
8498
readonly PROJECT_ID
8599

86-
if (($# == 0)); then
87-
echo "Missing build name"
100+
if (($# != 1)); then
101+
echo "Must specify exactly one build name"
88102
print_usage
89103
exit 1
90104
fi
91105
readonly BUILD_NAME="$1"
92106

107+
# --local is the most fundamental build mode, in that all other builds
108+
# eventually call this one. For example, a --docker build will build the
109+
# specified docker image, then in a container from that image it will run the
110+
# --local build. Similarly, the GCB build will submit the build to GCB, which
111+
# will call the --local build.
93112
if [[ "${LOCAL_BUILD}" = "true" ]]; then
113+
test -n "${DISTRO}" && io::log_red "Local build ignoring --distro=${DISTRO}"
114+
if [[ "${DOCKER_BUILD}" = "true" ]]; then
115+
echo "Only one of --local or --docker may be specified"
116+
print_usage
117+
exit 1
118+
fi
94119
io::log_h1 "Starting local build: ${BUILD_NAME}"
95120
readonly TIMEFORMAT="==> 🕑 ${BUILD_NAME} completed in %R seconds"
96121
time "${PROGRAM_DIR}/builds/${BUILD_NAME}.sh"
@@ -109,6 +134,44 @@ if [[ -z "${DISTRO}" ]]; then
109134
fi
110135
readonly DISTRO
111136

137+
# Uses docker to locally build the specified image and run the build command.
138+
# Docker builds store their outputs on the host system in `build-out/`.
139+
if [[ "${DOCKER_BUILD}" = "true" ]]; then
140+
io::log_h1 "Starting docker build: ${BUILD_NAME}"
141+
out_dir="${PROJECT_ROOT}/build-out/${DISTRO}-${BUILD_NAME}"
142+
out_home="${out_dir}/h"
143+
out_cmake="${out_dir}/cmake-out"
144+
mkdir -p "${out_home}" "${out_cmake}"
145+
image="gcb-${DISTRO}:latest"
146+
io::log_h2 "Building docker image: ${image}"
147+
docker build -t "${image}" "--build-arg=NCPU=$(nproc)" \
148+
-f "ci/cloudbuild/${DISTRO}.Dockerfile" ci
149+
io::log_h2 "Starting container for ${image} running ${BUILD_NAME}"
150+
run_flags=(
151+
"--interactive"
152+
"--tty"
153+
"--rm"
154+
"--user=$(id -u):$(id -g)"
155+
"--env=USER=$(id -un)"
156+
# Mounts an empty volume over "build-out" to isolate builds from each
157+
# other. Doesn't affect GCB builds, but it helps our local docker builds.
158+
"--volume=/workspace/build-out"
159+
"--volume=${PROJECT_ROOT}:/workspace:Z"
160+
"--workdir=/workspace"
161+
"--volume=${out_cmake}:/workspace/cmake-out:Z"
162+
"--volume=${out_home}:/h:Z"
163+
"--env=HOME=/h"
164+
)
165+
cmd=(ci/cloudbuild/build.sh --local "${BUILD_NAME}")
166+
if [[ "${DOCKER_SHELL}" = "true" ]]; then
167+
io::log "Starting shell, to manually run the requested build use:"
168+
echo "==> ${cmd[*]}"
169+
cmd=("bash")
170+
fi
171+
docker run "${run_flags[@]}" "${image}" "${cmd[@]}"
172+
exit
173+
fi
174+
112175
# Surface invalid arguments early rather than waiting for GCB to fail.
113176
if [ ! -r "${PROGRAM_DIR}/${DISTRO}.Dockerfile" ]; then
114177
echo "Unknown distro: ${DISTRO}"
@@ -120,11 +183,12 @@ elif [ ! -x "${PROGRAM_DIR}/builds/${BUILD_NAME}.sh" ]; then
120183
exit 1
121184
fi
122185

186+
# Uses Google Cloud build to run the specified build.
187+
io::log_h1 "Starting cloud build: ${BUILD_NAME}"
123188
account="$(gcloud config list account --format "value(core.account)")"
124189
subs="_DISTRO=${DISTRO}"
125190
subs+=",_BUILD_NAME=${BUILD_NAME}"
126191
subs+=",_CACHE_TYPE=manual-${account}"
127-
io::log_h1 "Starting cloud build: ${BUILD_NAME}"
128192
io::log "Substitutions ${subs}"
129193
args=(
130194
"--config=ci/cloudbuild/cloudbuild.yaml"
@@ -133,4 +197,4 @@ args=(
133197
if [[ -n "${PROJECT_ID}" ]]; then
134198
args+=("--project=${PROJECT_ID}")
135199
fi
136-
exec gcloud builds submit "${args[@]}" .
200+
gcloud builds submit "${args[@]}" .

0 commit comments

Comments
 (0)