debian/DEBIAN/control: package metadata, depends on python3.11+, postgresql-client debian/DEBIAN/postinst: creates activeblue-ai system user, installs venv, enables service debian/DEBIAN/prerm: stops and disables service before removal debian/DEBIAN/postrm: purge removes config, logs, venv, and system user debian/lib/systemd/system/activeblue-ai.service: - Runs as dedicated user with PrivateTmp + ProtectSystem hardening - EnvironmentFile=/etc/activeblue-ai/.env - Restart=on-failure with 5s backoff debian/usr/bin/activeblue-ai: CLI with start/stop/restart/status/logs/migrate/health/sweep/privacy/version build_deb.sh: builds activeblue-ai_X.Y.Z_all.deb in dist/ publish_repo.sh: scans packages, generates Release + checksums, optional GPG signing Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
97 lines
2.9 KiB
Bash
97 lines
2.9 KiB
Bash
#!/bin/bash
|
|
# Publish ActiveBlue AI to a local APT repository
|
|
# Run this on the repository host after build_deb.sh
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
VERSION=$(cat "$SCRIPT_DIR/VERSION" 2>/dev/null || echo "0.1.0")
|
|
PKG_NAME="activeblue-ai"
|
|
DEB_FILE="$SCRIPT_DIR/dist/${PKG_NAME}_${VERSION}_all.deb"
|
|
|
|
# APT repo configuration
|
|
REPO_ROOT="${REPO_ROOT:-/srv/apt-repo}"
|
|
REPO_DIST="${REPO_DIST:-stable}"
|
|
REPO_COMP="${REPO_COMP:-main}"
|
|
REPO_ARCH="${REPO_ARCH:-all}"
|
|
GPG_KEY="${GPG_KEY:-}"
|
|
|
|
if [ ! -f "$DEB_FILE" ]; then
|
|
echo "ERROR: .deb not found at $DEB_FILE" >&2
|
|
echo "Run ./build_deb.sh first." >&2
|
|
exit 1
|
|
fi
|
|
|
|
if ! command -v dpkg-scanpackages &>/dev/null; then
|
|
echo "Installing dpkg-dev..."
|
|
sudo apt-get install -y dpkg-dev apt-utils
|
|
fi
|
|
|
|
# Create repo structure
|
|
POOL_DIR="$REPO_ROOT/pool/$REPO_DIST/$REPO_COMP"
|
|
DISTS_DIR="$REPO_ROOT/dists/$REPO_DIST/$REPO_COMP/binary-$REPO_ARCH"
|
|
mkdir -p "$POOL_DIR" "$DISTS_DIR"
|
|
|
|
# Copy .deb to pool
|
|
cp -v "$DEB_FILE" "$POOL_DIR/"
|
|
|
|
# Generate Packages index
|
|
cd "$REPO_ROOT"
|
|
dpkg-scanpackages "pool/$REPO_DIST/$REPO_COMP" /dev/null > "dists/$REPO_DIST/$REPO_COMP/binary-$REPO_ARCH/Packages"
|
|
gzip -9 -c "dists/$REPO_DIST/$REPO_COMP/binary-$REPO_ARCH/Packages" > \
|
|
"dists/$REPO_DIST/$REPO_COMP/binary-$REPO_ARCH/Packages.gz"
|
|
|
|
# Generate Release file
|
|
SUITE="$REPO_DIST"
|
|
DATE=$(date -uR)
|
|
cat > "dists/$REPO_DIST/Release" <<RELEASE
|
|
Origin: ActiveBlue
|
|
Label: ActiveBlue AI
|
|
Suite: $SUITE
|
|
Version: $VERSION
|
|
Codename: $REPO_DIST
|
|
Architectures: $REPO_ARCH
|
|
Components: $REPO_COMP
|
|
Description: ActiveBlue AI APT repository
|
|
Date: $DATE
|
|
RELEASE
|
|
|
|
# Add checksums
|
|
{
|
|
echo "MD5Sum:"
|
|
find "dists/$REPO_DIST/$REPO_COMP" -type f | while read f; do
|
|
rel="${f#dists/$REPO_DIST/}"
|
|
md5=$(md5sum "$f" | cut -d' ' -f1)
|
|
size=$(stat -c%s "$f")
|
|
echo " $md5 $size $rel"
|
|
done
|
|
echo "SHA256:"
|
|
find "dists/$REPO_DIST/$REPO_COMP" -type f | while read f; do
|
|
rel="${f#dists/$REPO_DIST/}"
|
|
sha=$(sha256sum "$f" | cut -d' ' -f1)
|
|
size=$(stat -c%s "$f")
|
|
echo " $sha $size $rel"
|
|
done
|
|
} >> "dists/$REPO_DIST/Release"
|
|
|
|
# Sign with GPG if key provided
|
|
if [ -n "$GPG_KEY" ]; then
|
|
gpg --default-key "$GPG_KEY" --armor --detach-sign \
|
|
-o "dists/$REPO_DIST/Release.gpg" "dists/$REPO_DIST/Release"
|
|
gpg --default-key "$GPG_KEY" --clearsign \
|
|
-o "dists/$REPO_DIST/InRelease" "dists/$REPO_DIST/Release"
|
|
echo "Release signed with GPG key: $GPG_KEY"
|
|
else
|
|
echo "WARNING: No GPG_KEY set — repository is unsigned."
|
|
echo "Set GPG_KEY=<key-id> to sign the release."
|
|
fi
|
|
|
|
echo ""
|
|
echo "Repository published to: $REPO_ROOT"
|
|
echo ""
|
|
echo "To use this repository, add to /etc/apt/sources.list.d/activeblue.list:"
|
|
echo " deb [trusted=yes] http://<your-server>/apt-repo $REPO_DIST $REPO_COMP"
|
|
echo ""
|
|
echo "Then:"
|
|
echo " sudo apt-get update"
|
|
echo " sudo apt-get install activeblue-ai"
|