任何机器可用
curl -fsSL https://mm.md/ll-export-layer.sh | bash
安装。
卸载:
rm -f /usr/local/bin/ll-export-layer ~/.local/bin/ll-export-layer
(按你的安装位置)。
#!/usr/bin/env bash
#
# 一键从 OSTree 仓库导出玲珑应用为 .layer 文件
#
# 功能说明:
# - 自动从指定的 OSTree 仓库中列出所有玲珑应用(binary 层)
# - 让你选择要导出的 APPID / 版本 / 架构 / 渠道
# - 按你的思路执行:
# 1) ostree checkout → binary 目录
# 2) (如果 builder 缓存仓库已存在)删除对应 local: 引用 + prune
# 3) ll-builder import-dir binary
# 4) 在 binary 中自动查找 linglong.yaml
# 5) ll-builder export --layer -f linglong.yaml
#
# 使用方法:
# 直接运行:
# ./ll-export-layer.sh
# 或者指定源仓库(默认是系统仓库 /var/lib/linglong/repo):
# ./ll-export-layer.sh /var/lib/linglong/repo
# ./ll-export-layer.sh "$HOME/.cache/linglong-builder/repo"
#
set -euo pipefail
# 默认的源 OSTree 仓库路径:系统里安装的玲珑应用仓库
SOURCE_REPO="${1:-/var/lib/linglong/repo}"
# ll-builder 默认使用的缓存仓库位置(不需要手动 init,ll-builder 会自己创建)
BUILDER_REPO="$HOME/.cache/linglong-builder/repo"
# 导出的 layer 和 checkout 出来的 rootfs 会放在这里
OUTPUT_ROOT="$HOME/ll-output"
########################################
# 工具检测
########################################
need_cmd() {
if ! command -v "$1" >/dev/null 2>&1; then
echo "错误:未找到命令 '$1',请先安装或加入 PATH。" >&2
exit 1
fi
}
echo "==> 检查必要命令是否存在..."
need_cmd ostree
need_cmd ll-builder
########################################
# 从 OSTree 仓库读取已安装应用列表
########################################
echo "==> 从 OSTree 仓库读取已安装应用列表:$SOURCE_REPO"
# 获取所有 ref,过滤出 binary 层:
# 形如:stable:main/org.deepin.base/25.2.1.3/x86_64/binary
mapfile -t REFS < <(
ostree refs --repo="$SOURCE_REPO" 2>/dev/null \
| grep -E ':main/.+/.+/.+/binary$' || true
)
if [ "${#REFS[@]}" -eq 0 ]; then
echo "在 $SOURCE_REPO 中没有找到任何玲珑应用(binary 层)。"
exit 1
fi
# 把拆解好的字段存到数组里,方便后面显示和选择
declare -a CHANNELS APPIDS VERS ARCHS
for ref in "${REFS[@]}"; do
# ref 形如: stable:main/org.deepin.base/25.2.1.3/x86_64/binary
# 取出渠道:stable / gg / local 等,即冒号前面的部分
channel="${ref%%:*}"
# 去掉前面的 "xxx:main/" 部分
rest="${ref#*:main/}" # org.deepin.base/25.2.1.3/x86_64/binary
# 用 / 分割
appid="${rest%%/*}" # org.deepin.base
rest="${rest#*/}" # 25.2.1.3/x86_64/binary
ver="${rest%%/*}" # 25.2.1.3
rest="${rest#*/}" # x86_64/binary
arch="${rest%%/*}" # x86_64
# 最后一个字段是 binary,这里用不到
CHANNELS+=("$channel")
APPIDS+=("$appid")
VERS+=("$ver")
ARCHS+=("$arch")
done
########################################
# 交互式选择要导出的应用
########################################
echo
echo "==> 请选择要导出的玲珑应用:"
echo
# 列出所有应用,让用户选序号
for i in "${!REFS[@]}"; do
idx=$((i + 1))
printf "%2d) [%-6s] %-40s %-15s %-8s\n" \
"$idx" \
"${CHANNELS[$i]}" \
"${APPIDS[$i]}" \
"${VERS[$i]}" \
"${ARCHS[$i]}"
done
echo
read -r -p "请输入要导出的序号(1-${#REFS[@]},q 退出): " choice
if [[ "$choice" == "q" || "$choice" == "Q" ]]; then
echo "已取消。"
exit 0
fi
if ! [[ "$choice" =~ ^[0-9]+$ ]] || [ "$choice" -lt 1 ] || [ "$choice" -gt "${#REFS[@]}" ]; then
echo "输入无效。"
exit 1
fi
index=$((choice - 1))
APPID="${APPIDS[$index]}"
VER="${VERS[$index]}"
ARCH="${ARCHS[$index]}"
CHANNEL="${CHANNELS[$index]}"
REF="${REFS[$index]}"
echo
echo "==> 你选择的是:"
echo " APPID : $APPID"
echo " 版本 : $VER"
echo " 架构 : $ARCH"
echo " 渠道 : $CHANNEL"
echo " OSTree ref: $REF"
echo
########################################
# 设定工作目录
########################################
WORKDIR="$OUTPUT_ROOT/${APPID}-${VER}"
mkdir -p "$WORKDIR"
cd "$WORKDIR"
echo "==> 工作目录:$WORKDIR"
########################################
# 第一步:从源仓库 checkout rootfs
########################################
echo
echo "==> [1/5] checkout rootfs 到 binary/ 目录"
# 目标目录如果已经存在,为避免旧文件干扰,先删掉
rm -rf "$WORKDIR/binary"
# 从源仓库 checkout
ostree --repo="$SOURCE_REPO" checkout "$REF" binary
echo " 已从 $SOURCE_REPO checkout 到 $WORKDIR/binary"
########################################
# 第二步:清理 builder 缓存仓库中的旧 local: 引用(如果仓库存在)
########################################
echo
echo "==> [2/5] 清理 linglong-builder 缓存仓库中的旧 local: 引用(若存在)"
if [ -d "$BUILDER_REPO" ]; then
echo " 检测到缓存仓库存在:$BUILDER_REPO"
# 比较保守地删除本 APPID/VER/ARCH 的 local: binary 引用
local_ref="local:main/${APPID}/${VER}/${ARCH}/binary"
echo " 尝试删除旧引用:$local_ref"
ostree --repo="$BUILDER_REPO" refs --delete "$local_ref" 2>/dev/null || true
# 如果未来有 develop 层,可以在这里加一行类似:
# local_ref_develop="local:main/${APPID}/${VER}/${ARCH}/develop"
# ostree --repo="$BUILDER_REPO" refs --delete "$local_ref_develop" 2>/dev/null || true
echo " 执行 prune --refs-only(仅清理不再被任何 ref 引用的对象)"
ostree prune --repo="$BUILDER_REPO" --refs-only >/dev/null 2>&1
else
echo " 当前还没有缓存仓库,稍后 ll-builder import-dir 会自动创建 $BUILDER_REPO"
fi
########################################
# 第三步:按你的思路 import-dir
########################################
echo
echo "==> [3/5] 使用 ll-builder import-dir 导入 binary 目录"
# 这里不用指定 --repo,ll-builder 会自动使用 $HOME/.cache/linglong-builder/repo
ll-builder import-dir "$WORKDIR/binary"
echo " import-dir 完成。"
########################################
# 第四步:自动在 binary 中查找 linglong.yaml
########################################
echo
echo "==> [4/5] 在 binary/ 下查找 linglong.yaml"
# 有些包会把 linglong.yaml 放在 rootfs 某个子目录中,这里用 find 搜索
LINGLONG_YAML=$(find "$WORKDIR/binary" -maxdepth 6 -name linglong.yaml | head -n 1 || true)
if [ -z "$LINGLONG_YAML" ]; then
echo " 未找到 linglong.yaml。"
read -r -p " 请手动输入 linglong.yaml 路径(绝对或相对,留空则退出): " manual_yaml
if [ -z "$manual_yaml" ]; then
echo " 未指定 linglong.yaml,退出。"
exit 1
fi
LINGLONG_YAML="$manual_yaml"
fi
if [ ! -f "$LINGLONG_YAML" ]; then
echo " 指定的 linglong.yaml 不存在:$LINGLONG_YAML"
exit 1
fi
echo " 使用的 linglong.yaml: $LINGLONG_YAML"
########################################
# 第五步:export --layer -f linglong.yaml
########################################
echo
echo "==> [5/5] 使用 ll-builder export --layer 导出 layer"
# 在 WORKDIR 中执行导出,生成的 .layer 文件也会在这里
ll-builder export --layer -f "$LINGLONG_YAML"
echo
echo "==> 导出完成,生成的 .layer 文件如下:"
ls -lh "$WORKDIR"/*.layer 2>/dev/null || echo "(暂时没看到 *.layer,请检查上面的 export 日志)"
echo
echo "全部完成。"
本文链接:http://odata.cc/archives/21/