lolibrary/lolibrary

View on GitHub
bin/build.zsh

Summary

Maintainability
Test Coverage
#!/usr/bin/env zsh
# Usage: ./build service.foo [--push|-p] [--deploy|-d] [--force|-f]
#   Each stage is broken up into its own function so another script can source it.

args=$*

# set global variables we'll need throughout.
NAMESPACE=lolibrary
VERSION=$(git rev-parse HEAD)
PROJECT="github.com/lolibrary/lolibrary"
BASE="${GOPATH}/src/${PROJECT}"
GCP_PROJECT="lolibrary-180111"

# options; setting defaults.
OPT_DOCKER_PUSH=no
OPT_K8S_DEPLOY=no
OPT_FORCE=no
OPT_PRINT_OPTIONS=no

# args used for specific things.
SERVICE_DIR=""
BINARY_NAME=""
DOCKER_TAG=""
CACHE_DIR=""
SERVICE_NAME=""

function setup/parse_options() {
    while [ "$1" != "" ]; do
        local param=$(echo $1 | awk -F= '{print $1}')
        local value=$(echo $1 | awk -F= '{print $2}')

        case $param in
            -h | --help)
                setup/usage
                ;;
            -d | --deploy)
                OPT_K8S_DEPLOY=yes
                ;;
            -p | --push)
                OPT_DOCKER_PUSH=yes
                ;;
            -v | --print-options)
                OPT_PRINT_OPTIONS=yes
                ;;
            *)
                SERVICE_NAME=$param
                ;;
        esac
        shift
    done
}

# Configure options used to generate our builds.
function setup/configure() {
    if [[ "$SERVICE_NAME" = "" ]]; then
        setup/usage
    fi

    SERVICE_DIR="$BASE/$SERVICE_NAME"
    BINARY_NAME="org.lolibrary.$SERVICE_NAME"
    DOCKER_TAG="gcr.io/$GCP_PROJECT/$BINARY_NAME:$VERSION"
    CACHE_DIR="$BASE/.cache/$SERVICE_NAME"

    if [[ ! -d $SERVICE_DIR ]]; then
        print -P "⚠️  Directory %F{green}$SERVICE_DIR%f does not exist."
        exit 1
    fi
}

# Print usage and exit.
function setup/usage() {
    print -P "🌸 Usage: %F{red}build%f [-p|--push] [-d|--deploy] %F{green}service.foo%f"
    exit
}

function setup/print_options() {
    print -P "%F{red}OPT_DOCKER_PUSH%f: %F{blue}$OPT_DOCKER_PUSH%f"
    print -P "%F{red}OPT_K8S_DEPLOY %f: %F{blue}$OPT_K8S_DEPLOY %f"
    print -P "%F{red}SERVICE_NAME   %f: %F{blue}$SERVICE_NAME   %f"
    print -P "%F{red}SERVICE_DIR    %f: %F{blue}$SERVICE_DIR    %f"
    print -P "%F{red}BINARY_NAME    %f: %F{blue}$BINARY_NAME    %f"
    print -P "%F{red}DOCKER_TAG     %f: %F{blue}$DOCKER_TAG     %f"
    print -P "%F{red}CACHE_DIR      %f: %F{blue}$CACHE_DIR      %f"
}

function build/create_cache() {
    mkdir -p $CACHE_DIR
}

function build/go_build() {
    print -P "👩‍🔧 Building %F{green}$BINARY_NAME%f with %F{red}go build%f"

    CGO_ENABLED=0 GOOS=linux ARCH=amd64 \
        go build -i -installsuffix docker \
            -ldflags "-X \"$PROJECT/foundation.Version=$VERSION\" -X \"$PROJECT/libraries/filters.Version=$VERSION\"" \
            -o "$CACHE_DIR/$BINARY_NAME" "$SERVICE_DIR/main.go"

    print -P "✅ Binary %F{green}$BINARY_NAME%f built"
}

function docker/dockerfile() {
    local dockerfile="
FROM scratch
LABEL maintainer=engineering@lolibrary.org
EXPOSE 8080
ADD $BINARY_NAME /
ENTRYPOINT [\"/$BINARY_NAME\"]
"

    local dockerignore="
**/*
!$BINARY_NAME
"

    echo "$dockerignore" > "$CACHE_DIR/.dockerignore"
    echo "$dockerfile" > "$CACHE_DIR/Dockerfile"
}

function docker/build() {
    print -P "🐳 Building docker image %F{green}$DOCKER_TAG%f"

    docker/dockerfile
    docker build -t $DOCKER_TAG $CACHE_DIR > /dev/null 2>&1
}

function docker/push() {
    print -P "🚢 Pushing docker image to %F{green}$DOCKER_TAG%f"

    docker push $DOCKER_TAG
}

function k8s/deploy() {
    local service=$(echo $SERVICE_NAME | sed "s/\./-/g" | sed "s/^service/s/")

    print -P "⚙️  Updating %F{red}$service%f to %F{cyan}$DOCKER_TAG%f in k8s"

    cat "$SERVICE_DIR/manifest/prod.yml" | sed "s#PLACEHOLDER#\"$DOCKER_TAG\"#" | kubectl apply --record -f -

    print -P "♻️  Waiting for %F{red}$service%f to rollout..."

    kubectl rollout status "deployment/$service" -n $NAMESPACE

    print -P "✅ Deployed %F{red}$SERVICE_NAME%f successfully"
}

function debug_print() {
    if [[ "$VERBOSE" = "true" ]]; then
        print $1
    fi
}

debug_print "entering options parsing"
setup/parse_options $*

debug_print "setting up config"
setup/configure
if [[ $OPT_PRINT_OPTIONS = "yes" ]]; then
    setup/print_options
fi

debug_print "build starting"
# now we start to build the program.
build/create_cache
build/go_build

# now we have a build; deploy to docker.
debug_print "docker building"
docker/build

if [[ $OPT_DOCKER_PUSH = "yes" ]]; then
    docker/push
fi

debug_print "ready to deploy, checking."
# deploy to k8s with a confirmation.
if [[ $OPT_K8S_DEPLOY = "yes" ]]; then
    k8s/deploy
fi