github.com/discordapp/buildkite-agent@v2.6.6+incompatible/templates/bootstrap.sh (about)

     1  #!/bin/bash
     2  
     3  #
     4  #  _           _ _     _ _    _ _          _                 _       _
     5  # | |         (_) |   | | |  (_) |        | |               | |     | |
     6  # | |__  _   _ _| | __| | | ___| |_ ___   | |__   ___   ___ | |_ ___| |_ _ __ __ _ _ __
     7  # | '_ \| | | | | |/ _` | |/ / | __/ _ \  | '_ \ / _ \ / _ \| __/ __| __| '__/ _` | '_ \
     8  # | |_) | |_| | | | (_| |   <| | ||  __/  | |_) | (_) | (_) | |_\__ \ |_| | | (_| | |_) |
     9  # |_.__/ \__,_|_|_|\__,_|_|\_\_|\__\___|  |_.__/ \___/ \___/ \__|___/\__|_|  \__,_| .__/
    10  #                                                                                 | |
    11  #                                                                                 |_|
    12  # https://github.com/buildkite/agent/blob/master/templates/bootstrap.sh
    13  
    14  # It's possible for a hook or a build script to change things like `set -eou
    15  # pipefail`, causing our bootstrap.sh to misbehave, so this function will set
    16  # them back to what we expect them to be.
    17  function buildkite-flags-reset {
    18    # Causes this script to exit if a variable isn't present
    19    set -u
    20  
    21    # Ensure command pipes fail if any command fails (e.g. fail-cmd | success-cmd == fail)
    22    set -o pipefail
    23  
    24    # Turn off debugging
    25    set +x
    26  
    27    # If a command fails, don't exit, just keep on truckin'
    28    set +e
    29  }
    30  
    31  buildkite-flags-reset
    32  
    33  ##############################################################
    34  #
    35  # BOOTSTRAP FUNCTIONS
    36  # These functions are used throughout the bootstrap.sh file
    37  #
    38  ##############################################################
    39  
    40  BUILDKITE_PROMPT="\033[90m$\033[0m"
    41  
    42  function buildkite-prompt {
    43    # Output "$" prefix in a pleasant grey...
    44    echo -ne "$BUILDKITE_PROMPT"
    45  
    46    # ...each positional parameter with spaces and correct escaping for copy/pasting...
    47    printf " %q" "$@"
    48  
    49    # ...and a trailing newline.
    50    echo
    51  }
    52  
    53  function buildkite-comment {
    54    # Output a comment prefixed with a hash in grey
    55    echo -ne "\033[90m"
    56    echo "#" "$@"
    57    echo -ne "\033[0m"
    58  }
    59  
    60  # Shows the command being run, and runs it
    61  function buildkite-prompt-and-run {
    62    buildkite-prompt "$@"
    63    "$@"
    64  }
    65  
    66  # Shows the command about to be run, and exits if it fails
    67  function buildkite-run {
    68    buildkite-prompt-and-run "$@" || exit $?
    69  }
    70  
    71  function buildkite-debug {
    72    if [[ "$BUILDKITE_AGENT_DEBUG" == "true" ]]; then
    73      echo "$@"
    74    fi
    75  }
    76  
    77  function buildkite-debug-comment {
    78    if [[ "$BUILDKITE_AGENT_DEBUG" == "true" ]]; then
    79      buildkite-comment "$@"
    80    fi
    81  }
    82  
    83  function buildkite-prompt-debug {
    84    if [[ "$BUILDKITE_AGENT_DEBUG" == "true" ]]; then
    85      buildkite-prompt "$@"
    86    fi
    87  }
    88  
    89  # Runs the command, but only output what it's doing if we're in DEBUG mode
    90  function buildkite-run-debug {
    91    buildkite-prompt-debug "$@"
    92    "$@"
    93  }
    94  
    95  # Show an error and exit
    96  function buildkite-error {
    97    echo -e "~~~ :rotating_light: \033[31mBuildkite Error\033[0m"
    98    echo "$@"
    99    echo "^^^ +++"
   100    exit 1
   101  }
   102  
   103  # Show a warning
   104  function buildkite-warning {
   105    echo -ne "\033[33m⚠️ Buildkite Warning:\033[0m "
   106    echo "$@"
   107    echo "^^^ +++"
   108  }
   109  
   110  # Run a hook script. It won't exit on failure. It will store the hooks exit
   111  # status in BUILDKITE_LAST_HOOK_EXIT_STATUS
   112  export BUILDKITE_LAST_HOOK_EXIT_STATUS=""
   113  function buildkite-hook {
   114    HOOK_LABEL="$1"
   115    HOOK_SCRIPT_PATH="$2"
   116  
   117    if [[ -e "$HOOK_SCRIPT_PATH" ]]; then
   118      # Print to the screen we're going to run the hook
   119      echo "~~~ Running $HOOK_LABEL hook"
   120      buildkite-prompt "$HOOK_SCRIPT_PATH"
   121  
   122      # Run the script and store it's exit status. We don't run the hook in a
   123      # subshell because we want the hook scripts to be able to modify the
   124      # bootstrap's ENV variables. The only downside with this approach, is if
   125      # they call `exit`, the bootstrap script will exit as well. We this is an
   126      # acceptable tradeoff.
   127      source "$HOOK_SCRIPT_PATH"
   128      BUILDKITE_LAST_HOOK_EXIT_STATUS=$?
   129  
   130      # Reset the bootstrap.sh flags
   131      buildkite-flags-reset
   132    else
   133      # When in debug mode, show that we've skipped a hook
   134      buildkite-debug "~~~ Running $HOOK_LABEL hook"
   135      buildkite-debug-comment "Skipping, no hook script found at: $HOOK_SCRIPT_PATH"
   136    fi
   137  }
   138  
   139  # Exit from the bootstrap.sh script if the hook exits with a non-0 exit status
   140  function buildkite-hook-exit-on-error {
   141    if [[ -n "$BUILDKITE_LAST_HOOK_EXIT_STATUS" ]] && [[ $BUILDKITE_LAST_HOOK_EXIT_STATUS -ne 0 ]]; then
   142      buildkite-comment "Hook returned an exit status of $BUILDKITE_LAST_HOOK_EXIT_STATUS, exiting..."
   143      exit $BUILDKITE_LAST_HOOK_EXIT_STATUS
   144    fi
   145  }
   146  
   147  function buildkite-global-hook {
   148    buildkite-hook "global $1" "$BUILDKITE_HOOKS_PATH/$1"
   149    buildkite-hook-exit-on-error
   150  }
   151  
   152  function buildkite-local-hook {
   153    if [[ -e ".buildkite/hooks/$1" ]] && [[ "${BUILDKITE_NO_LOCAL_HOOKS:-}" == "true" ]]; then
   154      buildkite-error "Attempted to run .buildkite/hooks/$1, but BUILDKITE_NO_LOCAL_HOOKS is enabled"
   155    fi
   156  
   157    buildkite-hook "local $1" ".buildkite/hooks/$1"
   158    buildkite-hook-exit-on-error
   159  }
   160  
   161  function buildkite-git-clean {
   162    BUILDKITE_GIT_CLEAN_FLAGS=${BUILDKITE_GIT_CLEAN_FLAGS:--fdq}
   163    buildkite-run git clean "$BUILDKITE_GIT_CLEAN_FLAGS"
   164  
   165    if [[ -z "${BUILDKITE_DISABLE_GIT_SUBMODULES:-}" ]]; then
   166      buildkite-run git submodule foreach --recursive git clean "$BUILDKITE_GIT_CLEAN_FLAGS"
   167    fi
   168  }
   169  
   170  ##############################################################
   171  #
   172  # PATH DEFAULTS
   173  # Come up with the paths used throughout the bootstrap.sh file
   174  #
   175  ##############################################################
   176  
   177  # Add the $BUILDKITE_BIN_PATH to the $PATH
   178  export PATH="$PATH:$BUILDKITE_BIN_PATH"
   179  
   180  # Come up with the place that the repository will be checked out to
   181  SANITIZED_AGENT_NAME=$(echo "$BUILDKITE_AGENT_NAME" | tr -d '"')
   182  PROJECT_FOLDER_NAME="$SANITIZED_AGENT_NAME/$BUILDKITE_PROJECT_SLUG"
   183  export BUILDKITE_BUILD_CHECKOUT_PATH="$BUILDKITE_BUILD_PATH/$PROJECT_FOLDER_NAME"
   184  
   185  if [[ "$BUILDKITE_AGENT_DEBUG" == "true" ]]; then
   186    echo "~~~ Build environment variables"
   187    env | grep -e "^BUILDKITE" | sort
   188  fi
   189  
   190  ##############################################################
   191  #
   192  # ENVIRONMENT SETUP
   193  # A place for people to set up environment variables that
   194  # might be needed for their build scripts, such as secret
   195  # tokens and other information.
   196  #
   197  ##############################################################
   198  
   199  buildkite-global-hook "environment"
   200  
   201  ##############################################################
   202  #
   203  # REPOSITORY HANDLING
   204  # Creates the build folder and makes sure we're running the
   205  # build at the right commit.
   206  #
   207  ##############################################################
   208  
   209  # Run the `pre-checkout` hook
   210  buildkite-global-hook "pre-checkout"
   211  
   212  # Remove the checkout folder if BUILDKITE_CLEAN_CHECKOUT is present
   213  if [[ ! -z "${BUILDKITE_CLEAN_CHECKOUT:-}" ]] && [[ "$BUILDKITE_CLEAN_CHECKOUT" == "true" ]]; then
   214    echo "~~~ Cleaning project checkout"
   215  
   216    buildkite-run rm -rf "$BUILDKITE_BUILD_CHECKOUT_PATH"
   217  fi
   218  
   219  echo "~~~ Preparing build folder"
   220  
   221  buildkite-run mkdir -p "$BUILDKITE_BUILD_CHECKOUT_PATH"
   222  buildkite-run cd "$BUILDKITE_BUILD_CHECKOUT_PATH"
   223  
   224  # If the user has specificed their own checkout hook
   225  if [[ -e "$BUILDKITE_HOOKS_PATH/checkout" ]]; then
   226    buildkite-global-hook "checkout"
   227  else
   228    # If enabled, automatically run an ssh-keyscan on the git ssh host, to prevent
   229    # a yes/no promp from appearing when cloning/fetching
   230    if [[ "${BUILDKITE_AUTO_SSH_FINGERPRINT_VERIFICATION:-false}" == "true" ]]; then
   231      # Only bother running the keyscan if the SSH host has been provided by
   232      # Buildkite. It won't be present if the host isn't using the SSH protocol
   233      if [[ ! -z "${BUILDKITE_REPO_SSH_HOST:-}" ]]; then
   234        : "${BUILDKITE_SSH_DIRECTORY:="$HOME/.ssh"}"
   235        : "${BUILDKITE_SSH_KNOWN_HOST_PATH:="$BUILDKITE_SSH_DIRECTORY/known_hosts"}"
   236  
   237        # Ensure the known_hosts file exists
   238        mkdir -p "$BUILDKITE_SSH_DIRECTORY"
   239        touch "$BUILDKITE_SSH_KNOWN_HOST_PATH"
   240  
   241        # Only add the output from ssh-keyscan if it doesn't already exist in the
   242        # known_hosts file (unhashed or hashed).
   243        #
   244        # Note: We can't rely on exit status. Older versions of ssh-keygen always
   245        # exit successful. Only the presence of output is an indicator of whether
   246        # the host key was found or not.
   247        buildkite-prompt-debug ssh-keygen -f "$BUILDKITE_SSH_KNOWN_HOST_PATH" -F "$BUILDKITE_REPO_SSH_HOST"
   248        if [ -z "$(ssh-keygen -f "$BUILDKITE_SSH_KNOWN_HOST_PATH" -F "$BUILDKITE_REPO_SSH_HOST")" ]; then
   249          buildkite-prompt ssh-keyscan "$BUILDKITE_REPO_SSH_HOST"
   250          ssh-keyscan "$BUILDKITE_REPO_SSH_HOST" >> "$BUILDKITE_SSH_KNOWN_HOST_PATH" ||
   251            buildkite-warning "Couldn't ssh key scan repository host $BUILDKITE_REPO_SSH_HOST into $BUILDKITE_SSH_KNOWN_HOST_PATH"
   252          buildkite-debug-comment "Added \"$BUILDKITE_REPO_SSH_HOST\" to the list of known hosts at \"$BUILDKITE_SSH_KNOWN_HOST_PATH\""
   253        else
   254          buildkite-debug-comment "Host \"$BUILDKITE_REPO_SSH_HOST\" already in list of known hosts at \"$BUILDKITE_SSH_KNOWN_HOST_PATH\""
   255        fi
   256      else
   257        buildkite-debug-comment "No repo host to scan for auto SSH fingerprint verification"
   258      fi
   259    else
   260      buildkite-debug-comment "Skipping auto SSH fingerprint verification"
   261    fi
   262  
   263    # Disable any interactive Git/SSH prompting
   264    export GIT_TERMINAL_PROMPT=0
   265  
   266    if [[ "$BUILDKITE_AGENT_DEBUG" == "true" ]]; then
   267      buildkite-run git --version
   268    fi
   269  
   270    # Do we need to do a git checkout?
   271    if [[ -d ".git" ]]; then
   272      buildkite-run git remote set-url origin "$BUILDKITE_REPO"
   273    else
   274      BUILDKITE_GIT_CLONE_FLAGS=${BUILDKITE_GIT_CLONE_FLAGS:--v}
   275      buildkite-run git clone "$BUILDKITE_GIT_CLONE_FLAGS" -- "$BUILDKITE_REPO" .
   276    fi
   277  
   278    # Git clean prior to checkout
   279    buildkite-git-clean
   280  
   281    # If a refspec is provided then use it instead.
   282    # i.e. `refs/not/a/head`
   283    if [[ -n "${BUILDKITE_REFSPEC:-}" ]]; then
   284      buildkite-run git fetch -v origin "$BUILDKITE_REFSPEC"
   285      buildkite-run git checkout -f "$BUILDKITE_COMMIT"
   286  
   287    # GitHub has a special ref which lets us fetch a pull request head, whether
   288    # or not there is a current head in this repository or another which
   289    # references the commit. We presume a commit sha is provided. See:
   290    # https://help.github.com/articles/checking-out-pull-requests-locally/#modifying-an-inactive-pull-request-locally
   291    elif [[ "$BUILDKITE_PULL_REQUEST" != "false" ]] && [[ "$BUILDKITE_PROJECT_PROVIDER" == *"github"* ]]; then
   292      buildkite-run git fetch -v origin "refs/pull/$BUILDKITE_PULL_REQUEST/head"
   293      buildkite-comment "FETCH_HEAD is now $(git rev-parse FETCH_HEAD)"
   294      buildkite-run git checkout -f "$BUILDKITE_COMMIT"
   295  
   296    # If the commit is "HEAD" then we can't do a commit-specific fetch and will
   297    # need to fetch the remote head and checkout the fetched head explicitly.
   298    elif [[ "$BUILDKITE_COMMIT" == "HEAD" ]]; then
   299      buildkite-run git fetch -v origin "$BUILDKITE_BRANCH"
   300      buildkite-run git checkout -f FETCH_HEAD
   301  
   302    # Otherwise fetch and checkout the commit directly. Some repositories don't
   303    # support fetching a specific commit so we fall back to fetching all heads
   304    # and tags, hoping that the commit is included.
   305    else
   306      # By default `git fetch origin` will only fetch tags which are reachable
   307      # from a fetches branch. git 1.9.0+ changed `--tags` to fetch all tags in
   308      # addition to the default refspec, but pre 1.9.0 it excludes the default
   309      # refspec.
   310      buildkite-prompt-and-run git fetch -v origin "$BUILDKITE_COMMIT" ||
   311        buildkite-run git fetch -v origin "$(git config remote.origin.fetch)" "+refs/tags/*:refs/tags/*"
   312      buildkite-run git checkout -f "$BUILDKITE_COMMIT"
   313    fi
   314  
   315    if [[ -z "${BUILDKITE_DISABLE_GIT_SUBMODULES:-}" ]]; then
   316      # `submodule sync` will ensure the .git/config matches the .gitmodules file.
   317      # The command is only available in git version 1.8.1, so if the call fails,
   318      # continue the bootstrap script, and show an informative error.
   319      buildkite-prompt-and-run git submodule sync --recursive || {
   320          buildkite-warning "Failed to recursively sync git submodules. This is most likely because you have an older version of git installed ($(git --version)) and you need version 1.8.1 and above. If you're using submodules, it's highly recommended you upgrade if you can."
   321          buildkite-run git submodule sync
   322        }
   323  
   324      buildkite-prompt-and-run git submodule update --init --recursive --force || {
   325          buildkite-warning "Failed to update git submodules forcibly. This is most likely because you have an older version of git installed ($(git --version)) and you need version 1.7.6 and above. If you're using submodules, it's highly recommended you upgrade if you can."
   326          buildkite-run git submodule update --init --recursive
   327        }
   328  
   329      buildkite-run git submodule foreach --recursive git reset --hard
   330    fi
   331  
   332    # Git clean after checkout
   333    buildkite-git-clean
   334  
   335    # Grab author and commit information and send it back to Buildkite
   336    buildkite-debug "~~~ Saving Git information"
   337  
   338    # Check to see if the meta data exists before setting it
   339    buildkite-debug-comment "Checking to see if Git data needs to be sent to Buildkite"
   340    if ! buildkite-run-debug buildkite-agent meta-data exists "buildkite:git:commit"; then
   341      buildkite-debug-comment "Sending Git commit information back to Buildkite"
   342      buildkite-run-debug buildkite-agent meta-data set "buildkite:git:commit" "$(git show HEAD -s --format=fuller --no-color)"
   343      buildkite-run-debug buildkite-agent meta-data set "buildkite:git:branch" "$(git branch --contains HEAD --no-color)"
   344    fi
   345  fi
   346  
   347  # Store the current value of BUILDKITE_BUILD_CHECKOUT_PATH, so we can detect if
   348  # one of the post-checkout hooks changed it.
   349  PREVIOUS_BUILDKITE_BUILD_CHECKOUT_PATH=$BUILDKITE_BUILD_CHECKOUT_PATH
   350  
   351  # Run the `post-checkout` hook
   352  buildkite-global-hook "post-checkout"
   353  
   354  # Now that we have a repo, we can perform a `post-checkout` local hook
   355  buildkite-local-hook "post-checkout"
   356  
   357  # If the working directory has been changed by a hook, log and switch to it
   358  if [[ "$BUILDKITE_BUILD_CHECKOUT_PATH" != "$PREVIOUS_BUILDKITE_BUILD_CHECKOUT_PATH" ]]; then
   359    echo "~~~ A post-checkout hook has changed the working directory to $BUILDKITE_BUILD_CHECKOUT_PATH"
   360  
   361    if [ -d "$BUILDKITE_BUILD_CHECKOUT_PATH" ]; then
   362      buildkite-run cd "$BUILDKITE_BUILD_CHECKOUT_PATH"
   363    else
   364      buildkite-error "Failed to switch to \"$BUILDKITE_BUILD_CHECKOUT_PATH\" as it doesn't exist"
   365    fi
   366  fi
   367  
   368  ##############################################################
   369  #
   370  # RUN THE BUILD
   371  # Determines how to run the build, and then runs it
   372  #
   373  ##############################################################
   374  
   375  # Run the global `pre-command` hook
   376  buildkite-global-hook "pre-command"
   377  
   378  # Run the per-checkout `pre-command` hook
   379  buildkite-local-hook "pre-command"
   380  
   381  # If the user has specificed a local `command` hook
   382  if [[ -e ".buildkite/hooks/command" ]]; then
   383    if [[ "${BUILDKITE_NO_LOCAL_HOOKS:-}" == "true" ]]; then
   384      buildkite-error "Attempted to run .buildkite/hooks/command, but BUILDKITE_NO_LOCAL_HOOKS is enabled"
   385    fi
   386  
   387    # Manually run the hook to avoid it from exiting on failure
   388    buildkite-hook "local command" ".buildkite/hooks/command"
   389  
   390    # Capture the exit status from the build script
   391    export BUILDKITE_COMMAND_EXIT_STATUS=$BUILDKITE_LAST_HOOK_EXIT_STATUS
   392  # Then check for a global hook path
   393  elif [[ -e "$BUILDKITE_HOOKS_PATH/command" ]]; then
   394    # Manually run the hook to avoid it from exiting on failure
   395    buildkite-hook "global command" "$BUILDKITE_HOOKS_PATH/command"
   396  
   397    # Capture the exit status from the build script
   398    export BUILDKITE_COMMAND_EXIT_STATUS=$BUILDKITE_LAST_HOOK_EXIT_STATUS
   399  else
   400    # Make sure we actually have a command to run
   401    if [[ -z "$BUILDKITE_COMMAND" ]]; then
   402      buildkite-error "No command has been defined. Please go to \"Project Settings\" and configure your build step's \"Command\""
   403    fi
   404  
   405    # Literal file paths are just executed as the command
   406    #
   407    # NOTE: There is a slight problem with this check - and it's with usage with
   408    # Docker. If you specify a script to run inside the docker container, and that
   409    # isn't on the file system at the same path, then it won't match, and it'll be
   410    # treated as an eval. For example, you mount your repository at /app, and tell
   411    # the agent run `app/ci.sh`, ci.sh won't exist on the filesytem at this point
   412    # at app/ci.sh. The solution is to make sure the `WORKDIR` directroy of the
   413    # docker container is at /app in that case.
   414    if [[ -f "./$BUILDKITE_COMMAND" ]]; then
   415      BUILDKITE_COMMAND_DESCRIPTION="Running command"
   416      printf -v BUILDKITE_COMMAND_PROMPT "./%q" "$BUILDKITE_COMMAND"
   417      BUILDKITE_COMMAND_PATH="$BUILDKITE_COMMAND"
   418  
   419    # Otherwise we presume it is a shell script snippet
   420    else
   421      # Make sure the agent is even allowed to eval commands
   422      if [[ "$BUILDKITE_COMMAND_EVAL" != "true" ]]; then
   423        buildkite-error "This agent is not allowed to evaluate console commands. To allow this, re-run this agent without the \`--no-command-eval\` option, or specify a script within your repository to run instead (such as scripts/test.sh)."
   424      fi
   425  
   426      buildkite-debug "~~~ Preparing build script"
   427  
   428      BUILDKITE_COMMAND_DESCRIPTION="Running build script"
   429      BUILDKITE_COMMAND_PROMPT="$BUILDKITE_COMMAND"
   430      BUILDKITE_COMMAND_PATH="buildkite-script-$BUILDKITE_JOB_ID"
   431  
   432      # We'll actually run a temporary file with pipefail and exit-on-fail
   433      # containing the full script body, printed literally through printf
   434      printf "#!/bin/bash\nset -eo pipefail\n%s\n" "$BUILDKITE_COMMAND" > "$BUILDKITE_COMMAND_PATH"
   435  
   436      if [[ "$BUILDKITE_AGENT_DEBUG" == "true" ]]; then
   437        buildkite-run cat "./$BUILDKITE_COMMAND_PATH"
   438      fi
   439    fi
   440  
   441    # Make sure the command is executable
   442    if [[ ! -x "./$BUILDKITE_COMMAND_PATH" ]]; then
   443      buildkite-run-debug chmod +x "./$BUILDKITE_COMMAND_PATH"
   444    fi
   445  
   446    ## Docker
   447    if [[ -n "${BUILDKITE_DOCKER:-}" ]]; then
   448      DOCKER_CONTAINER="buildkite_${BUILDKITE_JOB_ID}_container"
   449      DOCKER_IMAGE="buildkite_${BUILDKITE_JOB_ID}_image"
   450  
   451      function docker-cleanup {
   452        echo "~~~ Cleaning up Docker containers"
   453        buildkite-prompt-and-run docker rm -f -v "$DOCKER_CONTAINER"
   454      }
   455  
   456      trap docker-cleanup EXIT
   457  
   458      # Build the Docker image, namespaced to the job
   459      echo "~~~ Building Docker image $DOCKER_IMAGE"
   460      buildkite-run docker build -f "${BUILDKITE_DOCKER_FILE:-Dockerfile}" -t "$DOCKER_IMAGE" .
   461  
   462      # Run the build script command in a one-off container
   463      echo "~~~ $BUILDKITE_COMMAND_DESCRIPTION (in Docker container)"
   464      buildkite-prompt-and-run docker run --name "$DOCKER_CONTAINER" "$DOCKER_IMAGE" "./$BUILDKITE_COMMAND_PATH"
   465  
   466      # Capture the exit status from the build script
   467      export BUILDKITE_COMMAND_EXIT_STATUS=$?
   468  
   469    ## Docker Compose
   470    elif [[ -n "${BUILDKITE_DOCKER_COMPOSE_CONTAINER:-}" ]]; then
   471      # Compose strips dashes and underscores, so we'll remove them to match the docker container names
   472      COMPOSE_PROJ_NAME="buildkite${BUILDKITE_JOB_ID//-}"
   473      COMPOSE_COMMAND=(docker-compose)
   474      IFS=":" read -ra COMPOSE_FILES <<< "${BUILDKITE_DOCKER_COMPOSE_FILE:-docker-compose.yml}"
   475      for FILE in "${COMPOSE_FILES[@]}"; do
   476        COMPOSE_COMMAND+=(-f "$FILE")
   477      done
   478      COMPOSE_COMMAND+=(-p "$COMPOSE_PROJ_NAME")
   479  
   480      # Switch on verbose in debug mode
   481      if [[ "$BUILDKITE_AGENT_DEBUG" == "true" ]]; then
   482        COMPOSE_COMMAND+=(--verbose)
   483      fi
   484  
   485      function compose-cleanup {
   486        if [[ "${BUILDKITE_DOCKER_COMPOSE_LEAVE_VOLUMES:-false}" == "true" ]]; then
   487          REMOVE_VOLUME_FLAG=""
   488        else
   489          REMOVE_VOLUME_FLAG="-v"
   490        fi
   491  
   492        echo "~~~ Cleaning up Docker containers"
   493  
   494        # Send them a friendly kill
   495        buildkite-prompt-and-run "${COMPOSE_COMMAND[@]}" kill
   496  
   497        if [[ "$(docker-compose --version)" == *version\ 1.6.* ]]; then
   498          # 1.6
   499  
   500          # There's no --all flag to remove adhoc containers
   501          buildkite-prompt-and-run "${COMPOSE_COMMAND[@]}" rm --force "$REMOVE_VOLUME_FLAG"
   502  
   503          # So now we remove the adhoc container
   504          COMPOSE_CONTAINER_NAME="${COMPOSE_PROJ_NAME}_${BUILDKITE_DOCKER_COMPOSE_CONTAINER}"
   505          buildkite-prompt-and-run docker rm -f "$REMOVE_VOLUME_FLAG" "${COMPOSE_CONTAINER_NAME}_run_1"
   506        else
   507          # 1.7+
   508  
   509          # `compose down` doesn't support force removing images, so we use `rm --force`
   510          buildkite-prompt-and-run "${COMPOSE_COMMAND[@]}" rm --force --all "$REMOVE_VOLUME_FLAG"
   511  
   512          # Stop and remove all the linked services and network
   513          buildkite-prompt-and-run "${COMPOSE_COMMAND[@]}" down
   514        fi
   515      }
   516  
   517      trap compose-cleanup EXIT
   518  
   519      # Build the Docker images using Compose, namespaced to the job
   520      echo "~~~ Building Docker images"
   521  
   522      if [[ "${BUILDKITE_DOCKER_COMPOSE_BUILD_ALL:-false}" == "true" ]]; then
   523        buildkite-run "${COMPOSE_COMMAND[@]}" build --pull
   524      else
   525        buildkite-run "${COMPOSE_COMMAND[@]}" build --pull "$BUILDKITE_DOCKER_COMPOSE_CONTAINER"
   526      fi
   527  
   528      # Run the build script command in the service specified in BUILDKITE_DOCKER_COMPOSE_CONTAINER
   529      echo "~~~ $BUILDKITE_COMMAND_DESCRIPTION (in Docker Compose container)"
   530      buildkite-prompt-and-run "${COMPOSE_COMMAND[@]}" run "$BUILDKITE_DOCKER_COMPOSE_CONTAINER" "./$BUILDKITE_COMMAND_PATH"
   531  
   532      # Capture the exit status from the build script
   533      export BUILDKITE_COMMAND_EXIT_STATUS=$?
   534  
   535    ## Standard
   536    else
   537      echo "~~~ $BUILDKITE_COMMAND_DESCRIPTION"
   538      echo -ne "$BUILDKITE_PROMPT "
   539      echo "$BUILDKITE_COMMAND_PROMPT"
   540      "./$BUILDKITE_COMMAND_PATH"
   541  
   542      # Capture the exit status from the build script
   543      export BUILDKITE_COMMAND_EXIT_STATUS=$?
   544  
   545      # Reset the bootstrap.sh flags
   546      buildkite-flags-reset
   547    fi
   548  fi
   549  
   550  # Run the per-checkout `post-command` hook
   551  buildkite-local-hook "post-command"
   552  
   553  # Run the global `post-command` hook
   554  buildkite-global-hook "post-command"
   555  
   556  ##############################################################
   557  #
   558  # ARTIFACTS
   559  # Uploads and build artifacts associated with this build
   560  #
   561  ##############################################################
   562  
   563  if [[ -n "$BUILDKITE_ARTIFACT_PATHS" ]]; then
   564    # Run the per-checkout `pre-artifact` hook
   565    buildkite-local-hook "pre-artifact"
   566  
   567    # Run the global `pre-artifact` hook
   568    buildkite-global-hook "pre-artifact"
   569  
   570    echo "~~~ Uploading artifacts"
   571    if [[ -n "${BUILDKITE_ARTIFACT_UPLOAD_DESTINATION:-}" ]]; then
   572      buildkite-prompt-and-run buildkite-agent artifact upload "$BUILDKITE_ARTIFACT_PATHS" "$BUILDKITE_ARTIFACT_UPLOAD_DESTINATION"
   573    else
   574      buildkite-prompt-and-run buildkite-agent artifact upload "$BUILDKITE_ARTIFACT_PATHS"
   575    fi
   576  
   577    # If the artifact upload fails, open the current group and exit with an error
   578    if [[ $? -ne 0 ]]; then
   579      buildkite-error "Unable to upload artifacts"
   580    fi
   581  
   582    # Run the per-checkout `post-artifact` hook
   583    buildkite-local-hook "post-artifact"
   584  
   585    # Run the global `post-artifact` hook
   586    buildkite-global-hook "post-artifact"
   587  fi
   588  
   589  # Run the per-checkout `pre-exit` hook
   590  buildkite-local-hook "pre-exit"
   591  
   592  # Run the global `pre-exit` hook
   593  buildkite-global-hook "pre-exit"
   594  
   595  # Be sure to exit this script with the same exit status that the users build
   596  # script exited with.
   597  exit $BUILDKITE_COMMAND_EXIT_STATUS