105 lines
3.7 KiB
Bash
Executable File
105 lines
3.7 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -Eeuo pipefail
|
|
|
|
ROOT=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
|
|
cd "$ROOT"
|
|
ENV_FILE="$ROOT/.env"
|
|
DATA_DIR="$ROOT/data"
|
|
GPG_DIR="$ROOT/gpg"
|
|
DEFAULT_HTTP_PORT=18765
|
|
|
|
log() { printf '[rustbox-package-builder] %s\n' "$*"; }
|
|
die() { printf '[rustbox-package-builder] ERROR: %s\n' "$*" >&2; exit 1; }
|
|
require_cmd() { command -v "$1" >/dev/null 2>&1 || die "missing command: $1"; }
|
|
|
|
require_cmd docker
|
|
if ! docker info >/dev/null 2>&1; then
|
|
die "Docker daemon is unavailable for the current user"
|
|
fi
|
|
if docker compose version >/dev/null 2>&1; then
|
|
COMPOSE=(docker compose)
|
|
elif command -v docker-compose >/dev/null 2>&1; then
|
|
COMPOSE=(docker-compose)
|
|
else
|
|
die "Docker Compose is required"
|
|
fi
|
|
|
|
if [[ ! -f "$ENV_FILE" ]]; then
|
|
cp "$ROOT/.env.example" "$ENV_FILE"
|
|
chmod 0600 "$ENV_FILE"
|
|
die "created .env; set SOURCE_REPO and REPOSITORY_URL, then run ./start.sh again"
|
|
fi
|
|
|
|
set -a
|
|
source "$ENV_FILE"
|
|
set +a
|
|
: "${SOURCE_REPO:?SOURCE_REPO is required in .env}"
|
|
: "${REPOSITORY_URL:?REPOSITORY_URL is required in .env}"
|
|
HTTP_PORT=${HTTP_PORT:-$DEFAULT_HTTP_PORT}
|
|
[[ "$HTTP_PORT" =~ ^[0-9]+$ ]] || die "HTTP_PORT must be numeric"
|
|
(( HTTP_PORT >= 1024 && HTTP_PORT <= 65535 )) || die "HTTP_PORT must be between 1024 and 65535"
|
|
|
|
REPOSITORY_URL=${REPOSITORY_URL%/}
|
|
case "$REPOSITORY_URL" in
|
|
http://*|https://*) ;;
|
|
*) die "REPOSITORY_URL must start with http:// or https://" ;;
|
|
esac
|
|
if [[ "$REPOSITORY_URL" == http://* && ! "$REPOSITORY_URL" =~ :[0-9]+$ ]]; then
|
|
REPOSITORY_URL="${REPOSITORY_URL}:${HTTP_PORT}"
|
|
if grep -q '^REPOSITORY_URL=' "$ENV_FILE"; then
|
|
sed -i "s#^REPOSITORY_URL=.*#REPOSITORY_URL=${REPOSITORY_URL}#" "$ENV_FILE"
|
|
fi
|
|
export REPOSITORY_URL
|
|
log "REPOSITORY_URL updated to $REPOSITORY_URL"
|
|
fi
|
|
|
|
mkdir -p "$DATA_DIR"/{repository,state,cargo,work,packages} "$GPG_DIR"
|
|
chmod 0700 "$GPG_DIR" 2>/dev/null || true
|
|
|
|
log "building builder and repository dashboard"
|
|
"${COMPOSE[@]}" --env-file "$ENV_FILE" build rustbox-package-builder repository-dashboard
|
|
|
|
if [[ ! -s "$GPG_DIR/private-key.asc" ]]; then
|
|
log "generating persistent GPG signing key"
|
|
IMAGE_REF=$("${COMPOSE[@]}" --env-file "$ENV_FILE" config | awk '
|
|
$1 == "rustbox-package-builder:" { in_builder=1; next }
|
|
in_builder && $1 == "image:" { print $2; exit }
|
|
')
|
|
[[ -n "$IMAGE_REF" ]] || die "cannot resolve builder image name"
|
|
uid=$(id -u)
|
|
gid=$(id -g)
|
|
docker run --rm --user "$uid:$gid" --entrypoint /bin/bash -e HOME=/tmp \
|
|
-v "$GPG_DIR:/out" "$IMAGE_REF" -lc '
|
|
set -Eeuo pipefail
|
|
export GNUPGHOME=/tmp/rustbox-keygen
|
|
mkdir -p "$GNUPGHOME" && chmod 0700 "$GNUPGHOME"
|
|
cat > /tmp/gpg-batch <<"KEY"
|
|
Key-Type: RSA
|
|
Key-Length: 3072
|
|
Key-Usage: sign
|
|
Name-Real: RustBox Package Repository
|
|
Name-Email: packages@localhost
|
|
Expire-Date: 0
|
|
%no-protection
|
|
%commit
|
|
KEY
|
|
gpg --batch --generate-key /tmp/gpg-batch
|
|
fingerprint=$(gpg --batch --with-colons --list-secret-keys | awk -F: '\''/^sec:/ {seen=1; next} seen && /^fpr:/ {print $10; exit}'\'')
|
|
test -n "$fingerprint"
|
|
gpg --batch --armor --export-secret-keys "$fingerprint" > /out/private-key.asc
|
|
gpg --batch --armor --export "$fingerprint" > /out/public-key.asc
|
|
printf "%s\n" "$fingerprint" > /out/fingerprint
|
|
chmod 0600 /out/private-key.asc
|
|
chmod 0644 /out/public-key.asc /out/fingerprint
|
|
'
|
|
fi
|
|
|
|
log "starting builder and repository HTTP service"
|
|
"${COMPOSE[@]}" --env-file "$ENV_FILE" up -d rustbox-package-builder repository-http
|
|
printf '\nRepository: %s/\n' "$REPOSITORY_URL"
|
|
printf 'Local HTTP: http://127.0.0.1:%s/\n' "$HTTP_PORT"
|
|
printf 'Health: http://127.0.0.1:%s/healthz\n' "$HTTP_PORT"
|
|
printf 'Builder log: '
|
|
printf '%q ' "${COMPOSE[@]}"
|
|
printf 'logs -f rustbox-package-builder\n'
|