玲珑应用导出

任何机器可用 
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 "全部完成。"
无标签
打赏
评论区
头像