diff --git a/scripts/build-apt-repo.sh b/scripts/build-apt-repo.sh index 2f437a0..9edb96b 100755 --- a/scripts/build-apt-repo.sh +++ b/scripts/build-apt-repo.sh @@ -1,10 +1,13 @@ #!/usr/bin/env bash # 从 dist/ 目录中的 .deb 构建标准 apt 仓库,输出到 apt-repo/。 -# 用法:scripts/build-apt-repo.sh [deb_dir] [out_dir] +# 用法:scripts/build-apt-repo.sh [deb_dir] [out_dir] [repo_slug] +# +# .deb 文件不存入 gh-pages(避免 git 历史膨胀)。 +# Packages 索引的 Filename 字段直接指向 GitHub Releases 下载地址, +# apt 客户端会从该 URL 拉取 .deb。 # # 产物结构(部署到 GitHub Pages 的 /debian/ 路径): -# pool/main/b/bash-agent/*.deb -# dists/stable/main/binary-{amd64,arm64,all}/Packages(.gz) +# dists/stable/main/binary-{amd64,arm64}/Packages(.gz) # dists/stable/Release (+ InRelease, 若提供 GPG_PRIVATE_KEY) # # 索引由内嵌 Python 生成(跨平台一致,不依赖 apt-ftparchive)。 @@ -14,21 +17,24 @@ set -euo pipefail VERSION_INPUT="${1:-}" DEB_DIR="${2:-dist}" OUT_DIR="${3:-apt-repo}" +REPO_SLUG="${4:-lloydzhou/bash-agent}" if [[ -z "$VERSION_INPUT" ]];then - echo "用法:$0 [deb_dir] [out_dir]" >&2 + echo "用法:$0 [deb_dir] [out_dir] [repo_slug]" >&2 exit 2 fi VERSION="${VERSION_INPUT#v}" [[ -d "$DEB_DIR" ]] || { echo "错误:deb 目录不存在:$DEB_DIR" >&2; exit 1; } +# GitHub Releases 下载基础 URL(apt 从这里拉 .deb,不经过 gh-pages) +RELEASES_URL="https://github.com/${REPO_SLUG}/releases/download/${VERSION_INPUT}" + REPO_DIR="$OUT_DIR/debian" -POOL_DIR="$REPO_DIR/pool/main/b/bash-agent" rm -rf "$OUT_DIR" -mkdir -p "$POOL_DIR" +mkdir -p "$REPO_DIR/dists/stable" -# 1. 收集 .deb 到 pool(只收架构包;防御性排除 *_all.deb,避免 apt 选择纯 Bash 包) +# 收集 .deb 文件名(只收架构包) shopt -s nullglob debs=( "$DEB_DIR"/bash-agent_*_amd64.deb "$DEB_DIR"/bash-agent_*_arm64.deb ) shopt -u nullglob @@ -36,54 +42,30 @@ if (( ${#debs[@]} == 0 ));then echo "错误:$DEB_DIR 中没有架构 .deb 文件(*_amd64.deb / *_arm64.deb)" >&2 exit 1 fi -for deb in "${debs[@]}"; do - echo "收录:$(basename "$deb")" - cp "$deb" "$POOL_DIR/" -done - -# 2/3. 生成 Packages 索引与 Release 文件 -# 优先使用 apt-ftparchive(官方工具,格式标准);无则退回 Python fallback -if command -v apt-ftparchive >/dev/null 2>&1; then - for arch in amd64 arm64; do - bindir="$REPO_DIR/dists/stable/main/binary-$arch" - mkdir -p "$bindir" - ( cd "$REPO_DIR" && apt-ftparchive packages --arch "$arch" pool/main ) > "$bindir/Packages" - gzip -9n -c "$bindir/Packages" > "$bindir/Packages.gz" - echo "生成:dists/stable/main/binary-$arch/Packages.gz ($(grep -c '^Package:' "$bindir/Packages") 条)" - done - ( cd "$REPO_DIR" && apt-ftparchive release \ - -o APT::FTPArchive::Release::Origin=bash-agent \ - -o APT::FTPArchive::Release::Label=bash-agent \ - -o APT::FTPArchive::Release::Suite=stable \ - -o APT::FTPArchive::Release::Codename=stable \ - -o APT::FTPArchive::Release::Architectures="amd64 arm64" \ - -o APT::FTPArchive::Release::Components=main \ - -o APT::FTPArchive::Release::Description="bash-agent APT repository" \ - dists/stable ) > "$REPO_DIR/dists/stable/Release" - echo "生成:dists/stable/Release(apt-ftparchive)" -else - echo "警告:无 apt-ftparchive,使用 Python 生成索引" >&2 - PYTHON_BIN="$(command -v python3 || command -v python)" - export REPO_DIR VERSION - "$PYTHON_BIN" - <<'PYEOF' -import gzip, hashlib, os, re, subprocess, sys - -repo = os.environ["REPO_DIR"] -version = os.environ["VERSION"] -pool = os.path.join(repo, "pool/main/b/bash-agent") -comp = "main" -dist = os.path.join(repo, "dists/stable") - -debs = sorted(f for f in os.listdir(pool) if f.endswith(".deb")) + +# 生成 Packages 索引(Python,跨平台一致) +# Filename 字段指向 GitHub Releases URL,.deb 不写入 gh-pages +PYTHON_BIN="$(command -v python3 || command -v python)" +export REPO_DIR VERSION RELEASES_URL DEB_DIR + +"$PYTHON_BIN" - <<'PYEOF' +import gzip, hashlib, os, io, tarfile, sys, datetime + +repo = os.environ["REPO_DIR"] +version = os.environ["VERSION"] +releases = os.environ["RELEASES_URL"] +deb_dir = os.environ["DEB_DIR"] +comp = "main" +dist = os.path.join(repo, "dists/stable") + +debs = sorted(f for f in os.listdir(deb_dir) if f.endswith(".deb") and "_all" not in f) if not debs: - sys.exit("错误:pool 中没有 .deb") + sys.exit("错误:没有架构 .deb") def control_fields(deb_path): """读取 .deb 的 control 字段(ar + tar 解出 control.tar.*)""" - import io, tarfile with open(deb_path, "rb") as f: data = f.read() - # ar 归档解析 idx = 8 members = {} while idx + 60 <= len(data): @@ -111,8 +93,8 @@ def control_fields(deb_path): fields["Description"] += "\n" + line.strip() return fields -def pkg_entry(relpath): - full = os.path.join(repo, relpath) +def pkg_entry(deb_name): + full = os.path.join(deb_dir, deb_name) with open(full, "rb") as f: data = f.read() ctl = control_fields(full) @@ -120,6 +102,7 @@ def pkg_entry(relpath): sha256 = hashlib.sha256(data).hexdigest() sha1 = hashlib.sha1(data).hexdigest() md5 = hashlib.md5(data).hexdigest() + filename_url = f"{releases}/{deb_name}" lines = [ f"Package: {ctl.get('Package', 'bash-agent')}", f"Version: {ctl.get('Version', version)}", @@ -133,21 +116,20 @@ def pkg_entry(relpath): if ctl.get("Description"): lines.append(f"Description: {ctl['Description'].splitlines()[0]}") lines += [ - f"Filename: {relpath}", + f"Filename: {filename_url}", f"Size: {size}", f"MD5sum: {md5}", f"SHA1: {sha1}", f"SHA256: {sha256}", ] - return "\n".join(lines) + "\n\n", size, md5, sha1, sha256 + return "\n".join(lines) + "\n\n" # 按架构生成 Packages entries_by_arch = {} for deb in debs: - rel = os.path.join("pool/main/b/bash-agent", deb) - entry, size, md5, sha1, sha256 = pkg_entry(rel) - arch = control_fields(os.path.join(repo, rel)).get("Architecture", "amd64") - entries_by_arch.setdefault(arch, []).append((rel, entry, size, md5, sha1, sha256)) + entry = pkg_entry(deb) + arch = control_fields(os.path.join(deb_dir, deb)).get("Architecture", "amd64") + entries_by_arch.setdefault(arch, []).append(entry) archs = sorted(entries_by_arch) for arch in archs: @@ -155,7 +137,7 @@ for arch in archs: os.makedirs(bindir, exist_ok=True) pkgs_path = os.path.join(bindir, "Packages") with open(pkgs_path, "w") as f: - for _, entry, *_ in entries_by_arch[arch]: + for entry in entries_by_arch[arch]: f.write(entry) with open(pkgs_path + ".gz", "wb") as f: with open(pkgs_path, "rb") as raw: @@ -168,7 +150,6 @@ def file_info(path): data = f.read() return len(data), hashlib.md5(data).hexdigest(), hashlib.sha1(data).hexdigest(), hashlib.sha256(data).hexdigest() -import datetime release_extra = { "Origin": "bash-agent", "Label": "bash-agent", @@ -202,9 +183,8 @@ with open(os.path.join(dist, "Release"), "w") as f: print("生成:dists/stable/Release") print("APT 仓库构建完成:", repo) PYEOF -fi -# 4. 可选 GPG 签名 +# 可选 GPG 签名 if [[ -n "${GPG_PRIVATE_KEY:-}" ]]; then printf '%s\n' "$GPG_PRIVATE_KEY" | gpg --batch --yes --import 2>/dev/null || \ printf '%s\n' "$GPG_PRIVATE_KEY" | base64 -d 2>/dev/null | gpg --batch --yes --import 2>/dev/null || true