273 lines
7.4 KiB
Bash
273 lines
7.4 KiB
Bash
#!/usr/bin/env bash
|
|
set -Eeuo pipefail
|
|
|
|
# RustProxy multi-arch OCI builder for RouterOS.
|
|
# Rust compilation runs on BUILDPLATFORM. Target binaries are cross-compiled,
|
|
# so rustc/cargo do not run through QEMU for arm64 builds.
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
|
|
resolve_project_root() {
|
|
if [[ -n "${RUSTPROXY_PROJECT_ROOT:-}" ]]; then
|
|
(cd "$RUSTPROXY_PROJECT_ROOT" && pwd)
|
|
return
|
|
fi
|
|
|
|
if command -v git >/dev/null 2>&1 && git -C "$SCRIPT_DIR" rev-parse --show-toplevel >/dev/null 2>&1; then
|
|
git -C "$SCRIPT_DIR" rev-parse --show-toplevel
|
|
return
|
|
fi
|
|
|
|
local dir="$SCRIPT_DIR"
|
|
while [[ "$dir" != "/" ]]; do
|
|
if [[ -f "$dir/Cargo.toml" ]]; then
|
|
printf '%s\n' "$dir"
|
|
return
|
|
fi
|
|
dir="$(dirname "$dir")"
|
|
done
|
|
|
|
return 1
|
|
}
|
|
|
|
PROJECT_ROOT="$(resolve_project_root)" || {
|
|
echo "Cannot find repository root (Cargo.toml)." >&2
|
|
echo "Set RUSTPROXY_PROJECT_ROOT=/path/to/RustProxy." >&2
|
|
exit 1
|
|
}
|
|
|
|
[[ -f "$PROJECT_ROOT/Cargo.toml" ]] || {
|
|
echo "Missing $PROJECT_ROOT/Cargo.toml" >&2
|
|
exit 1
|
|
}
|
|
|
|
# Supports the invocation:
|
|
# ENV_FILE=integrations/routeros-oci-build/.env bash integrations/routeros-oci-build/build-routeros-oci.sh push
|
|
ENV_FILE="${ROUTEROS_OCI_ENV_FILE:-${ENV_FILE:-$SCRIPT_DIR/.env}}"
|
|
if [[ "$ENV_FILE" != /* ]]; then
|
|
ENV_FILE="$PROJECT_ROOT/$ENV_FILE"
|
|
fi
|
|
|
|
[[ -f "$ENV_FILE" ]] || {
|
|
echo "Missing environment file: $ENV_FILE" >&2
|
|
echo "Copy $SCRIPT_DIR/.env.example to $SCRIPT_DIR/.env and edit it." >&2
|
|
exit 1
|
|
}
|
|
|
|
set -a
|
|
# shellcheck disable=SC1090
|
|
source "$ENV_FILE"
|
|
set +a
|
|
|
|
ACTION="${1:-push}"
|
|
|
|
VERSION="$(awk '
|
|
/^\[package\][[:space:]]*$/ { in_package=1; next }
|
|
/^\[/ && in_package { exit }
|
|
in_package && /^[[:space:]]*version[[:space:]]*=/ {
|
|
line=$0
|
|
sub(/^[^=]*=[[:space:]]*"/, "", line)
|
|
sub(/".*/, "", line)
|
|
print line
|
|
exit
|
|
}
|
|
' "$PROJECT_ROOT/Cargo.toml")"
|
|
[[ -n "$VERSION" ]] || { echo "Cannot determine package version from Cargo.toml" >&2; exit 1; }
|
|
|
|
# Registry/image. Preferred format: REGISTRY + IMAGE_NAME.
|
|
# OCI_IMAGE is kept as a compatible shorthand.
|
|
if [[ -n "${OCI_IMAGE:-}" ]]; then
|
|
IMAGE_REPO="${OCI_IMAGE%/}"
|
|
REGISTRY="${OCI_REGISTRY:-${IMAGE_REPO%%/*}}"
|
|
else
|
|
: "${REGISTRY:?Set REGISTRY in $ENV_FILE}"
|
|
: "${IMAGE_NAME:?Set IMAGE_NAME in $ENV_FILE}"
|
|
REGISTRY="${REGISTRY#https://}"
|
|
REGISTRY="${REGISTRY#http://}"
|
|
REGISTRY="${REGISTRY%/}"
|
|
IMAGE_REPO="${REGISTRY}/${IMAGE_NAME#/}"
|
|
fi
|
|
|
|
REGISTRY="${REGISTRY#https://}"
|
|
REGISTRY="${REGISTRY#http://}"
|
|
REGISTRY="${REGISTRY%/}"
|
|
IMAGE_REPO="${IMAGE_REPO#https://}"
|
|
IMAGE_REPO="${IMAGE_REPO#http://}"
|
|
IMAGE_REPO="${IMAGE_REPO%/}"
|
|
|
|
BUILD_PLATFORMS="${BUILD_PLATFORMS:-linux/amd64,linux/arm64}"
|
|
BUILDER="${BUILDER:-rustproxy-routeros}"
|
|
RUST_VERSION="${RUST_VERSION:-1.88}"
|
|
DEBIAN_SUITE="${DEBIAN_SUITE:-trixie}"
|
|
DOCKERFILE="${DOCKERFILE:-$SCRIPT_DIR/Dockerfile.routeros}"
|
|
OCI_TAG="${OCI_TAG:-$VERSION}"
|
|
OCI_LATEST="${OCI_LATEST:-1}"
|
|
OCI_EXTRA_TAGS="${OCI_EXTRA_TAGS:-routeros}"
|
|
OCI_PULL="${OCI_PULL:-1}"
|
|
OCI_NO_CACHE="${OCI_NO_CACHE:-0}"
|
|
OCI_VERIFY="${OCI_VERIFY:-1}"
|
|
OCI_REGISTRY_CACHE="${OCI_REGISTRY_CACHE:-0}"
|
|
OCI_PROGRESS="${OCI_PROGRESS:-plain}"
|
|
|
|
IMAGE_TAG="${IMAGE_REPO}:${OCI_TAG}"
|
|
|
|
[[ -f "$DOCKERFILE" ]] || { echo "Missing Dockerfile: $DOCKERFILE" >&2; exit 1; }
|
|
|
|
REGISTRY_USERNAME="${OCI_USERNAME:-${REGISTRY_USERNAME:-}}"
|
|
REGISTRY_SECRET="${OCI_TOKEN:-${OCI_PASSWORD:-${REGISTRY_PASSWORD:-}}}"
|
|
|
|
registry_login() {
|
|
if [[ -z "$REGISTRY_USERNAME" && -z "$REGISTRY_SECRET" ]]; then
|
|
return 0
|
|
fi
|
|
if [[ -z "$REGISTRY_USERNAME" || -z "$REGISTRY_SECRET" ]]; then
|
|
echo "Set both OCI_USERNAME and OCI_PASSWORD/OCI_TOKEN, or neither." >&2
|
|
exit 1
|
|
fi
|
|
printf '%s' "$REGISTRY_SECRET" | docker login "$REGISTRY" \
|
|
--username "$REGISTRY_USERNAME" \
|
|
--password-stdin
|
|
}
|
|
|
|
ensure_builder() {
|
|
command -v docker >/dev/null 2>&1 || { echo "docker is required" >&2; exit 127; }
|
|
docker buildx version >/dev/null 2>&1 || { echo "docker buildx is required" >&2; exit 127; }
|
|
if docker buildx inspect "$BUILDER" >/dev/null 2>&1; then
|
|
docker buildx use "$BUILDER"
|
|
else
|
|
local args=(create --name "$BUILDER" --driver docker-container --use)
|
|
[[ -z "${BUILDER_CONFIG:-}" ]] || args+=(--config "$BUILDER_CONFIG")
|
|
docker buildx "${args[@]}" >/dev/null
|
|
fi
|
|
docker buildx inspect --bootstrap >/dev/null
|
|
}
|
|
|
|
revision="unknown"
|
|
if command -v git >/dev/null 2>&1 && git -C "$PROJECT_ROOT" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
|
revision="$(git -C "$PROJECT_ROOT" rev-parse --short=12 HEAD 2>/dev/null || printf 'unknown')"
|
|
fi
|
|
created="$(date -u +'%Y-%m-%dT%H:%M:%SZ')"
|
|
|
|
common_build_args() {
|
|
BUILD_ARGS=(
|
|
build
|
|
--builder "$BUILDER"
|
|
--file "$DOCKERFILE"
|
|
--target runtime-trixie
|
|
--build-arg "BUILD_VERSION=$VERSION"
|
|
--build-arg "RUST_VERSION=$RUST_VERSION"
|
|
--build-arg "DEBIAN_SUITE=$DEBIAN_SUITE"
|
|
--provenance=false
|
|
--sbom=false
|
|
--progress "$OCI_PROGRESS"
|
|
--label "org.opencontainers.image.title=RustProxy"
|
|
--label "org.opencontainers.image.version=$VERSION"
|
|
--label "org.opencontainers.image.revision=$revision"
|
|
--label "org.opencontainers.image.created=$created"
|
|
)
|
|
|
|
[[ "$OCI_PULL" == "1" ]] && BUILD_ARGS+=(--pull)
|
|
[[ "$OCI_NO_CACHE" == "1" ]] && BUILD_ARGS+=(--no-cache)
|
|
|
|
if [[ "$OCI_REGISTRY_CACHE" == "1" ]]; then
|
|
BUILD_ARGS+=(
|
|
--cache-from "type=registry,ref=${IMAGE_REPO}:buildcache"
|
|
--cache-to "type=registry,ref=${IMAGE_REPO}:buildcache,mode=max,image-manifest=true,oci-mediatypes=true"
|
|
)
|
|
fi
|
|
}
|
|
|
|
build_push() {
|
|
registry_login
|
|
ensure_builder
|
|
common_build_args
|
|
|
|
BUILD_ARGS+=(
|
|
--platform "$BUILD_PLATFORMS"
|
|
--tag "$IMAGE_TAG"
|
|
)
|
|
|
|
if [[ "$OCI_LATEST" == "1" && "$OCI_TAG" != "latest" ]]; then
|
|
BUILD_ARGS+=(--tag "${IMAGE_REPO}:latest")
|
|
fi
|
|
|
|
if [[ -n "$OCI_EXTRA_TAGS" ]]; then
|
|
local tag
|
|
IFS=',' read -r -a extra_tags <<< "$OCI_EXTRA_TAGS"
|
|
for tag in "${extra_tags[@]}"; do
|
|
tag="${tag//[[:space:]]/}"
|
|
[[ -n "$tag" ]] && BUILD_ARGS+=(--tag "${IMAGE_REPO}:${tag}")
|
|
done
|
|
fi
|
|
|
|
BUILD_ARGS+=(--push "$PROJECT_ROOT")
|
|
|
|
cat <<INFO
|
|
==> RustProxy RouterOS OCI build
|
|
image: $IMAGE_TAG
|
|
platforms: $BUILD_PLATFORMS
|
|
Rust: $RUST_VERSION
|
|
runtime: debian:$DEBIAN_SUITE-slim
|
|
builder: $BUILDER
|
|
project: $PROJECT_ROOT
|
|
Dockerfile: $DOCKERFILE
|
|
|
|
Rust compilation runs on BUILDPLATFORM; arm64 is cross-compiled.
|
|
INFO
|
|
|
|
docker buildx "${BUILD_ARGS[@]}"
|
|
|
|
if [[ "$OCI_VERIFY" == "1" ]]; then
|
|
echo
|
|
echo "==> Published manifest"
|
|
docker buildx imagetools inspect "$IMAGE_TAG"
|
|
fi
|
|
}
|
|
|
|
build_load() {
|
|
local arch="${2:-amd64}" platform
|
|
case "$arch" in
|
|
amd64|x86_64) platform="linux/amd64"; arch="amd64" ;;
|
|
arm64|aarch64) platform="linux/arm64"; arch="arm64" ;;
|
|
*) echo "Usage: $0 load [amd64|arm64]" >&2; exit 2 ;;
|
|
esac
|
|
|
|
ensure_builder
|
|
common_build_args
|
|
BUILD_ARGS+=(
|
|
--platform "$platform"
|
|
--tag "${IMAGE_REPO}:local-${arch}"
|
|
--load
|
|
"$PROJECT_ROOT"
|
|
)
|
|
|
|
docker buildx "${BUILD_ARGS[@]}"
|
|
printf 'Loaded %s:local-%s (%s)\n' "$IMAGE_REPO" "$arch" "$platform"
|
|
}
|
|
|
|
show_config() {
|
|
cat <<CFG
|
|
project_root=$PROJECT_ROOT
|
|
env_file=$ENV_FILE
|
|
version=$VERSION
|
|
image=$IMAGE_TAG
|
|
registry=$REGISTRY
|
|
platforms=$BUILD_PLATFORMS
|
|
builder=$BUILDER
|
|
rust=$RUST_VERSION
|
|
debian_suite=$DEBIAN_SUITE
|
|
dockerfile=$DOCKERFILE
|
|
registry_cache=$OCI_REGISTRY_CACHE
|
|
CFG
|
|
}
|
|
|
|
case "$ACTION" in
|
|
push) build_push ;;
|
|
load) build_load "${2:-amd64}" ;;
|
|
config) show_config ;;
|
|
*)
|
|
echo "Usage: $0 [push|load [amd64|arm64]|config]" >&2
|
|
exit 2
|
|
;;
|
|
esac
|