#!/busybox/sh # scotty-git shared action: build and push a container image with Kaniko. # Inputs arrive as INPUT_ environment variables (see action.yml). # Never prints credentials. The registry login only exists inside this short-lived container. set -eu fail() { printf '::error::%s\n' "$*"; exit 1; } DESTINATION=${INPUT_DESTINATION:-} CONTEXT=${INPUT_CONTEXT:-.} DOCKERFILE=${INPUT_DOCKERFILE:-Dockerfile} PUSH=${INPUT_PUSH:-true} REGISTRY=${INPUT_REGISTRY:-} USERNAME=${INPUT_USERNAME:-} PASSWORD=${INPUT_PASSWORD:-} GCP_KEY_JSON=${INPUT_GCP_KEY_JSON:-} CACHE_REPO=${INPUT_CACHE_REPO:-} BUILD_ARGS=${INPUT_BUILD_ARGS:-} EXTRA_ARGS=${INPUT_EXTRA_ARGS:-} # ---- Source: the repository the job already checked out ---- WS=${GITHUB_WORKSPACE:-} if [ -z "$WS" ] || [ ! -d "$WS" ]; then fail "No checked-out workspace found. Add 'uses: actions/checkout@v4' before this step."; fi if [ ! -d "$WS/$CONTEXT" ]; then fail "Build context '$CONTEXT' does not exist in the repository."; fi if [ ! -f "$WS/$DOCKERFILE" ]; then fail "Dockerfile '$DOCKERFILE' not found in the repository."; fi # ---- Destinations ---- DESTS=$(printf '%s' "$DESTINATION" | tr ',\n' ' ') if [ "$PUSH" = "true" ] && [ -z "$(printf '%s' "$DESTS" | tr -d ' ')" ]; then fail "'destination' is required when push is true." fi # ---- Registry login (written only inside this container) ---- mkdir -p /kaniko/.docker if [ -z "$REGISTRY" ]; then for d in $DESTS; do REGISTRY=${d%%/*}; break; done if [ -z "$REGISTRY" ] && [ -n "$CACHE_REPO" ]; then REGISTRY=${CACHE_REPO%%/*}; fi fi AUTH="" if [ -n "$GCP_KEY_JSON" ]; then AUTH=$(printf '_json_key:%s' "$GCP_KEY_JSON" | base64 | tr -d '\n') elif [ -n "$USERNAME" ] && [ -n "$PASSWORD" ]; then AUTH=$(printf '%s:%s' "$USERNAME" "$PASSWORD" | base64 | tr -d '\n') elif [ -n "$USERNAME" ] || [ -n "$PASSWORD" ]; then fail "Set both 'username' and 'password', or neither." fi if [ -n "$AUTH" ]; then if [ -z "$REGISTRY" ]; then fail "Could not tell which registry the credentials are for; set 'registry'."; fi printf '{"auths":{"%s":{"auth":"%s"}}}\n' "$REGISTRY" "$AUTH" > /kaniko/.docker/config.json echo "Logged in to $REGISTRY for this build." fi # ---- Executor flags ---- set -- --context "dir://$WS/$CONTEXT" --dockerfile "$WS/$DOCKERFILE" --cleanup if [ "$PUSH" = "true" ]; then for d in $DESTS; do set -- "$@" --destination "$d"; done set -- "$@" --digest-file /kaniko/digest else set -- "$@" --no-push for d in $DESTS; do set -- "$@" --destination "$d"; done fi if [ -n "$CACHE_REPO" ]; then set -- "$@" --cache=true --cache-repo "$CACHE_REPO"; fi OLDIFS=$IFS IFS=' ' for a in $BUILD_ARGS; do if [ -n "$a" ]; then set -- "$@" --build-arg "$a"; fi done IFS=$OLDIFS # shellcheck disable=SC2086 # extra_args is deliberately split into separate flags if [ -n "$EXTRA_ARGS" ]; then set -- "$@" $EXTRA_ARGS; fi echo "Building $DOCKERFILE (context: $CONTEXT)$( [ "$PUSH" = "true" ] && echo " -> $DESTS" || echo ", not pushing")" # Keep a copy of the output: when a RUN step (not Kaniko itself) is killed for memory, # Kaniko exits 1 and only its message says "exit status 137". { /kaniko/executor "$@" 2>&1; echo "$?" > /kaniko/exit-code; } | tee /kaniko/build.log RC=$(cat /kaniko/exit-code 2>/dev/null || echo 1) if [ "$RC" -ne 0 ] && grep -q 'exit status 137' /kaniko/build.log; then RC=137; fi if [ "$RC" -eq 137 ]; then LIMIT="${SCOTTY_GIT_JOB_MEMORY_LIMIT:-1.5 GB}" fail "Build stopped: this job hit scotty-git's per-job memory limit ($LIMIT) and was killed (exit code 137). The limit protects the shared git server. Reduce the build's memory use (e.g. fewer parallel steps in the Dockerfile, smaller dependencies), or ask the scotty-git admins for a larger runner." elif [ "$RC" -ne 0 ]; then fail "Kaniko build failed (exit code $RC). See the Kaniko output above for the failing step." fi # ---- Output ---- if [ "$PUSH" = "true" ] && [ -f /kaniko/digest ]; then DIGEST=$(cat /kaniko/digest) echo "Pushed: $DIGEST" if [ -n "${GITHUB_OUTPUT:-}" ]; then echo "digest=$DIGEST" >> "$GITHUB_OUTPUT"; fi fi exit 0