blob: a9efc2b4af2864051985d0cb301dfe4e8c21e49f [file] [log] [blame]
#!/usr/bin/env bash
# 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.
# Files are relative to this script directory.
SELF="${BASH_SOURCE[0]}"
cd "$( dirname "${BASH_SOURCE[0]}" )"
COMPOSE_FILE=./infrastructure/docker/build/docker-compose.yml
COMPOSE_FILE_OPT=./infrastructure/docker/build/docker-compose-opt.yml
# Check for dependencies
if ! which docker >/dev/null 2>&1; then
echo "Error: docker is required for a docker build." >&2
exit 1
fi
# If the user defined COMPOSE, use that as the path for docker-compose.
if [ ! -z "$COMPOSE" ]; then
COMPOSECMD=( "$COMPOSE" )
else
COMPOSECMD=()
fi
# Check to see if docker-compose is already installed and use it directly, if possible.
if [ ${#COMPOSECMD[@]} -eq 0 ]; then
if which docker-compose >/dev/null 2>&1; then
COMPOSECMD=( docker-compose )
fi
fi
# If it's unavailable, download the image and run docker-compose inside a container.
# This is considerably slower, but allows for building on hosts without docker-compose.
if [ ${#COMPOSECMD[@]} -eq 0 ]; then
# Pin the version of docker-compose.
IMAGE="docker/compose:1.11.2"
# We need to either mount the docker socket or export the docker host into the container.
# This allows the container to manage "sibling" containers via docker.
if [ -z "$DOCKER_HOST" ]; then
DOCKER_HOST="/var/run/docker.sock"
fi
if [ -S "$DOCKER_HOST" ]; then
DOCKER_ADDR=(-v "$DOCKER_HOST:$DOCKER_HOST" -e DOCKER_HOST)
else
DOCKER_ADDR=(-e DOCKER_HOST -e DOCKER_TLS_VERIFY -e DOCKER_CERT_PATH)
fi
# We mount the current directory (the base of the repository) into the same location
# inside the container. There are many places for which this won't work, but "/" is
# a major one.
#
# You're going to want to avoid keeping your repository in a folder named "/usr/bin",
# "/home", "/var" or any other standard paths that will be needed by the docker container.
#
# This is very unlikely to cause trouble for anyone in practice.
if [ "$(pwd)" == "/" ]; then
echo "Error: Cannot compile directly at filesystem root." >&2
exit 1
fi
# Mount the working directory, and the home directory. Mounting $HOME provides container
# access to config files that are kept there.
VOLUMES=(-v "$(pwd):$(pwd)" -v "$HOME:$HOME" -v "$HOME:/root")
# Prepull the image, to avoid spitting out pull progress during other commands.
if ! docker inspect $IMAGE >/dev/null 2>&1; then
docker pull $IMAGE >/dev/null 2>&1
fi
# COMPOSECMD is kept as an array to significantly simplify handling paths that contain
# spaces and other special characters.
COMPOSECMD=(docker run --rm "${DOCKER_ADDR[@]}" $COMPOSE_OPTIONS "${VOLUMES[@]}" -w "$(pwd)" $IMAGE)
fi
# Parse command line arguments
verbose=0
debug=0
quiet=0
all=0
build_images=0
NO_LOG_FILES=0
NO_SOURCE=0
SIMPLE=0
list=0
failure=0
print_help=0
while getopts h78abdf:lopqvsSL opt; do
case $opt in
\?)
failure=1;
;;
7)
BASE_IMAGE=centos
RHEL_VERSION=7
;;
8)
BASE_IMAGE=rockylinux
RHEL_VERSION=8
;;
a)
all=1
;;
b)
build_images=1
;;
d)
echo '-d is set! Disabling all compiler optimizations for debugging...';
# If DEBUG_BUILD is true, then Golang binaries will remain unoptimized and
# RPM packaging will not strip binaries.
RUN_OPTIONS+=(-e 'DEBUG_BUILD=true');
debug=1
;;
f)
COMPOSE_FILE="$OPTARG"
;;
h)
print_help=1;
;;
L)
NO_LOG_FILES=1
;;
l)
list=1;
;;
o)
COMPOSE_FILE="$COMPOSE_FILE_OPT";
;;
p)
build_images=0
;;
q)
exec >/dev/null 2>&1
quiet=1
;;
v)
verbose=1
;;
s)
SIMPLE=1
;;
S)
NO_SOURCE=1
;;
esac
done
CMD=$("${COMPOSECMD[@]}" -f $COMPOSE_FILE config --services)
# Weasel must be built first otherwise it will fail from build artifacts/cache in an environment that doesn't have git
# e.g. the environment produced by `./pkg source`.
PROJECTS=$(grep -E "^weasel$" <<<"$CMD"; grep -Ev "^weasel$"<<<"$CMD")
HELP_TEXT="$(cat <<HELP_TEXT
Usage: $SELF [options] [projects]
-7 Build RPMs targeting CentOS 7
-8 Build RPMs targeting Rocky Linux 8 (default)
-a Build all projects, including optional ones.
-b Build builder Docker images before building projects
-d Disable compiler optimizations for debugging.
-f <file> Use <file> as the docker-compose. Default: $COMPOSE_FILE
-h Print help message and exit
-L Don$(echo \')t write logs to files - respects output levels on stderr/stdout as set by -q/-v
-l List available projects.
-o Build from the optional list. Same as -f "$COMPOSE_FILE_OPT"
-p Pull builder Docker images, do not build them (default)
-q Quiet mode. Supresses output. (default)
-s Simple output filenames - e.g. traffic_ops.rpm instead of traffic_ops-6.1.0-11637.ec9ff6a6.el8.x86_64.rpm
-S Skip building "source rpms"
-v Verbose mode. Lists all build output.
If no projects are listed, all projects will be packaged.
Valid projects:
$(echo "$PROJECTS" | sed "s/^/ - /")
HELP_TEXT
)";
if [[ "$failure" -ne 0 ]]; then
echo "$HELP_TEXT" >&2;
exit "$failure";
fi
if [[ "$print_help" -eq 1 ]]; then
echo "$HELP_TEXT";
exit 0;
fi
if [[ "$list" -eq 1 ]]; then
echo "$PROJECTS";
exit 0;
fi
shift $((OPTIND-1))
# Mark BASE_IMAGE for export
export BASE_IMAGE;
if [[ -n "$BASE_IMAGE" ]]; then
RUN_OPTIONS+=(-e BASE_IMAGE);
fi
# Mark RHEL_VERSION for export
export RHEL_VERSION;
if [[ -n "$RHEL_VERSION" ]]; then
RUN_OPTIONS+=(-e RHEL_VERSION);
fi
export SIMPLE;
export NO_SOURCE;
export NO_LOG_FILES;
RUN_OPTIONS+=(-e SIMPLE);
RUN_OPTIONS+=(-e NO_SOURCE);
RUN_OPTIONS+=(-e NO_LOG_FILES);
# If no specific packages are listed, or -a is used, run them all.
if (( ! "$#" || "$all" == 1 )); then
set -- `$SELF -f "$COMPOSE_FILE" -l`
fi
# Create the dist directory if it does not exist.
mkdir -p dist
# Build each project in turn.
badproj=""
while (( "$#" )); do
echo Building $1.
(
if (( "$verbose" == 0 )); then
exec >/dev/null 2>&1
fi
# Build the project
if [[ $build_images -eq 1 ]]; then
"${COMPOSECMD[@]}" -f $COMPOSE_FILE build $1 || exit 1
else
"${COMPOSECMD[@]}" -f $COMPOSE_FILE pull $1 || exit 1
fi
"${COMPOSECMD[@]}" -f $COMPOSE_FILE run "${RUN_OPTIONS[@]}" --rm $1 || exit 1
# Check for a chained compose file for this particular project.
# A chained compose file will be named exactly the same as main docker-compose, with .service added,
# where <service> is the name of the specific service to be chained. The file may be a symlink to another
# compose file, in which case the symlink will be followed before it is processed.
if [ -e "$COMPOSE_FILE.$1" ] ; then
$SELF -f $(realpath -e "$COMPOSE_FILE.$1") $([ "$verbose" == 0 ] || echo "-v") $([ "$quiet" == 0 ] || echo "-q") $([ "$debug" == 0 ] || echo "-d") $([ "$build" == 0 ] || echo "-b") $("${COMPOSECMD[@]}" -f $(realpath -e "$COMPOSE_FILE.$1") config --services)
chained_exit=$?
[ $chained_exit == 0 ] || exit $chained_exit
fi
) || {
# Don't totally bail out, but make note of the failures.
failure=1
badproj="$badproj $1"
}
shift
done
[ "$all" == 0 ] || $SELF -o $([ "$verbose" == 0 ] || echo "-v") $([ "$quiet" == 0 ] || echo "-q") $([ "$debug" == 0 ] || echo "-d") || failure=1
if [[ ! -z $badproj ]]; then
echo "Failed to build$badproj."
fi
if [[ -d dist ]]; then
echo "Results in 'dist':"
ls -lt dist
else
echo "dist artifact directory not found."
fi
exit $failure