secured-remote-docker-build/scripts/trigger_build.sh
Lukas Schaefer 7aeef62a39
Implement auth for git repo
Signed-off-by: Lukas Schaefer <lukas@lschaefer.xyz>
2026-09-03 21:16:26 -04:00

148 lines
4.2 KiB
Shell
Executable file

#!/usr/bin/env bash
# Trigger a BuildKit build via the docker-build-container HTTP API.
# Requires: bash, curl, mktemp, tee, awk, grep (no Python).
#
# Required env:
# BUILDKIT_URL POST endpoint (e.g. https://buildkit.example/build)
# BUILDKIT_TOKEN JWT from mint_token.py
# TAGS Newline- or comma-separated image refs
# USERNAME Registry username
# PASSWORD Registry password
#
# Context — provide one or both:
# COMMIT Git commit SHA; required for JSON (no FILES)
# FILES Newline-separated paths to upload (multipart). Each path is
# both the local file and the context path (multipart filename).
#
# Optional:
# REMOTE Registry host for auth (defaults on the server to Docker Hub)
# REPO_USERNAME Git username for private repo clone
# REPO_PASSWORD Git password/token for private repo clone
set -euo pipefail
json_string() {
local s=$1 i c o out=
for ((i = 0; i < ${#s}; i++)); do
c=${s:i:1}
# case patterns are globs: quote "\\" so a real backslash matches.
case "$c" in
'"') out+='\"' ;;
"\\") out+='\\' ;;
$'\n') out+='\n' ;;
$'\r') out+='\r' ;;
$'\t') out+='\t' ;;
*)
printf -v o '%d' "'$c"
if ((o < 32)); then
printf -v out '%s\\u%04x' "$out" "$o"
else
out+="$c"
fi
;;
esac
done
printf '"%s"' "$out"
}
: "${BUILDKIT_URL:?BUILDKIT_URL is required}"
: "${BUILDKIT_TOKEN:?BUILDKIT_TOKEN is required}"
: "${TAGS:?TAGS is required}"
: "${USERNAME:?USERNAME is required}"
: "${PASSWORD:?PASSWORD is required}"
tag_list=()
while IFS= read -r tag; do
[ -z "${tag}" ] && continue
tag_list+=("${tag}")
done < <(printf '%s\n' "${TAGS}" | tr ',' '\n')
tags_json='['
sep=
for tag in "${tag_list[@]}"; do
tags_json+="${sep}$(json_string "${tag}")"
sep=,
done
tags_json+=']'
file_specs=()
if [ -n "${FILES:-}" ]; then
while IFS= read -r entry; do
[ -z "${entry}" ] && continue
file_specs+=("${entry}")
done < <(printf '%s\n' "${FILES}")
fi
tmpdir="$(mktemp -d)"
trap 'rm -rf "${tmpdir}"' EXIT
headers_file="${tmpdir}/headers"
response_file="${tmpdir}/response"
curl_args=(
-sSN
-D "${headers_file}"
-X POST "${BUILDKIT_URL}"
)
if [ "${#file_specs[@]}" -gt 0 ]; then
# Multipart: --form-string for fields (literal), -F for file parts.
curl_args+=(
--form-string "token=${BUILDKIT_TOKEN}"
--form-string "tags=${tags_json}"
--form-string "username=${USERNAME}"
--form-string "password=${PASSWORD}"
)
if [ -n "${COMMIT:-}" ]; then
curl_args+=(--form-string "commit=${COMMIT}")
fi
if [ -n "${REMOTE:-}" ]; then
curl_args+=(--form-string "remote=${REMOTE}")
fi
if [ -n "${REPO_USERNAME:-}" ]; then
curl_args+=(--form-string "repo_username=${REPO_USERNAME}")
fi
if [ -n "${REPO_PASSWORD:-}" ]; then
curl_args+=(--form-string "repo_password=${REPO_PASSWORD}")
fi
for path in "${file_specs[@]}"; do
# filename= is the relative path inside the build context.
curl_args+=(-F "files=@${path};filename=${path}")
done
else
body_file="${tmpdir}/body.json"
{
printf '{"token":%s,"commit":%s,"tags":%s,"username":%s,"password":%s' \
"$(json_string "${BUILDKIT_TOKEN}")" \
"$(json_string "${COMMIT:-}")" \
"${tags_json}" \
"$(json_string "${USERNAME}")" \
"$(json_string "${PASSWORD}")"
if [ -n "${REMOTE:-}" ]; then
printf ',"remote":%s' "$(json_string "${REMOTE}")"
fi
if [ -n "${REPO_USERNAME:-}" ]; then
printf ',"repo_username":%s' "$(json_string "${REPO_USERNAME}")"
fi
if [ -n "${REPO_PASSWORD:-}" ]; then
printf ',"repo_password":%s' "$(json_string "${REPO_PASSWORD}")"
fi
printf '}'
} >"${body_file}"
curl_args+=(
-H "Content-Type: application/json"
--data-binary @"${body_file}"
)
fi
curl "${curl_args[@]}" | tee "${response_file}"
http_code="$(awk '/^HTTP\//{code=$2} END{print code}' "${headers_file}")"
if [ "${http_code}" != "200" ]; then
echo "buildkit returned HTTP ${http_code:-?} (expected 200)" >&2
exit 1
fi
if grep -q '\[build-service\] ERROR:' "${response_file}"; then
echo "buildkit reported a build failure" >&2
exit 1
fi