|
|
#!/usr/bin/env bash
|
|
|
# 从 dist/linux-deb 产物生成可部署的 APT 仓库目录(repo/)。
|
|
|
set -euo pipefail
|
|
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
|
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
|
|
|
|
DIST_ROOT="$REPO_ROOT/dist/linux-deb"
|
|
|
OUTPUT_REPO_DIR="$REPO_ROOT/dist/repo"
|
|
|
SUITE="${APT_REPO_SUITE:-v10}"
|
|
|
COMPONENT="${APT_REPO_COMPONENT:-main}"
|
|
|
ARCHES="${APT_REPO_ARCHES:-amd64 arm64}"
|
|
|
GPG_KEY_ID="${APT_GPG_KEY_ID:-com.jgzy.product}"
|
|
|
AUTO_CREATE_GPG_KEY="${APT_GPG_AUTO_CREATE:-1}"
|
|
|
PUBLIC_KEY_ASC_NAME="${APT_PUBLIC_KEY_ASC_NAME:-zyyun-archive-keyring.asc}"
|
|
|
PUBLIC_KEY_GPG_NAME="${APT_PUBLIC_KEY_GPG_NAME:-zyyun-archive-keyring.gpg}"
|
|
|
|
|
|
usage() {
|
|
|
cat <<EOF
|
|
|
用法:
|
|
|
$(basename "$0") [--output-dir <dir>] [--suite <name>] [--component <name>] [项目名...]
|
|
|
|
|
|
环境变量:
|
|
|
APT_GPG_KEY_ID GPG 签名 Key ID(默认: com.jgzy.product)
|
|
|
APT_GPG_AUTO_CREATE 1/0。密钥不存在时自动执行 quick-gen-key(默认: 1)
|
|
|
APT_REPO_SUITE 仓库 suite(默认: v10)
|
|
|
APT_REPO_COMPONENT 仓库 component(默认: main)
|
|
|
APT_REPO_ARCHES 仓库架构(默认: "amd64 arm64")
|
|
|
EOF
|
|
|
}
|
|
|
|
|
|
PROJECTS=()
|
|
|
while (($# > 0)); do
|
|
|
case "$1" in
|
|
|
--output-dir)
|
|
|
OUTPUT_REPO_DIR="${2:-}"
|
|
|
shift 2
|
|
|
;;
|
|
|
--suite)
|
|
|
SUITE="${2:-}"
|
|
|
shift 2
|
|
|
;;
|
|
|
--component)
|
|
|
COMPONENT="${2:-}"
|
|
|
shift 2
|
|
|
;;
|
|
|
--help|-h)
|
|
|
usage
|
|
|
exit 0
|
|
|
;;
|
|
|
--)
|
|
|
shift
|
|
|
break
|
|
|
;;
|
|
|
-*)
|
|
|
echo "错误: 未知参数 $1" >&2
|
|
|
exit 1
|
|
|
;;
|
|
|
*)
|
|
|
PROJECTS+=("$1")
|
|
|
shift
|
|
|
;;
|
|
|
esac
|
|
|
done
|
|
|
|
|
|
if (($# > 0)); then
|
|
|
PROJECTS+=("$@")
|
|
|
fi
|
|
|
|
|
|
if [[ ! -d "$DIST_ROOT" ]]; then
|
|
|
echo "错误: 未找到 deb 产物目录 $DIST_ROOT" >&2
|
|
|
echo "请先执行 scripts/docker/run-build.sh 或 scripts/build-linux-deb-all.sh" >&2
|
|
|
exit 1
|
|
|
fi
|
|
|
|
|
|
if ! command -v apt-ftparchive >/dev/null 2>&1; then
|
|
|
echo "错误: 未安装 apt-ftparchive(请安装 apt-utils)" >&2
|
|
|
exit 1
|
|
|
fi
|
|
|
|
|
|
if ! command -v gpg >/dev/null 2>&1; then
|
|
|
echo "错误: 未安装 gpg(签名与公钥导出需要)" >&2
|
|
|
exit 1
|
|
|
fi
|
|
|
|
|
|
ensure_signing_key() {
|
|
|
local key="$1"
|
|
|
local quick_gen_cmd=(
|
|
|
gpg --batch --pinentry-mode loopback --passphrase ""
|
|
|
--quick-gen-key "$key" rsa4096 sign 5y
|
|
|
)
|
|
|
|
|
|
if gpg --list-secret-keys "$key" >/dev/null 2>&1; then
|
|
|
return 0
|
|
|
fi
|
|
|
|
|
|
if [[ "$AUTO_CREATE_GPG_KEY" != "1" ]]; then
|
|
|
echo "错误: 未找到签名私钥: $key" >&2
|
|
|
echo "请先执行以下命令创建密钥,或设置 APT_GPG_AUTO_CREATE=1 自动创建:" >&2
|
|
|
echo " ${quick_gen_cmd[*]}" >&2
|
|
|
exit 1
|
|
|
fi
|
|
|
|
|
|
echo "==> gpg key not found, generating: $key"
|
|
|
"${quick_gen_cmd[@]}"
|
|
|
}
|
|
|
|
|
|
rm -rf "$OUTPUT_REPO_DIR"
|
|
|
mkdir -p "$OUTPUT_REPO_DIR/pool/$COMPONENT" "$OUTPUT_REPO_DIR/dists/$SUITE/$COMPONENT"
|
|
|
|
|
|
copy_project_debs() {
|
|
|
local project="$1"
|
|
|
local initial="${project:0:1}"
|
|
|
local src_dir="$DIST_ROOT/$project"
|
|
|
local dst_dir="$OUTPUT_REPO_DIR/pool/$COMPONENT/$initial/$project"
|
|
|
|
|
|
if [[ ! -d "$src_dir" ]]; then
|
|
|
echo "警告: 未找到项目产物目录 $src_dir,跳过" >&2
|
|
|
return 0
|
|
|
fi
|
|
|
|
|
|
mkdir -p "$dst_dir"
|
|
|
shopt -s nullglob
|
|
|
local files=( "$src_dir"/amd64/*.deb "$src_dir"/arm64/*.deb )
|
|
|
shopt -u nullglob
|
|
|
if (( ${#files[@]} == 0 )); then
|
|
|
echo "警告: $src_dir 下没有 .deb 文件,跳过" >&2
|
|
|
return 0
|
|
|
fi
|
|
|
cp -v "${files[@]}" "$dst_dir/"
|
|
|
}
|
|
|
|
|
|
if ((${#PROJECTS[@]} == 0)); then
|
|
|
shopt -s nullglob
|
|
|
for project_path in "$DIST_ROOT"/*; do
|
|
|
[[ -d "$project_path" ]] || continue
|
|
|
PROJECTS+=( "$(basename "$project_path")" )
|
|
|
done
|
|
|
shopt -u nullglob
|
|
|
fi
|
|
|
|
|
|
if ((${#PROJECTS[@]} == 0)); then
|
|
|
echo "错误: 未发现任何可发布项目($DIST_ROOT 为空)" >&2
|
|
|
exit 1
|
|
|
fi
|
|
|
|
|
|
ensure_signing_key "$GPG_KEY_ID"
|
|
|
|
|
|
for project in "${PROJECTS[@]}"; do
|
|
|
copy_project_debs "$project"
|
|
|
done
|
|
|
|
|
|
# 为每个架构生成独立 Packages(按文件名后缀筛选)。
|
|
|
for arch in $ARCHES; do
|
|
|
out_dir="$OUTPUT_REPO_DIR/dists/$SUITE/$COMPONENT/binary-$arch"
|
|
|
mkdir -p "$out_dir"
|
|
|
tmp_pool="$OUTPUT_REPO_DIR/.tmp-pool-$arch"
|
|
|
rm -rf "$tmp_pool"
|
|
|
mkdir -p "$tmp_pool"
|
|
|
|
|
|
shopt -s globstar nullglob
|
|
|
arch_files=( "$OUTPUT_REPO_DIR"/pool/$COMPONENT/**/*.deb )
|
|
|
shopt -u globstar nullglob
|
|
|
|
|
|
for deb_file in "${arch_files[@]}"; do
|
|
|
base="$(basename "$deb_file")"
|
|
|
# 兼容 xxx_amd64.deb / xxx_arm64.deb 命名
|
|
|
if [[ "$base" == *"_${arch}.deb" ]]; then
|
|
|
rel="${deb_file#$OUTPUT_REPO_DIR/pool/$COMPONENT/}"
|
|
|
mkdir -p "$tmp_pool/$(dirname "$rel")"
|
|
|
cp -f "$deb_file" "$tmp_pool/$rel"
|
|
|
fi
|
|
|
done
|
|
|
|
|
|
apt-ftparchive packages "$tmp_pool" > "$out_dir/Packages"
|
|
|
# apt-ftparchive 会把临时目录的绝对路径写入 Filename,客户端无法下载;改为相对仓库根的 pool/... 路径。
|
|
|
sed -i "s|^Filename: .*\\.tmp-pool-${arch}/|Filename: pool/${COMPONENT}/|" "$out_dir/Packages"
|
|
|
gzip -kf "$out_dir/Packages"
|
|
|
rm -rf "$tmp_pool"
|
|
|
done
|
|
|
|
|
|
apt-ftparchive \
|
|
|
-o APT::FTPArchive::Release::Suite="$SUITE" \
|
|
|
-o APT::FTPArchive::Release::Codename="$SUITE" \
|
|
|
-o APT::FTPArchive::Release::Components="$COMPONENT" \
|
|
|
-o APT::FTPArchive::Release::Architectures="$ARCHES" \
|
|
|
release "$OUTPUT_REPO_DIR/dists/$SUITE" > "$OUTPUT_REPO_DIR/dists/$SUITE/Release"
|
|
|
|
|
|
gpg --default-key "$GPG_KEY_ID" -abs \
|
|
|
-o "$OUTPUT_REPO_DIR/dists/$SUITE/Release.gpg" \
|
|
|
"$OUTPUT_REPO_DIR/dists/$SUITE/Release"
|
|
|
gpg --default-key "$GPG_KEY_ID" --clearsign \
|
|
|
-o "$OUTPUT_REPO_DIR/dists/$SUITE/InRelease" \
|
|
|
"$OUTPUT_REPO_DIR/dists/$SUITE/Release"
|
|
|
|
|
|
gpg --armor --export "$GPG_KEY_ID" > "$OUTPUT_REPO_DIR/$PUBLIC_KEY_ASC_NAME"
|
|
|
gpg --export "$GPG_KEY_ID" > "$OUTPUT_REPO_DIR/$PUBLIC_KEY_GPG_NAME"
|
|
|
|
|
|
SIGN_HINT="已签名(KEY: $GPG_KEY_ID)"
|
|
|
|
|
|
echo ""
|
|
|
echo "==> APT 仓库目录生成完成"
|
|
|
echo " 输出目录: $OUTPUT_REPO_DIR"
|
|
|
echo " Suite: $SUITE, Component: $COMPONENT, Architectures: $ARCHES"
|
|
|
echo " 签名状态: $SIGN_HINT"
|
|
|
echo " 公钥文件: $OUTPUT_REPO_DIR/$PUBLIC_KEY_ASC_NAME"
|
|
|
echo " $OUTPUT_REPO_DIR/$PUBLIC_KEY_GPG_NAME"
|
|
|
echo " 可直接将 dist/repo 打包上传到仓库机。"
|