148 lines
4.2 KiB
Shell
Executable file
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
|