#!/usr/bin/env bash set -Eeuo pipefail source /opt/rustbox-package-builder/scripts/lib.sh : "${SHA:?SHA required}" : "${SRC:?SRC required}" REPOSITORY="$DATA_DIR/repository" APTLY_CONF="$DATA_DIR/state/aptly.conf" OUT="$DATA_DIR/work/out" PKG="$DATA_DIR/work/package" rm -rf "$OUT" "$PKG" mkdir -p "$OUT" "$DATA_DIR/packages/deb" "$DATA_DIR/packages/rpm/x86_64" "$DATA_DIR/packages/suse/x86_64" VERSION=$(python3.11 - "$SRC/Cargo.toml" <<'PY' import sys, tomllib p=tomllib.load(open(sys.argv[1],'rb')) print(p['package']['version']) PY ) [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+([.-][A-Za-z0-9._-]+)?$ ]] || fatal "unsupported RustBox version: $VERSION" SHORT_SHA=${SHA:0:12} requested=${PACKAGE_RELEASE:-auto} if [[ "$requested" == auto ]]; then last_version=""; last_release=0 [[ -r "$DATA_DIR/state/last_version" ]] && last_version=$(cat "$DATA_DIR/state/last_version") [[ -r "$DATA_DIR/state/last_package_release" ]] && last_release=$(cat "$DATA_DIR/state/last_package_release") if [[ "$last_version" == "$VERSION" && "$last_release" =~ ^[0-9]+$ ]]; then RELEASE=$((last_release + 1)) else RELEASE=1 fi else [[ "$requested" =~ ^[1-9][0-9]*$ ]] || fatal "PACKAGE_RELEASE must be auto or a positive integer" RELEASE=$requested fi cd "$SRC" TOOLCHAIN=$(/opt/rustbox-package-builder/scripts/ensure-rust-toolchain.sh "$SRC") log "building RustBox $VERSION-$RELEASE from $SHA with Rust $TOOLCHAIN" [[ -f Cargo.lock ]] || fatal "Cargo.lock is required for reproducible packages" if ! grep -q 'log_dir' src/config.rs || ! grep -q 'log_dir' src/main.rs; then fatal "RustBox source must support native file logging through log_dir" fi case "${RUN_LINUX_TESTS:-compile}" in 0|off|false|no) log "cargo tests skipped" ;; 1|compile|check|no-run) cargo "+$TOOLCHAIN" test --release --locked --no-run ;; full|run) cargo "+$TOOLCHAIN" test --release --locked ;; *) fatal "RUN_LINUX_TESTS must be 0, compile, or full" ;; esac cargo "+$TOOLCHAIN" build --release --locked BINARY="$CARGO_TARGET_DIR/release/rustbox" [[ -x "$BINARY" ]] || fatal "built binary not found: $BINARY" "$BINARY" --version RUSTBOX_CONFIG=/opt/rustbox-package-builder/packaging/rustbox.toml "$BINARY" --check-config MAX_GLIBC_FOUND=$(objdump -T "$BINARY" 2>/dev/null | grep -oE 'GLIBC_[0-9]+\.[0-9]+' | sed 's/^GLIBC_//' | sort -V | tail -n1 || true) if [[ -n "$MAX_GLIBC_FOUND" ]]; then newest=$(printf '%s\n%s\n' "$MAX_GLIBC_VERSION" "$MAX_GLIBC_FOUND" | sort -V | tail -n1) [[ "$newest" == "$MAX_GLIBC_VERSION" ]] || fatal "binary requires GLIBC_$MAX_GLIBC_FOUND; configured maximum is GLIBC_$MAX_GLIBC_VERSION" log "glibc compatibility: GLIBC_$MAX_GLIBC_FOUND or older" fi /opt/rustbox-package-builder/scripts/prepare-package.sh "$SRC" "$PKG" "$BINARY" "$VERSION" "$RELEASE" DEB="$OUT/rustbox_${VERSION}-${RELEASE}_amd64.deb" RPM="$OUT/rustbox-${VERSION}-${RELEASE}.x86_64.rpm" SUSE="$OUT/rustbox-${VERSION}-${RELEASE}.suse.x86_64.rpm" nfpm package --config "$PKG/nfpm-deb.yaml" --packager deb --target "$DEB" nfpm package --config "$PKG/nfpm-rpm.yaml" --packager rpm --target "$RPM" if [[ "${BUILD_SUSE:-1}" == 1 ]]; then nfpm package --config "$PKG/nfpm-suse.yaml" --packager rpm --target "$SUSE" fi ar t "$DEB" | grep '^_gpgorigin$' >/dev/null || fatal "DEB package is not signed" rpm -qpi "$RPM" | awk -F: '/^Signature/ && $0 !~ /\(none\)/ {ok=1} END{exit !ok}' || fatal "RPM package is not signed" if [[ -f "$SUSE" ]]; then rpm -qpi "$SUSE" | awk -F: '/^Signature/ && $0 !~ /\(none\)/ {ok=1} END{exit !ok}' || fatal "SUSE RPM package is not signed"; fi cp -f "$DEB" "$DATA_DIR/packages/deb/" cp -f "$RPM" "$DATA_DIR/packages/rpm/x86_64/" [[ -f "$SUSE" ]] && cp -f "$SUSE" "$DATA_DIR/packages/suse/x86_64/" aptly -config="$APTLY_CONF" repo add -force-replace rustbox "$DEB" >/dev/null SNAPSHOT="rustbox-${VERSION}-${SHORT_SHA}-$(date +%s)" aptly -config="$APTLY_CONF" snapshot create "$SNAPSHOT" from repo rustbox >/dev/null APT_SIGN=(-batch -gpg-key="$GPG_KEY_ID") if aptly -config="$APTLY_CONF" publish list -raw | awk -v d="$REPO_DISTRIBUTION" '$2 == d {found=1} END{exit !found}'; then aptly -config="$APTLY_CONF" publish switch -force-overwrite "${APT_SIGN[@]}" "$REPO_DISTRIBUTION" . "$SNAPSHOT" >/dev/null else aptly -config="$APTLY_CONF" publish snapshot -force-overwrite "${APT_SIGN[@]}" -distribution="$REPO_DISTRIBUTION" -component="$REPO_COMPONENT" -architectures=amd64 "$SNAPSHOT" >/dev/null fi APT_PUBLIC="$DATA_DIR/aptly/public" [[ -f "$APT_PUBLIC/dists/$REPO_DISTRIBUTION/InRelease" ]] || fatal "APT InRelease missing" RELEASE_ID="$(date -u '+%Y%m%dT%H%M%SZ')-${SHORT_SHA}" RELEASE_DIR="$REPOSITORY/releases/$RELEASE_ID" mkdir -p "$RELEASE_DIR/apt" "$RELEASE_DIR/rpm/x86_64" "$RELEASE_DIR/suse/x86_64" rsync -a --delete "$APT_PUBLIC/" "$RELEASE_DIR/apt/" rsync -a --delete "$DATA_DIR/packages/rpm/x86_64/" "$RELEASE_DIR/rpm/x86_64/" if [[ "${BUILD_SUSE:-1}" == 1 ]]; then rsync -a --delete "$DATA_DIR/packages/suse/x86_64/" "$RELEASE_DIR/suse/x86_64/"; fi for rpm_root in "$RELEASE_DIR/rpm/x86_64"; do rm -rf "$rpm_root/repodata"; createrepo_c --update "$rpm_root" >/dev/null done if [[ "${BUILD_SUSE:-1}" == 1 ]]; then rm -rf "$RELEASE_DIR/suse/x86_64/repodata"; createrepo_c --update "$RELEASE_DIR/suse/x86_64" >/dev/null fi GPG_SIGN=(--batch --yes --pinentry-mode loopback) for rpm_root in "$RELEASE_DIR/rpm/x86_64"; do gpg "${GPG_SIGN[@]}" --armor --detach-sign --local-user "$GPG_KEY_ID" --output "$rpm_root/repodata/repomd.xml.asc" "$rpm_root/repodata/repomd.xml" done if [[ "${BUILD_SUSE:-1}" == 1 ]]; then gpg "${GPG_SIGN[@]}" --armor --detach-sign --local-user "$GPG_KEY_ID" --output "$RELEASE_DIR/suse/x86_64/repodata/repomd.xml.asc" "$RELEASE_DIR/suse/x86_64/repodata/repomd.xml" fi gpg --batch --armor --export "$GPG_KEY_ID" > "$RELEASE_DIR/repo-key.gpg" BUILT_AT=$(date -u '+%Y-%m-%dT%H:%M:%SZ') cat > "$RELEASE_DIR/LINUX-BUILD-INFO.txt" <> "$RELEASE_DIR/SUSE-BUILD-INFO.txt" else : > "$RELEASE_DIR/SUSE-BUILD-INFO.txt" fi rm -f "$REPOSITORY/current.new" ln -s "releases/$RELEASE_ID" "$REPOSITORY/current.new" mv -Tf "$REPOSITORY/current.new" "$REPOSITORY/current" printf '%s\n' "$SHA" | atomic_write "$DATA_DIR/state/last_commit" printf '%s\n' "$VERSION" | atomic_write "$DATA_DIR/state/last_version" printf '%s\n' "$RELEASE" | atomic_write "$DATA_DIR/state/last_package_release" retention=${RELEASE_RETENTION:-10} if [[ "$retention" =~ ^[1-9][0-9]*$ ]]; then mapfile -t old < <(find "$REPOSITORY/releases" -mindepth 1 -maxdepth 1 -type d -printf '%T@ %p\n' | sort -nr | awk -v keep="$retention" 'NR>keep {$1=""; sub(/^ /,""); print}') for dir in "${old[@]}"; do [[ "$dir" == "$RELEASE_DIR" ]] || rm -rf -- "$dir"; done fi log "published RustBox $VERSION-$RELEASE: $REPOSITORY_URL"