mirror of https://github.com/linuxdeepin/linglong
Compare commits
6 Commits
ee862e5d33
...
f3ce211187
| Author | SHA1 | Date |
|---|---|---|
|
|
f3ce211187 | |
|
|
e1a6509752 | |
|
|
bd80b77f50 | |
|
|
c77b8263c0 | |
|
|
c094c2ab21 | |
|
|
f0cb94dbc8 |
|
|
@ -183,7 +183,6 @@ For assistance, use the following channels:
|
|||
- [Init](./docs/pages/en/guide/reference/commands/ll-pica/ll-pica-init.md)
|
||||
- [Convert](./docs/pages/en/guide/reference/commands/ll-pica/ll-pica-convert.md)
|
||||
- [Adep](./docs/pages/en/guide/reference/commands/ll-pica/ll-pica-adep.md)
|
||||
- [Install](./docs/pages/en/guide/reference/commands/ll-pica/install.md)
|
||||
|
||||
#### AppImage Conversion
|
||||
|
||||
|
|
|
|||
|
|
@ -185,7 +185,6 @@ ll-cli run cn.org.linyaps.demo
|
|||
- [初始化](./docs/pages/guide/reference/commands/ll-pica/ll-pica-init.md)
|
||||
- [转换](./docs/pages/guide/reference/commands/ll-pica/ll-pica-convert.md)
|
||||
- [依赖](./docs/pages/guide/reference/commands/ll-pica/ll-pica-adep.md)
|
||||
- [安装](./docs/pages/guide/reference/commands/ll-pica/install.md)
|
||||
|
||||
#### AppImage 包转换
|
||||
|
||||
|
|
|
|||
|
|
@ -740,7 +740,7 @@ int main(int argc, char **argv)
|
|||
Q_INIT_RESOURCE(builder_releases);
|
||||
// 初始化应用,builder在非tty环境也输出日志
|
||||
linglong::utils::global::applicationInitialize(true);
|
||||
linglong::utils::global::initLinyapsLogSystem(argv[0]);
|
||||
linglong::utils::global::initLinyapsLogSystem(linglong::utils::log::LogBackend::Console);
|
||||
|
||||
CLI::App commandParser{ _("linyaps builder CLI \n"
|
||||
"A CLI program to build linyaps application\n") };
|
||||
|
|
|
|||
|
|
@ -930,7 +930,7 @@ int main(int argc, char **argv)
|
|||
QCoreApplication app(argc, argv);
|
||||
// application initialize
|
||||
applicationInitialize();
|
||||
initLinyapsLogSystem(argv[0]);
|
||||
initLinyapsLogSystem(linglong::utils::log::LogBackend::Journal);
|
||||
|
||||
// invoke method
|
||||
auto ret = QMetaObject::invokeMethod(
|
||||
|
|
|
|||
|
|
@ -151,7 +151,7 @@ auto main(int argc, char *argv[]) -> int
|
|||
QCoreApplication app(argc, argv);
|
||||
|
||||
applicationInitialize();
|
||||
initLinyapsLogSystem(argv[0]);
|
||||
initLinyapsLogSystem(linglong::utils::log::LogBackend::Journal);
|
||||
|
||||
auto ociRuntimeCLI = qgetenv("LINGLONG_OCI_RUNTIME");
|
||||
if (ociRuntimeCLI.isEmpty()) {
|
||||
|
|
|
|||
|
|
@ -174,11 +174,11 @@ docs/pages/en/guide/
|
|||
|
||||
### Command Reference
|
||||
|
||||
- Build Tool: [`reference/commands/ll-builder/`](reference/commands/ll-builder/) directory
|
||||
- CLI Tool: [`reference/commands/ll-cli/`](reference/commands/ll-cli/) directory
|
||||
- Deb Package Conversion Tool: [`reference/commands/ll-pica/`](reference/commands/ll-pica/) directory
|
||||
- AppImage Conversion Tool: [`reference/commands/ll-appimage-convert/`](reference/commands/ll-appimage-convert/) directory
|
||||
- Flatpak Conversion Tool: [`reference/commands/ll-pica-flatpak/`](reference/commands/ll-pica-flatpak/) directory
|
||||
- Build Tool: [`reference/commands/ll-builder/`](reference/commands/ll-builder/ll-builder.md) directory
|
||||
- CLI Tool: [`reference/commands/ll-cli/`](reference/commands/ll-cli/ll-cli.md) directory
|
||||
- Deb Package Conversion Tool: [`reference/commands/ll-pica/`](reference/commands/ll-pica/ll-pica.md) directory
|
||||
- AppImage Conversion Tool: [`reference/commands/ll-appimage-convert/`](reference/commands/ll-appimage-convert/ll-appimage-convert.md) directory
|
||||
- Flatpak Conversion Tool: [`reference/commands/ll-pica-flatpak/`](reference/commands/ll-pica-flatpak/ll-pica-flatpak.md) directory
|
||||
|
||||
### Common Issues
|
||||
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ ll-cli is a package manager frontend for managing Linyaps application installati
|
|||
| --------- | ------------------------------------- | --------------------------------------------------------------------------------------- |
|
||||
| run | [ll-cli-run(1)](./run.md) | Run applications |
|
||||
| ps | [ll-cli-ps(1)](./ps.md) | List running applications |
|
||||
| enter | [ll-cli-exec(1)](./exec.md) | Enter the namespace of a running application |
|
||||
| enter | [ll-cli-exec(1)](./enter.md) | Enter the namespace of a running application |
|
||||
| kill | [ll-cli-kill(1)](./kill.md) | Stop running applications |
|
||||
| prune | [ll-cli-prune(1)](./prune.md) | Remove unused base systems or runtimes |
|
||||
| install | [ll-cli-install(1)](./install.md) | Install applications or runtimes |
|
||||
|
|
@ -52,7 +52,7 @@ ll-cli is a package manager frontend for managing Linyaps application installati
|
|||
|
||||
## SEE ALSO
|
||||
|
||||
**[ll-cli-run(1)](./run.md)**, **[ll-cli-ps(1)](./ps.md)**, **[ll-cli-exec(1)](./exec.md)**, **[ll-cli-kill(1)](./kill.md)**, **[ll-cli-prune(1)](./prune.md)**, **[ll-cli-install(1)](./install.md)**, **[ll-cli-uninstall(1)](./uninstall.md)**, **[ll-cli-upgrade(1)](./upgrade.md)**, **[ll-cli-list(1)](./list.md)**, **[ll-cli-info(1)](./info.md)**, **[ll-cli-content(1)](./content.md)**, **[ll-cli-search(1)](./search.md)**, **[ll-cli-repo(1)](./repo.md)**
|
||||
**[ll-cli-run(1)](./run.md)**, **[ll-cli-ps(1)](./ps.md)**, **[ll-cli-exec(1)](./enter.md)**, **[ll-cli-kill(1)](./kill.md)**, **[ll-cli-prune(1)](./prune.md)**, **[ll-cli-install(1)](./install.md)**, **[ll-cli-uninstall(1)](./uninstall.md)**, **[ll-cli-upgrade(1)](./upgrade.md)**, **[ll-cli-list(1)](./list.md)**, **[ll-cli-info(1)](./info.md)**, **[ll-cli-content(1)](./content.md)**, **[ll-cli-search(1)](./search.md)**, **[ll-cli-repo(1)](./repo.md)**
|
||||
|
||||
## HISTORY
|
||||
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ ll-cli run org.deepin.demo -- bash -x /path/to/bash/script
|
|||
|
||||
## SEE ALSO
|
||||
|
||||
**[ll-cli(1)](./ll-cli.md)**, **[ll-cli-ps(1)](./ps.md)**, **[ll-cli-exec(1)](./exec.md)**
|
||||
**[ll-cli(1)](./ll-cli.md)**, **[ll-cli-ps(1)](./ps.md)**, **[ll-cli-exec(1)](./enter.md)**
|
||||
|
||||
## HISTORY
|
||||
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ ll-pica init -a "arm64"
|
|||
|
||||
## NOTES
|
||||
|
||||
After the package.yaml file is generated, users can modify it as needed. For detailed fields, refer to: [Conversion Configuration File Introduction](./manifests.md).
|
||||
After the package.yaml file is generated, users can modify it as needed. For detailed fields, refer to: [Conversion Configuration File Introduction](./ll-pica-manifests.md).
|
||||
|
||||
## SEE ALSO
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
# Conversion Configuration File Introduction
|
||||
|
||||
`package.yaml` is the basic information file for `ll-pica` to convert deb packages. It contains information such as the base and runtime versions to be built, and the deb packages that need to be converted.
|
||||
|
||||
## Project Directory Structure
|
||||
|
||||
```bash
|
||||
{workdir}
|
||||
├── package
|
||||
│ └── {appid}
|
||||
│ ├── linglong
|
||||
│ ├── linglong.yaml
|
||||
│ └── start.sh
|
||||
└── package.yaml
|
||||
```
|
||||
|
||||
## Field Definitions
|
||||
|
||||
### Build Environment
|
||||
|
||||
The build environment for converting deb packages to Linglong packages.
|
||||
|
||||
```bash
|
||||
runtime:
|
||||
version: 23.0.1
|
||||
base_version: 23.0.0
|
||||
source: https://community-packages.deepin.com/beige/
|
||||
distro_version: beige
|
||||
arch: amd64
|
||||
```
|
||||
|
||||
| Name | Description |
|
||||
| -------------- | ------------------------------------------------------------------------- |
|
||||
| runtime | Runtime environment |
|
||||
| version | Runtime version, three-digit version can fuzzy match the fourth digit |
|
||||
| base_version | Base version number, three-digit version can fuzzy match the fourth digit |
|
||||
| source | Source used when obtaining deb package dependencies |
|
||||
| distro_version | Distribution code name |
|
||||
| arch | Architecture required for obtaining deb packages |
|
||||
|
||||
### Deb Package Information
|
||||
|
||||
```bash
|
||||
file:
|
||||
deb:
|
||||
- type: local
|
||||
id: com.baidu.baidunetdisk
|
||||
name: com.baidu.baidunetdisk
|
||||
ref: /tmp/com.baidu.baidunetdisk_4.17.7_amd64.deb
|
||||
```
|
||||
|
||||
| Name | Description |
|
||||
| ---- | ---------------------------------------------------------------------------------------- |
|
||||
| type | Acquisition method, local requires specifying ref, repo does not require specifying ref. |
|
||||
| id | Unique name of the build product |
|
||||
| name | Specify the correct package name that apt can search for |
|
||||
| ref | Path of the deb package on the host machine |
|
||||
|
|
@ -85,7 +85,7 @@ build: |
|
|||
|
||||
The linglong.yaml file follows YAML syntax specifications.
|
||||
|
||||
For detailed explanation of fields in linglong.yaml, refer to: [Build Configuration File Introduction](../ll-builder/manifests.md)
|
||||
For detailed explanation of fields in linglong.yaml, refer to: [Build Configuration File Introduction](../building/manifests.md)
|
||||
|
||||
## Build
|
||||
|
||||
|
|
|
|||
|
|
@ -18,15 +18,15 @@ linyaps is composed of three parts:
|
|||
|
||||
Automatically built based on the latest tag
|
||||
|
||||
1. Repository address <https://ci.deepin.com/repo/obs/linglong:/CI:/release>
|
||||
2. Build address <https://build.deepin.com/project/show/linglong:CI:release>
|
||||
1. Repository address <https://ci.deepin.com/repo/obs/linglong:/CI:/release>
|
||||
2. Build address <https://build.deepin.com/project/show/linglong:CI:release>
|
||||
|
||||
### latest repository
|
||||
|
||||
Automatically built based on the latest commit
|
||||
|
||||
1. Repository address <https://ci.deepin.com/repo/obs/linglong:/CI:/latest>
|
||||
2. Build address <https://build.deepin.com/project/show/linglong:CI:latest>
|
||||
1. Repository address <https://ci.deepin.com/repo/obs/linglong:/CI:/latest>
|
||||
2. Build address <https://build.deepin.com/project/show/linglong:CI:latest>
|
||||
|
||||
:::tip
|
||||
|
||||
|
|
@ -162,3 +162,27 @@ sudo apt install linglong-builder
|
|||
```bash
|
||||
sudo dnf install linglong-builder
|
||||
```
|
||||
|
||||
## Linyaps Conversion Tool Installation Instructions
|
||||
|
||||
### Deepin 23/25
|
||||
|
||||
```bash
|
||||
sudo apt install linglong-pica
|
||||
|
||||
```
|
||||
|
||||
### Arch Linux
|
||||
|
||||
Install via [AUR repository](https://aur.archlinux.org/packages/linglong-pica) or [self-hosted repository](https://github.com/taotieren/aur-repo).
|
||||
|
||||
```bash
|
||||
|
||||
# AUR
|
||||
yay -Syu linglong-pica
|
||||
|
||||
# or self-hosted repository
|
||||
|
||||
sudo pacman -Syu linglong-pica
|
||||
|
||||
```
|
||||
|
|
|
|||
|
|
@ -1,28 +0,0 @@
|
|||
# Linyaps Conversion Tool (pica) Installation
|
||||
|
||||
This tool provides the ability to convert deb, appimage, and flatpak packages to Linyaps packages, generating the linglong.yaml file needed to build Linyaps applications, and relies on ll-builder to implement application building and export.
|
||||
|
||||
## deepin v23
|
||||
|
||||
```bash
|
||||
sudo apt install linglong-pica
|
||||
```
|
||||
|
||||
## UOS 1070
|
||||
|
||||
```bash
|
||||
echo "deb [trusted=yes] https://ci.deepin.com/repo/deepin/deepin-community/linglong-repo/ unstable main" | sudo tee -a /etc/apt/sources.list
|
||||
sudo apt update
|
||||
sudo apt install linglong-pica
|
||||
```
|
||||
|
||||
## Arch Linux
|
||||
|
||||
Install through [AUR repository](https://aur.archlinux.org/packages/linglong-pica) or [self-built repository](https://github.com/taotieren/aur-repo).
|
||||
|
||||
```bash
|
||||
# AUR
|
||||
yay -Syu linglong-pica
|
||||
# Or self-built repository
|
||||
sudo pacman -Syu linglong-pica
|
||||
```
|
||||
|
|
@ -172,11 +172,11 @@ docs/pages/guide/
|
|||
|
||||
### 命令参考
|
||||
|
||||
- 构建工具: [`reference/commands/ll-builder/`](reference/commands/ll-builder/) 目录
|
||||
- 命令行工具: [`reference/commands/ll-cli/`](reference/commands/ll-cli/) 目录
|
||||
- deb包转换工具: [`reference/commands/ll-pica/`](reference/commands/ll-pica/) 目录
|
||||
- AppImage转换工具: [`reference/commands/ll-appimage-convert/`](reference/commands/ll-appimage-convert/) 目录
|
||||
- Flatpak转换工具: [`reference/commands/ll-pica-flatpak/`](reference/commands/ll-pica-flatpak/) 目录
|
||||
- 构建工具: [`reference/commands/ll-builder/`](reference/commands/ll-builder/ll-builder.md) 目录
|
||||
- 命令行工具: [`reference/commands/ll-cli/`](reference/commands/ll-cli/ll-cli.md) 目录
|
||||
- deb包转换工具: [`reference/commands/ll-pica/`](reference/commands/ll-pica/ll-pica.md) 目录
|
||||
- AppImage转换工具: [`reference/commands/ll-appimage-convert/`](reference/commands/ll-appimage-convert/ll-appimage-convert.md) 目录
|
||||
- Flatpak转换工具: [`reference/commands/ll-pica-flatpak/`](reference/commands/ll-pica-flatpak/ll-pica-flatpak.md) 目录
|
||||
|
||||
### 常见问题
|
||||
|
||||
|
|
|
|||
|
|
@ -1,88 +0,0 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd.
|
||||
|
||||
SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
-->
|
||||
|
||||
# 转换AppImage应用
|
||||
|
||||
转换 `AppImage` 包格式( `.AppImage` 或 `.AppImage` ) 到如意玲珑包格式( `.layer` 或 `.uab` )
|
||||
|
||||
查看 `ll-appimage-convert convert` 命令的帮助信息:
|
||||
|
||||
```bash
|
||||
ll-appimage-convert convert --help
|
||||
```
|
||||
|
||||
`ll-appimage-convert convert` 命令的帮助信息如下:
|
||||
|
||||
```text
|
||||
Usage:
|
||||
ll-appimage-convert convert [flags]
|
||||
Flags:
|
||||
-b, --build build linglong
|
||||
-d, --description string detailed description of the package
|
||||
-f, --file string app package file, it is not required option,
|
||||
you can ignore this option
|
||||
when you set --url option and --hash option
|
||||
--hash string pkg hash value, it must be used with --url option
|
||||
-h, --help help for convert
|
||||
-i, --id string the unique name of the package
|
||||
-l, --layer export layer file
|
||||
-n, --name string the description the package
|
||||
-u, --url string pkg url, it is not required option, you can ignore this option when you set -f option
|
||||
-v, --version string the version of the package
|
||||
Global Flags:
|
||||
-V, --verbose verbose output
|
||||
```
|
||||
|
||||
`ll-appimage-convert convert` 命令会根据指定的应用名称( `--name` 选项)生成一个目录,该目录会作为如意玲珑项目的根目录,即 `linglong.yaml` 文件所在的位置。它支持两种转换方法:
|
||||
|
||||
1. 你可以使用 `--file` 选项将指定的 `AppImage` 文件转换为如意玲珑包文件;
|
||||
2. 你可以使用 `--url` 和 `--hash` 选项将指定的 `AppImage url` 和 `hash` 值转换为如意玲珑包文件;
|
||||
3. 你可以使用 `--layer` 选项导出 `.layer` 格式文件,否则将默认导出 `.uab` 格式文件。
|
||||
|
||||
`Tips: 在如意玲珑版本大于1.5.7时,convert 默认导出 uab 包,如果想要导出 layer 文件,需要加上 --layer 参数`
|
||||
|
||||
你可以使用 `--output` 选项生成如意玲珑项目的配置文件 (`linglong.yaml`) 和构建如意玲珑 `.layer` (`.uab`) 的脚本文件
|
||||
然后你可以执行该脚本去生成对应的如意玲珑包当你修改 `linglong.yaml` 配置文件后。如果不指定该选项,将直接导出对应的如意玲珑包。
|
||||
|
||||
以通过 `--url` 选项将 [BrainWaves](https://github.com/makebrainwaves/BrainWaves/releases/download/v0.15.1/BrainWaves-0.15.1.AppImage) `AppImage` 文件转换为如意玲珑 `.layer` 文件为例,主要步骤如下:
|
||||
|
||||
指定要转换的如意玲珑包的相关参数,稍等片刻后你就可以得到 `io.github.brainwaves_0.15.1.0_x86_64_runtime.layer` 或者 `io.github.brainwaves_0.15.1.0_x86_64_runtime.uab` 包文件。
|
||||
|
||||
```bash
|
||||
ll-appimage-convert convert --url "https://github.com/makebrainwaves/BrainWaves/releases/download/v0.15.1/BrainWaves-0.15.1.AppImage" --hash "04fcfb9ccf5c0437cd3007922fdd7cd1d0a73883fd28e364b79661dbd25a4093" --name "io.github.brainwaves" --id "io.github.brainwaves" --version "0.15.1.0" --description "io.github.brainwaves" -b
|
||||
```
|
||||
|
||||
以通过 `--file` 选项将 `BrainWaves-0.15.1.AppImage` 转换为如意玲珑 `.uab` 包为例,主要步骤如下:
|
||||
|
||||
```bash
|
||||
ll-appimage-convert convert -f ~/Downloads/BrainWaves-0.15.1.AppImage --name "io.github.brainwaves" --id "io.github.brainwaves" --version "0.15.1.0" --description "io.github.brainwaves" -b
|
||||
```
|
||||
|
||||
转换完成的目录结构如下:
|
||||
|
||||
```text
|
||||
├── io.github.brainwaves_x86_64_0.15.1.0_main.uab
|
||||
├── linglong
|
||||
└── linglong.yaml
|
||||
```
|
||||
|
||||
以通过 `--file` 选项将 `BrainWaves-0.15.1.AppImage` 转换为如意玲珑 `.layer` 包为例,主要步骤如下:
|
||||
|
||||
```bash
|
||||
ll-appimage-convert convert -f ~/Downloads/BrainWaves-0.15.1.AppImage --name "io.github.brainwaves" --id "io.github.brainwaves" --version "0.15.1.0" --description "io.github.brainwaves" -b --layer
|
||||
```
|
||||
|
||||
转换完成的目录结构如下:
|
||||
|
||||
```text
|
||||
├── io.github.brainwaves_0.15.1.0_x86_64_binary.layer
|
||||
├── io.github.brainwaves_0.15.1.0_x86_64_develop.layer
|
||||
├── linglong
|
||||
└── linglong.yaml
|
||||
```
|
||||
|
||||
`.uab` 或 `.layer` 文件验证:
|
||||
导出的 `.uab` 或者 `.layer` 需要安装后进行验证,安装 layer 文件和运行应用参考:[安装应用](../ll-cli/install.md)
|
||||
|
|
@ -38,7 +38,7 @@ ll-cli 是一个包管理器前端,用于管理如意玲珑应用的安装、
|
|||
| --------- | ------------------------------------- | ----------------------------------------------- |
|
||||
| run | [ll-cli-run(1)](./run.md) | 运行应用程序 |
|
||||
| ps | [ll-cli-ps(1)](./ps.md) | 列出正在运行的应用程序 |
|
||||
| enter | [ll-cli-exec(1)](./exec.md) | 进入应用程序正在运行的命名空间 |
|
||||
| enter | [ll-cli-exec(1)](./enter.md) | 进入应用程序正在运行的命名空间 |
|
||||
| kill | [ll-cli-kill(1)](./kill.md) | 停止运行的应用程序 |
|
||||
| prune | [ll-cli-prune(1)](./prune.md) | 移除未使用的最小系统或运行时 |
|
||||
| install | [ll-cli-install(1)](./install.md) | 安装应用程序或运行时 |
|
||||
|
|
@ -52,7 +52,7 @@ ll-cli 是一个包管理器前端,用于管理如意玲珑应用的安装、
|
|||
|
||||
## SEE ALSO
|
||||
|
||||
**[ll-cli-run(1)](./run.md)**, **[ll-cli-ps(1)](./ps.md)**, **[ll-cli-exec(1)](./exec.md)**, **[ll-cli-kill(1)](./kill.md)**, **[ll-cli-prune(1)](./prune.md)**, **[ll-cli-install(1)](./install.md)**, **[ll-cli-uninstall(1)](./uninstall.md)**, **[ll-cli-upgrade(1)](./upgrade.md)**, **[ll-cli-list(1)](./list.md)**, **[ll-cli-info(1)](./info.md)**, **[ll-cli-content(1)](./content.md)**, **[ll-cli-search(1)](./search.md)**, **[ll-cli-repo(1)](./repo.md)**
|
||||
**[ll-cli-run(1)](./run.md)**, **[ll-cli-ps(1)](./ps.md)**, **[ll-cli-exec(1)](./enter.md)**, **[ll-cli-kill(1)](./kill.md)**, **[ll-cli-prune(1)](./prune.md)**, **[ll-cli-install(1)](./install.md)**, **[ll-cli-uninstall(1)](./uninstall.md)**, **[ll-cli-upgrade(1)](./upgrade.md)**, **[ll-cli-list(1)](./list.md)**, **[ll-cli-info(1)](./info.md)**, **[ll-cli-content(1)](./content.md)**, **[ll-cli-search(1)](./search.md)**, **[ll-cli-repo(1)](./repo.md)**
|
||||
|
||||
## HISTORY
|
||||
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ ll-cli run org.deepin.demo -- bash -x /path/to/bash/script
|
|||
|
||||
## SEE ALSO
|
||||
|
||||
**[ll-cli(1)](./ll-cli.md)**, **[ll-cli-ps(1)](./ps.md)**, **[ll-cli-exec(1)](./exec.md)**
|
||||
**[ll-cli(1)](./ll-cli.md)**, **[ll-cli-ps(1)](./ps.md)**, **[ll-cli-exec(1)](./enter.md)**
|
||||
|
||||
## HISTORY
|
||||
|
||||
|
|
|
|||
|
|
@ -1,28 +0,0 @@
|
|||
# 玲珑转换工具(pica)安装
|
||||
|
||||
本工具提供 deb、appimage、flatpak包转换为如意玲珑包的能力,生成构建如意玲珑应用需要的 linglong.yaml 文件,并依赖 ll-builder 来实现应用构建和导出。
|
||||
|
||||
## deepin v23
|
||||
|
||||
```bash
|
||||
sudo apt install linglong-pica
|
||||
```
|
||||
|
||||
## UOS 1070
|
||||
|
||||
```bash
|
||||
echo "deb [trusted=yes] https://ci.deepin.com/repo/deepin/deepin-community/linglong-repo/ unstable main" | sudo tee -a /etc/apt/sources.list
|
||||
sudo apt update
|
||||
sudo apt install linglong-pica
|
||||
```
|
||||
|
||||
## Arch Linux
|
||||
|
||||
通过 [AUR 仓库](https://aur.archlinux.org/packages/linglong-pica) 或 [自建源仓库](https://github.com/taotieren/aur-repo) 安装。
|
||||
|
||||
```bash
|
||||
# AUR
|
||||
yay -Syu linglong-pica
|
||||
# 或自建源
|
||||
sudo pacman -Syu linglong-pica
|
||||
```
|
||||
|
|
@ -66,7 +66,7 @@ ll-pica init -a "arm64"
|
|||
|
||||
## NOTES
|
||||
|
||||
package.yaml 文件生成后,用户可以根据需要进行修改。详细字段参考:[转换配置文件简介](./manifests.md)。
|
||||
package.yaml 文件生成后,用户可以根据需要进行修改。详细字段参考:[转换配置文件简介](./ll-pica-manifests.md)。
|
||||
|
||||
## SEE ALSO
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
# 转换配置文件简介
|
||||
|
||||
package.yaml 是 `ll-pica` 转换 deb 包的基础信息。如构建的 base、runtime 的版本,需要被转换的 deb 包。
|
||||
|
||||
## 工程目录结构
|
||||
|
||||
```bash
|
||||
{workdir}
|
||||
├── package
|
||||
│ └── {appid}
|
||||
│ ├── linglong
|
||||
│ ├── linglong.yaml
|
||||
│ └── start.sh
|
||||
└── package.yaml
|
||||
```
|
||||
|
||||
## 字段定义
|
||||
|
||||
### 构建环境
|
||||
|
||||
deb 包转玲珑包的构建环境。
|
||||
|
||||
```bash
|
||||
runtime:
|
||||
version: 23.0.1
|
||||
base_version: 23.0.0
|
||||
source: https://community-packages.deepin.com/beige/
|
||||
distro_version: beige
|
||||
arch: amd64
|
||||
```
|
||||
|
||||
| 名称 | 描述 |
|
||||
| -------------- | ----------------------------------------------- |
|
||||
| runtime | 运行时(runtime) |
|
||||
| version | 运行时(runtime)版本,三位数可以模糊匹配第四位 |
|
||||
| base_version | base 的版本号, 三位数可以模糊匹配第四位 |
|
||||
| source | 获取 deb 包依赖时使用的源 |
|
||||
| distro_version | 发行版的代号 |
|
||||
| arch | 获取 deb 包需要的架构 |
|
||||
|
||||
### deb 包信息
|
||||
|
||||
```bash
|
||||
file:
|
||||
deb:
|
||||
- type: local
|
||||
id: com.baidu.baidunetdisk
|
||||
name: com.baidu.baidunetdisk
|
||||
ref: /tmp/com.baidu.baidunetdisk_4.17.7_amd64.deb
|
||||
```
|
||||
|
||||
| 名称 | 描述 |
|
||||
| ---- | ----------------------------------------------------- |
|
||||
| type | 获取的方式,local 需要指定 ref,repo 不需要指定 ref。 |
|
||||
| id | 构建产物的唯一名称 |
|
||||
| name | 指定 apt 能搜索到的正确包名 |
|
||||
| ref | deb 包在宿主机的路径 |
|
||||
|
|
@ -85,7 +85,7 @@ build: |
|
|||
|
||||
linglong.yaml 文件遵循 yaml 语法规范。
|
||||
|
||||
linglong.yaml 中字段的详细解释参考:[构建配置文件简介](../ll-builder/manifests.md)
|
||||
linglong.yaml 中字段的详细解释参考:[构建配置文件简介](../building/manifests.md)
|
||||
|
||||
## 构建
|
||||
|
||||
|
|
|
|||
|
|
@ -18,15 +18,15 @@ SPDX-License-Identifier: LGPL-3.0-or-later
|
|||
|
||||
基于最新tag自动构建
|
||||
|
||||
1. 仓库地址 <https://ci.deepin.com/repo/obs/linglong:/CI:/release>
|
||||
2. 构建地址 <https://build.deepin.com/project/show/linglong:CI:release>
|
||||
1. 仓库地址 <https://ci.deepin.com/repo/obs/linglong:/CI:/release>
|
||||
2. 构建地址 <https://build.deepin.com/project/show/linglong:CI:release>
|
||||
|
||||
### latest 仓库
|
||||
|
||||
基于最新提交自动构建
|
||||
|
||||
1. 仓库地址 <https://ci.deepin.com/repo/obs/linglong:/CI:/latest>
|
||||
2. 构建地址 <https://build.deepin.com/project/show/linglong:CI:latest>
|
||||
1. 仓库地址 <https://ci.deepin.com/repo/obs/linglong:/CI:/latest>
|
||||
2. 构建地址 <https://build.deepin.com/project/show/linglong:CI:latest>
|
||||
|
||||
:::tip
|
||||
|
||||
|
|
@ -162,3 +162,22 @@ sudo apt install linglong-builder
|
|||
```bash
|
||||
sudo dnf install linglong-builder
|
||||
```
|
||||
|
||||
## 如意玲珑转换工具安装说明
|
||||
|
||||
### Deepin 23/25
|
||||
|
||||
```bash
|
||||
sudo apt install linglong-pica
|
||||
```
|
||||
|
||||
### Arch Linux
|
||||
|
||||
通过 [AUR 仓库](https://aur.archlinux.org/packages/linglong-pica) 或 [自建源仓库](https://github.com/taotieren/aur-repo) 安装。
|
||||
|
||||
```bash
|
||||
# AUR
|
||||
yay -Syu linglong-pica
|
||||
# 或自建源
|
||||
sudo pacman -Syu linglong-pica
|
||||
```
|
||||
|
|
|
|||
|
|
@ -49,10 +49,14 @@ pfl_add_library(
|
|||
src/linglong/package/layer_file.h
|
||||
src/linglong/package/layer_packager.cpp
|
||||
src/linglong/package/layer_packager.h
|
||||
src/linglong/package_manager/action.cpp
|
||||
src/linglong/package_manager/action.h
|
||||
src/linglong/package_manager/package_manager.cpp
|
||||
src/linglong/package_manager/package_manager.h
|
||||
src/linglong/package_manager/package_task.cpp
|
||||
src/linglong/package_manager/package_task.h
|
||||
src/linglong/package_manager/ref_installation.cpp
|
||||
src/linglong/package_manager/ref_installation.h
|
||||
src/linglong/package_manager/uab_installation.cpp
|
||||
src/linglong/package_manager/uab_installation.h
|
||||
src/linglong/package/reference.cpp
|
||||
|
|
@ -76,6 +80,8 @@ pfl_add_library(
|
|||
src/linglong/repo/migrate.h
|
||||
src/linglong/repo/ostree_repo.cpp
|
||||
src/linglong/repo/ostree_repo.h
|
||||
src/linglong/repo/remote_packages.cpp
|
||||
src/linglong/repo/remote_packages.h
|
||||
src/linglong/repo/repo_cache.cpp
|
||||
src/linglong/repo/repo_cache.h
|
||||
src/linglong/runtime/container_builder.cpp
|
||||
|
|
|
|||
|
|
@ -281,14 +281,8 @@ utils::error::Result<void> UABPackager::packIcon() noexcept
|
|||
{
|
||||
LINGLONG_TRACE("add icon to uab")
|
||||
|
||||
auto iconAchieve = this->uab.parentDir().absoluteFilePath("icon.a");
|
||||
if (auto ret = utils::command::Cmd("ar").exec({ "q", iconAchieve, icon->absoluteFilePath() });
|
||||
!ret) {
|
||||
return LINGLONG_ERR(ret);
|
||||
}
|
||||
|
||||
QByteArray iconSection{ "linglong.icon" };
|
||||
if (auto ret = this->uab.addNewSection(iconSection, QFileInfo{ iconAchieve }); !ret) {
|
||||
if (auto ret = this->uab.addNewSection(iconSection, icon.value()); !ret) {
|
||||
return LINGLONG_ERR(ret);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,84 @@
|
|||
// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
#include "action.h"
|
||||
|
||||
#include "linglong/repo/ostree_repo.h"
|
||||
#include "linglong/repo/repo_cache.h"
|
||||
|
||||
namespace linglong::service {
|
||||
|
||||
utils::error::Result<ActionOperation>
|
||||
Action::getActionOperation(const api::types::v1::PackageInfoV2 &target, bool extraModuleOnly)
|
||||
{
|
||||
LINGLONG_TRACE("get action operation");
|
||||
|
||||
ActionOperation action;
|
||||
auto targetRef = package::Reference::fromPackageInfo(target);
|
||||
if (!targetRef) {
|
||||
return LINGLONG_ERR(targetRef);
|
||||
}
|
||||
|
||||
if (extraModuleOnly) {
|
||||
action.operation = ActionOperation::Install;
|
||||
} else {
|
||||
repo::repoCacheQuery query;
|
||||
query.channel = target.channel;
|
||||
query.id = target.id;
|
||||
query.deleted = false;
|
||||
auto localRefs = repo.listLocalBy(query);
|
||||
if (!localRefs) {
|
||||
return LINGLONG_ERR(localRefs);
|
||||
}
|
||||
|
||||
if (!localRefs->empty()) {
|
||||
// localRefs is sort by version
|
||||
auto oldRef = package::Reference::fromPackageInfo(localRefs->front().info);
|
||||
if (!oldRef) {
|
||||
return LINGLONG_ERR(oldRef);
|
||||
}
|
||||
if (targetRef->version > oldRef->version) {
|
||||
action.operation = ActionOperation::Upgrade;
|
||||
} else if (targetRef->version < oldRef->version) {
|
||||
action.operation = ActionOperation::Downgrade;
|
||||
} else {
|
||||
action.operation = ActionOperation::Overwrite;
|
||||
}
|
||||
|
||||
// see if the same version is installed
|
||||
for (auto it = localRefs->begin() + 1; it != localRefs->end(); ++it) {
|
||||
auto ref = package::Reference::fromPackageInfo(it->info);
|
||||
if (!ref) {
|
||||
return LINGLONG_ERR(ref);
|
||||
}
|
||||
|
||||
if (ref->version == targetRef->version) {
|
||||
action.operation = ActionOperation::Overwrite;
|
||||
oldRef = std::move(ref);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// multiple version can be installed for non-app package
|
||||
if (action.operation != ActionOperation::Overwrite && target.kind != "app") {
|
||||
action.operation = ActionOperation::Install;
|
||||
}
|
||||
|
||||
if (action.operation != ActionOperation::Install) {
|
||||
action.oldRef = std::move(oldRef).value();
|
||||
}
|
||||
} else {
|
||||
action.operation = ActionOperation::Install;
|
||||
}
|
||||
}
|
||||
|
||||
action.kind = target.kind;
|
||||
action.newRef = package::ReferenceWithRepo{
|
||||
.reference = std::move(targetRef).value(),
|
||||
};
|
||||
|
||||
return action;
|
||||
}
|
||||
|
||||
} // namespace linglong::service
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "linglong/api/types/v1/CommonOptions.hpp"
|
||||
#include "linglong/package/reference.h"
|
||||
|
||||
namespace linglong::repo {
|
||||
class OSTreeRepo;
|
||||
}
|
||||
|
||||
namespace linglong::service {
|
||||
|
||||
class PackageManager;
|
||||
class PackageTask;
|
||||
|
||||
struct ActionOperation
|
||||
{
|
||||
enum Policy {
|
||||
Upgrade,
|
||||
Install,
|
||||
Remove,
|
||||
Overwrite,
|
||||
Downgrade,
|
||||
} operation;
|
||||
|
||||
std::string kind;
|
||||
std::optional<package::Reference> oldRef;
|
||||
std::optional<package::ReferenceWithRepo> newRef;
|
||||
};
|
||||
|
||||
class Action
|
||||
{
|
||||
public:
|
||||
Action(PackageManager &pm, repo::OSTreeRepo &repo, api::types::v1::CommonOptions options)
|
||||
: pm(pm)
|
||||
, repo(repo)
|
||||
, options(options)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~Action() = default;
|
||||
|
||||
// prepare runs in DBus calling context, so it should not do long time operations
|
||||
virtual utils::error::Result<void> prepare() = 0;
|
||||
// doAction runs in PM tasks context, it's application's main loop for now
|
||||
virtual utils::error::Result<void> doAction(PackageTask &task) = 0;
|
||||
virtual std::string getTaskName() const = 0;
|
||||
|
||||
protected:
|
||||
utils::error::Result<ActionOperation>
|
||||
getActionOperation(const api::types::v1::PackageInfoV2 &target, bool extraModuleOnly);
|
||||
|
||||
PackageManager ±
|
||||
repo::OSTreeRepo &repo;
|
||||
api::types::v1::CommonOptions options;
|
||||
};
|
||||
|
||||
} // namespace linglong::service
|
||||
|
|
@ -14,12 +14,14 @@
|
|||
#include "linglong/api/types/v1/Repo.hpp"
|
||||
#include "linglong/api/types/v1/State.hpp"
|
||||
#include "linglong/common/dir.h"
|
||||
#include "linglong/common/strings.h"
|
||||
#include "linglong/extension/extension.h"
|
||||
#include "linglong/package/layer_file.h"
|
||||
#include "linglong/package/layer_packager.h"
|
||||
#include "linglong/package/reference.h"
|
||||
#include "linglong/package/uab_file.h"
|
||||
#include "linglong/package_manager/package_task.h"
|
||||
#include "linglong/package_manager/ref_installation.h"
|
||||
#include "linglong/package_manager/uab_installation.h"
|
||||
#include "linglong/repo/config.h"
|
||||
#include "linglong/repo/ostree_repo.h"
|
||||
|
|
@ -785,9 +787,13 @@ QVariantMap PackageManager::installFromUAB(const QDBusUnixFileDescriptor &fd,
|
|||
auto taskRet = tasks.addNewTask(
|
||||
{ action->getTaskName() },
|
||||
[action](PackageTask &task) {
|
||||
LINGLONG_TRACE("uab installation task")
|
||||
|
||||
auto res = action->doAction(task);
|
||||
if (!res) {
|
||||
LogD("uab installation failed: {}", res.error());
|
||||
LogE("uab installation failed: {}", res.error());
|
||||
task.reportError(LINGLONG_ERRV(std::move(res).error().message(),
|
||||
utils::error::ErrorCode::AppInstallFailed));
|
||||
}
|
||||
},
|
||||
connection());
|
||||
|
|
@ -842,213 +848,57 @@ auto PackageManager::Install(const QVariantMap ¶meters) noexcept -> QVariant
|
|||
return toDBusReply(utils::error::ErrorCode::AppInstallFailed, paras.error().message());
|
||||
}
|
||||
|
||||
api::types::v1::PackageManager1Package package;
|
||||
package.id = paras->package.id;
|
||||
package.channel = paras->package.channel;
|
||||
package.version = paras->package.version;
|
||||
|
||||
// 解析用户输入
|
||||
auto fuzzyRef = fuzzyReferenceFromPackage(package);
|
||||
const auto &package = paras->package;
|
||||
auto fuzzyRef =
|
||||
package::FuzzyReference::create(package.channel, package.id, package.version, std::nullopt);
|
||||
if (!fuzzyRef) {
|
||||
return toDBusReply(utils::error::ErrorCode::AppInstallFailed, fuzzyRef.error().message());
|
||||
}
|
||||
|
||||
std::string curModule = "binary";
|
||||
// install binary module by default
|
||||
auto modules = package.modules.value_or(std::vector<std::string>{ "binary" });
|
||||
|
||||
if (paras->package.modules && paras->package.modules->size() == 1) {
|
||||
// Manually install single module
|
||||
curModule = paras->package.modules->front();
|
||||
std::optional<repo::Repo> usedRepo;
|
||||
if (paras->repo) {
|
||||
auto repo = this->repo.getRepoByAlias(*paras->repo);
|
||||
if (!repo) {
|
||||
return toDBusReply(utils::error::ErrorCode::AppInstallFailed, repo.error().message());
|
||||
}
|
||||
usedRepo = std::move(repo).value();
|
||||
}
|
||||
|
||||
auto modules = paras->package.modules.value_or(std::vector<std::string>{ curModule });
|
||||
LogI("install {} {} from {}",
|
||||
fuzzyRef->toString(),
|
||||
common::strings::join(modules),
|
||||
usedRepo ? usedRepo->name : "(all)");
|
||||
|
||||
// 安装module
|
||||
if (curModule != "binary") {
|
||||
// 安装module必须是和binary相同的版本,所以不允许指定
|
||||
if (fuzzyRef->version) {
|
||||
return toDBusReply(utils::error::ErrorCode::AppInstallModuleNoVersion,
|
||||
"cannot specify a version when installing a module");
|
||||
}
|
||||
|
||||
auto ret = tasks.addNewTask(
|
||||
{ fuzzyRef->toString() },
|
||||
[this, curModule, fuzzyRef = std::move(*fuzzyRef), repo = paras->repo](
|
||||
PackageTask &taskRef) {
|
||||
LINGLONG_TRACE("install module")
|
||||
auto localRef = this->repo.clearReference(fuzzyRef, { .fallbackToRemote = false });
|
||||
if (!localRef.has_value()) {
|
||||
taskRef.reportError(
|
||||
LINGLONG_ERRV("to install the module, one must first install the app",
|
||||
utils::error::ErrorCode::AppInstallModuleRequireAppFirst));
|
||||
return;
|
||||
}
|
||||
auto modules = this->repo.getModuleList(*localRef);
|
||||
if (std::find(modules.begin(), modules.end(), curModule) != modules.end()) {
|
||||
taskRef.reportError(
|
||||
LINGLONG_ERRV("module is already installed",
|
||||
utils::error::ErrorCode::AppInstallModuleAlreadyExists));
|
||||
return;
|
||||
}
|
||||
std::optional<api::types::v1::Repo> remoteRepo;
|
||||
if (repo) {
|
||||
auto repoRet = this->repo.getRepoByAlias(*repo);
|
||||
if (!repoRet) {
|
||||
taskRef.reportError(
|
||||
LINGLONG_ERRV("failed to get repo by alias", repoRet.error()));
|
||||
return;
|
||||
}
|
||||
remoteRepo = *repoRet;
|
||||
}
|
||||
this->Install(taskRef, *localRef, std::nullopt, std::vector{ curModule }, remoteRepo);
|
||||
},
|
||||
connection());
|
||||
if (!ret) {
|
||||
return toDBusReply(utils::error::ErrorCode::AppInstallFailed, ret.error().message());
|
||||
}
|
||||
|
||||
auto &taskRef = ret->get();
|
||||
Q_EMIT TaskAdded(QDBusObjectPath{ taskRef.taskObjectPath() });
|
||||
taskRef.updateState(linglong::api::types::v1::State::Queued,
|
||||
"queued to install from remote");
|
||||
return utils::serialize::toQVariantMap(api::types::v1::PackageManager1PackageTaskResult{
|
||||
.taskObjectPath = taskRef.taskObjectPath().toStdString(),
|
||||
.code = 0,
|
||||
.message = "installing",
|
||||
});
|
||||
auto action = RefInstallationAction::create(*fuzzyRef,
|
||||
modules,
|
||||
*this,
|
||||
repo,
|
||||
paras->options,
|
||||
std::move(usedRepo));
|
||||
if (!action) {
|
||||
return toDBusReply(utils::error::ErrorCode::AppInstallFailed, "");
|
||||
}
|
||||
|
||||
// 如果用户输入了版本号,检查本地是否已经安装此版本
|
||||
if (fuzzyRef->version) {
|
||||
auto ref = this->repo.clearReference(*fuzzyRef,
|
||||
{
|
||||
.fallbackToRemote = false // NOLINT
|
||||
});
|
||||
if (ref) {
|
||||
return toDBusReply(utils::error::ErrorCode::AppInstallAlreadyInstalled,
|
||||
ref->toString() + " is already installed.");
|
||||
}
|
||||
auto res = action->prepare();
|
||||
if (!res) {
|
||||
return toDBusReply(utils::error::ErrorCode::AppInstallFailed, res.error().message());
|
||||
}
|
||||
|
||||
// we need latest local reference
|
||||
auto originalVersion = fuzzyRef->version;
|
||||
fuzzyRef->version.reset();
|
||||
auto localRef = this->repo.clearReference(*fuzzyRef,
|
||||
{
|
||||
.fallbackToRemote = false // NOLINT
|
||||
});
|
||||
// set version back
|
||||
|
||||
fuzzyRef->version = originalVersion;
|
||||
|
||||
api::types::v1::PackageManager1RequestInteractionAdditionalMessage additionalMessage;
|
||||
if (localRef) {
|
||||
additionalMessage.localRef = localRef->toString();
|
||||
}
|
||||
|
||||
auto refRet = [¶s, &fuzzyRef, &curModule, this] {
|
||||
if (!paras->repo) {
|
||||
return this->repo.getRemoteReferenceByPriority(*fuzzyRef,
|
||||
{ .onlyClearHighestPriority = false },
|
||||
curModule);
|
||||
auto installer = [action](PackageTask &task) {
|
||||
LINGLONG_TRACE("ref installation task")
|
||||
LogD("ref installation task is running");
|
||||
auto res = action->doAction(task);
|
||||
if (!res) {
|
||||
LogE("ref installation failed: {}", res.error());
|
||||
task.reportError(LINGLONG_ERRV(std::move(res).error().message(),
|
||||
utils::error::ErrorCode::AppInstallFailed));
|
||||
}
|
||||
|
||||
auto originalPriority = this->repo.promotePriority(paras->repo.value());
|
||||
auto recover = linglong::utils::finally::finally([&] {
|
||||
this->repo.recoverPriority(paras->repo.value(), originalPriority);
|
||||
});
|
||||
|
||||
return this->repo.getRemoteReferenceByPriority(*fuzzyRef,
|
||||
{ .onlyClearHighestPriority = true },
|
||||
curModule);
|
||||
}();
|
||||
|
||||
if (!refRet) {
|
||||
if (refRet.error().code()
|
||||
== static_cast<int>(utils::error::ErrorCode::AppNotFoundFromRemote)) {
|
||||
return toDBusReply(utils::error::ErrorCode::AppInstallNotFoundFromRemote,
|
||||
refRet.error().message());
|
||||
}
|
||||
|
||||
return toDBusReply(utils::error::ErrorCode::AppInstallFailed, refRet.error().message());
|
||||
}
|
||||
|
||||
auto remoteRef = refRet->reference;
|
||||
|
||||
additionalMessage.remoteRef = remoteRef.toString();
|
||||
|
||||
// 如果远程版本大于本地版本就升级,否则需要加--force降级,如果本地没有则直接安装,如果本地版本和远程版本相等就提示已安装
|
||||
auto msgType = api::types::v1::InteractionMessageType::Install;
|
||||
if (!additionalMessage.localRef.empty()) {
|
||||
if (remoteRef.version == localRef->version) {
|
||||
return toDBusReply(utils::error::ErrorCode::AppInstallAlreadyInstalled,
|
||||
localRef->toString() + " is already installed");
|
||||
}
|
||||
|
||||
if (remoteRef.version > localRef->version) {
|
||||
msgType = api::types::v1::InteractionMessageType::Upgrade;
|
||||
} else if (!paras->options.force) {
|
||||
auto err = fmt::format("The latest version has been installed. If you want to "
|
||||
"replace it, try using 'll-cli install {}/{} --force'",
|
||||
remoteRef.id,
|
||||
remoteRef.version.toString());
|
||||
return toDBusReply(utils::error::ErrorCode::AppInstallNeedDowngrade, err);
|
||||
}
|
||||
}
|
||||
|
||||
auto refSpec = fmt::format("{}:{}/{}/{}/{}",
|
||||
refRet->repo.name,
|
||||
remoteRef.channel,
|
||||
remoteRef.id,
|
||||
remoteRef.arch.toStdString(),
|
||||
curModule);
|
||||
// Note: do not capture any reference of variable which defined in this func.
|
||||
// it will be a dangling reference.
|
||||
auto installer = [this,
|
||||
remoteRef,
|
||||
localRef = localRef.has_value()
|
||||
? std::make_optional(std::move(localRef).value())
|
||||
: std::nullopt,
|
||||
curModule,
|
||||
modules,
|
||||
skipInteraction = paras->options.skipInteraction,
|
||||
msgType,
|
||||
additionalMessage,
|
||||
originalRepo = refRet->repo](PackageTask &taskRef) {
|
||||
// 升级需要用户交互
|
||||
if (msgType == api::types::v1::InteractionMessageType::Upgrade && !skipInteraction) {
|
||||
Q_EMIT RequestInteraction(QDBusObjectPath(taskRef.taskObjectPath()),
|
||||
static_cast<int>(msgType),
|
||||
utils::serialize::toQVariantMap(additionalMessage));
|
||||
|
||||
QEventLoop loop;
|
||||
api::types::v1::InteractionReply interactionReply;
|
||||
// Note: if capture the &taskRef into this lambda, be careful with it's life cycle.
|
||||
connect(this,
|
||||
&PackageManager::ReplyReceived,
|
||||
[&interactionReply, &loop](const QVariantMap &reply) {
|
||||
interactionReply =
|
||||
*utils::serialize::fromQVariantMap<api::types::v1::InteractionReply>(
|
||||
reply);
|
||||
loop.exit(0);
|
||||
});
|
||||
loop.exec();
|
||||
if (interactionReply.action != "yes") {
|
||||
taskRef.updateState(linglong::api::types::v1::State::Canceled, "canceled");
|
||||
}
|
||||
}
|
||||
|
||||
if (isTaskDone(taskRef.subState())) {
|
||||
return;
|
||||
}
|
||||
|
||||
this->Install(taskRef,
|
||||
remoteRef,
|
||||
localRef,
|
||||
localRef.has_value() ? this->repo.getModuleList(*localRef) : modules,
|
||||
originalRepo);
|
||||
};
|
||||
|
||||
auto taskRet = tasks.addNewTask({ refSpec }, std::move(installer), connection());
|
||||
auto taskRet = tasks.addNewTask({ action->getTaskName() }, std::move(installer), connection());
|
||||
if (!taskRet) {
|
||||
return toDBusReply(utils::error::ErrorCode::Unknown, taskRet.error().message());
|
||||
}
|
||||
|
|
@ -1059,119 +909,10 @@ auto PackageManager::Install(const QVariantMap ¶meters) noexcept -> QVariant
|
|||
return utils::serialize::toQVariantMap(api::types::v1::PackageManager1PackageTaskResult{
|
||||
.taskObjectPath = taskRef.taskObjectPath().toStdString(),
|
||||
.code = 0,
|
||||
.message = remoteRef.toString() + " is now installing",
|
||||
.message = action->getTaskName() + " is now installing",
|
||||
});
|
||||
}
|
||||
|
||||
void PackageManager::Install(PackageTask &taskContext,
|
||||
const package::Reference &newRef,
|
||||
std::optional<package::Reference> oldRef,
|
||||
const std::vector<std::string> &modules,
|
||||
const std::optional<api::types::v1::Repo> &repo) noexcept
|
||||
{
|
||||
LINGLONG_TRACE("install app")
|
||||
taskContext.updateState(linglong::api::types::v1::State::Processing,
|
||||
"Installing " + newRef.toString());
|
||||
|
||||
utils::Transaction transaction;
|
||||
|
||||
// 仅安装远程存在的modules
|
||||
auto installModules = [&repo, &newRef, &modules, this] {
|
||||
if (!repo) {
|
||||
return this->repo.getRemoteModuleListByPriority(newRef, modules, repo.has_value());
|
||||
}
|
||||
|
||||
auto originalPriority = this->repo.promotePriority(repo->alias.value_or(repo->name));
|
||||
auto recover = linglong::utils::finally::finally([&] {
|
||||
this->repo.recoverPriority(repo->alias.value_or(repo->name), originalPriority);
|
||||
});
|
||||
|
||||
return this->repo.getRemoteModuleListByPriority(newRef, modules, repo.has_value());
|
||||
}();
|
||||
|
||||
if (!installModules) {
|
||||
taskContext.reportError(LINGLONG_ERRV(std::move(installModules).error().message(),
|
||||
utils::error::ErrorCode::AppInstallFailed));
|
||||
return;
|
||||
}
|
||||
if (installModules->second.empty()) {
|
||||
auto list = std::accumulate(modules.begin(), modules.end(), std::string(","));
|
||||
taskContext.reportError(
|
||||
LINGLONG_ERRV("These modules do not exist remotely: " + QString::fromStdString(list),
|
||||
utils::error::ErrorCode::AppInstallModuleNotFound));
|
||||
return;
|
||||
}
|
||||
|
||||
InstallRef(taskContext,
|
||||
newRef,
|
||||
installModules->second,
|
||||
installModules->first); // install modules
|
||||
if (isTaskDone(taskContext.subState())) {
|
||||
return;
|
||||
}
|
||||
|
||||
transaction.addRollBack([this, &newRef, installModules = *installModules]() noexcept {
|
||||
auto tmp = PackageTask::createTemporaryTask();
|
||||
UninstallRef(tmp, newRef, installModules.second);
|
||||
if (tmp.state() != linglong::api::types::v1::State::Succeed) {
|
||||
LogE("failed to rollback install {}", newRef.toString());
|
||||
}
|
||||
});
|
||||
|
||||
taskContext.updateSubState(linglong::api::types::v1::SubState::PostAction,
|
||||
"processing after install");
|
||||
|
||||
auto mergeRet = this->repo.mergeModules();
|
||||
if (!mergeRet) {
|
||||
qCritical() << "merge modules failed: " << mergeRet.error().message();
|
||||
}
|
||||
|
||||
auto layer = this->repo.getLayerItem(newRef);
|
||||
if (!layer) {
|
||||
taskContext.reportError(LINGLONG_ERRV(std::move(layer).error().message(),
|
||||
utils::error::ErrorCode::AppInstallFailed));
|
||||
return;
|
||||
}
|
||||
// only app should do 'remove' and 'export'
|
||||
if (layer->info.kind == "app") {
|
||||
// remove all previous modules
|
||||
if (oldRef) {
|
||||
auto ret = this->removeAfterInstall(*oldRef, newRef, modules);
|
||||
if (!ret) {
|
||||
auto msg = fmt::format("Failed to remove old reference {} after install {}: {}",
|
||||
oldRef->toString(),
|
||||
newRef.toString(),
|
||||
ret.error().message());
|
||||
|
||||
taskContext.reportError(LINGLONG_ERRV(QString::fromStdString(msg),
|
||||
utils::error::ErrorCode::AppInstallFailed));
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
this->repo.exportReference(newRef);
|
||||
}
|
||||
auto result = this->tryGenerateCache(newRef);
|
||||
if (!result) {
|
||||
taskContext.reportError(
|
||||
LINGLONG_ERRV("Failed to generate some cache.\n" + result.error().message(),
|
||||
utils::error::ErrorCode::AppInstallFailed));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto ret = executePostInstallHooks(newRef);
|
||||
if (!ret) {
|
||||
taskContext.updateState(linglong::api::types::v1::State::Failed,
|
||||
"Failed to execute postInstall hooks.\n" + ret.error().message());
|
||||
return;
|
||||
}
|
||||
|
||||
transaction.commit();
|
||||
taskContext.updateState(linglong::api::types::v1::State::Succeed,
|
||||
"Install " + newRef.toString() + " (from repo: "
|
||||
+ installModules->first.name.c_str() + ") " + " success");
|
||||
}
|
||||
|
||||
void PackageManager::InstallRef(PackageTask &taskContext,
|
||||
const package::Reference &ref,
|
||||
std::vector<std::string> modules,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,275 @@
|
|||
// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
#include "ref_installation.h"
|
||||
|
||||
#include "linglong/package_manager/package_manager.h"
|
||||
#include "linglong/repo/ostree_repo.h"
|
||||
#include "linglong/repo/repo_cache.h"
|
||||
#include "linglong/utils/log/log.h"
|
||||
|
||||
namespace linglong::service {
|
||||
|
||||
std::shared_ptr<RefInstallationAction>
|
||||
RefInstallationAction::create(package::FuzzyReference fuzzyRef,
|
||||
std::vector<std::string> modules,
|
||||
PackageManager &pm,
|
||||
repo::OSTreeRepo &repo,
|
||||
api::types::v1::CommonOptions opts,
|
||||
std::optional<api::types::v1::Repo> usedRepo)
|
||||
{
|
||||
auto p =
|
||||
new RefInstallationAction(fuzzyRef, modules, pm, repo, std::move(opts), std::move(usedRepo));
|
||||
return std::shared_ptr<RefInstallationAction>(p);
|
||||
}
|
||||
|
||||
void RefInstallationAction::checkModules(const std::vector<std::string> &modules,
|
||||
bool &hasBinary,
|
||||
bool &extraModule)
|
||||
{
|
||||
for (const auto &module : modules) {
|
||||
if (module == "binary" || module == "runtime") {
|
||||
hasBinary = true;
|
||||
} else {
|
||||
extraModule = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool RefInstallationAction::extraModuleOnly(const std::vector<std::string> &modules)
|
||||
{
|
||||
bool hasBinary = false;
|
||||
bool extraModule = false;
|
||||
checkModules(modules, hasBinary, extraModule);
|
||||
|
||||
return extraModule && !hasBinary;
|
||||
}
|
||||
|
||||
RefInstallationAction::RefInstallationAction(package::FuzzyReference fuzzyRef,
|
||||
std::vector<std::string> modules,
|
||||
PackageManager &pm,
|
||||
repo::OSTreeRepo &repo,
|
||||
api::types::v1::CommonOptions opts,
|
||||
std::optional<api::types::v1::Repo> usedRepo)
|
||||
: Action(pm, repo, opts)
|
||||
, fuzzyRef(std::move(fuzzyRef))
|
||||
, modules(std::move(modules))
|
||||
, usedRepo(std::move(usedRepo))
|
||||
{
|
||||
}
|
||||
|
||||
utils::error::Result<void> RefInstallationAction::prepare()
|
||||
{
|
||||
LINGLONG_TRACE("ref installation prepare");
|
||||
|
||||
if (prepared) {
|
||||
return LINGLONG_OK;
|
||||
}
|
||||
|
||||
auto extraOnly = extraModuleOnly(modules);
|
||||
auto localRef = repo.latestLocalReference(fuzzyRef);
|
||||
if (extraOnly) {
|
||||
if (!localRef) {
|
||||
return LINGLONG_ERR("no matched binary module found");
|
||||
}
|
||||
|
||||
// Use the locally installed binary module's version to locate corresponding remote
|
||||
// modules
|
||||
fuzzyRef.version = localRef->version.toString();
|
||||
|
||||
// filter out the modules that are already installed
|
||||
std::vector<std::string> toInstall;
|
||||
for (const auto &module : modules) {
|
||||
auto ret = repo.getLayerItem(*localRef, module);
|
||||
if (!ret) {
|
||||
toInstall.emplace_back(module);
|
||||
}
|
||||
}
|
||||
modules = std::move(toInstall);
|
||||
} else if (localRef && localRef->version.toString() == fuzzyRef.version) {
|
||||
// if the user-specified version exactly matches the locally installed version
|
||||
return LINGLONG_ERR("package already installed",
|
||||
utils::error::ErrorCode::AppInstallAlreadyInstalled);
|
||||
}
|
||||
|
||||
this->taskName = fmt::format("Installing {}", fuzzyRef.toString());
|
||||
this->extraOnly = extraOnly;
|
||||
|
||||
prepared = true;
|
||||
return LINGLONG_OK;
|
||||
}
|
||||
|
||||
utils::error::Result<void> RefInstallationAction::doAction(PackageTask &task)
|
||||
{
|
||||
LINGLONG_TRACE("ref installation do action");
|
||||
|
||||
if (!prepared) {
|
||||
return LINGLONG_ERR("action not prepared");
|
||||
}
|
||||
|
||||
auto res = preInstall(task);
|
||||
if (!res) {
|
||||
return res;
|
||||
}
|
||||
|
||||
res = install(task);
|
||||
if (!res) {
|
||||
return res;
|
||||
}
|
||||
|
||||
return postInstall(task);
|
||||
}
|
||||
|
||||
utils::error::Result<void> RefInstallationAction::preInstall(PackageTask &task)
|
||||
{
|
||||
LINGLONG_TRACE("ref installation preInstall");
|
||||
|
||||
// find all candidate remote packages
|
||||
auto candidates = repo.matchRemoteByPriority(fuzzyRef, usedRepo);
|
||||
if (!candidates) {
|
||||
return LINGLONG_ERR(candidates);
|
||||
}
|
||||
|
||||
// find the latest package from candidates
|
||||
auto target = candidates->getLatestPackage();
|
||||
if (!target) {
|
||||
return LINGLONG_ERR("package not found",
|
||||
utils::error::ErrorCode::AppInstallNotFoundFromRemote);
|
||||
}
|
||||
|
||||
auto operation = getActionOperation(target->second.get(), extraOnly);
|
||||
if (!operation) {
|
||||
return LINGLONG_ERR(operation);
|
||||
}
|
||||
if (operation->newRef) {
|
||||
operation->newRef->repo = target->first.get();
|
||||
}
|
||||
|
||||
if (operation->operation == ActionOperation::Overwrite) {
|
||||
return LINGLONG_ERR("package already installed",
|
||||
utils::error::ErrorCode::AppInstallAlreadyInstalled);
|
||||
}
|
||||
if (operation->operation == ActionOperation::Downgrade && !options.force) {
|
||||
return LINGLONG_ERR("latest version already installed",
|
||||
utils::error::ErrorCode::AppInstallNeedDowngrade);
|
||||
}
|
||||
|
||||
if (operation->operation == ActionOperation::Policy::Upgrade && !options.skipInteraction) {
|
||||
auto additionalMessage = api::types::v1::PackageManager1RequestInteractionAdditionalMessage{
|
||||
.localRef = operation->oldRef->toString(),
|
||||
.remoteRef = operation->newRef->reference.toString()
|
||||
};
|
||||
if (!pm.waitConfirm(task,
|
||||
api::types::v1::InteractionMessageType::Upgrade,
|
||||
additionalMessage)) {
|
||||
return LINGLONG_ERR("action canceled");
|
||||
}
|
||||
}
|
||||
|
||||
this->operation = std::move(operation).value();
|
||||
this->candidates = std::move(candidates).value();
|
||||
|
||||
return LINGLONG_OK;
|
||||
}
|
||||
|
||||
utils::error::Result<void> RefInstallationAction::install(PackageTask &task)
|
||||
{
|
||||
LINGLONG_TRACE("ref installation install");
|
||||
|
||||
const auto &newRef = operation.newRef->reference;
|
||||
task.updateState(linglong::api::types::v1::State::Processing,
|
||||
fmt::format("Installing {}", newRef.toString()));
|
||||
|
||||
auto remoteModules = candidates.getReferenceModules(newRef);
|
||||
if (remoteModules.empty()) {
|
||||
return LINGLONG_ERR("no modules found");
|
||||
}
|
||||
|
||||
auto installModules = std::vector<std::string>{};
|
||||
for (const auto &module : modules) {
|
||||
if (std::find(remoteModules.begin(), remoteModules.end(), module) != remoteModules.end()) {
|
||||
installModules.emplace_back(module);
|
||||
}
|
||||
}
|
||||
if (installModules.empty()) {
|
||||
return LINGLONG_ERR("no modules found");
|
||||
}
|
||||
|
||||
pm.InstallRef(task, newRef, installModules, operation.newRef->repo);
|
||||
if (task.isTaskDone()) {
|
||||
return LINGLONG_ERR("install canceled");
|
||||
}
|
||||
transaction.addRollBack([this, &installModules, &newRef]() noexcept {
|
||||
auto tmp = PackageTask::createTemporaryTask();
|
||||
pm.UninstallRef(tmp, newRef, installModules);
|
||||
if (tmp.state() != linglong::api::types::v1::State::Succeed) {
|
||||
LogE("failed to rollback install {}", newRef.toString());
|
||||
}
|
||||
});
|
||||
|
||||
return LINGLONG_OK;
|
||||
}
|
||||
|
||||
utils::error::Result<void> RefInstallationAction::postInstall(PackageTask &task)
|
||||
{
|
||||
LINGLONG_TRACE("ref installation postInstall");
|
||||
|
||||
task.updateSubState(linglong::api::types::v1::SubState::PostAction, "processing after install");
|
||||
|
||||
auto mergeRet = this->repo.mergeModules();
|
||||
if (!mergeRet) {
|
||||
LogE("failed to merge modules: {}", mergeRet.error());
|
||||
}
|
||||
|
||||
const auto &newRef = operation.newRef->reference;
|
||||
const auto &oldRef = operation.oldRef;
|
||||
auto layer = this->repo.getLayerItem(newRef);
|
||||
if (!layer) {
|
||||
task.reportError(
|
||||
LINGLONG_ERRV(layer.error().message(), utils::error::ErrorCode::AppInstallFailed));
|
||||
return LINGLONG_ERR("failed to get layer item", layer);
|
||||
}
|
||||
// only app should do 'remove' and 'export'
|
||||
if (layer->info.kind == "app") {
|
||||
// remove all previous modules
|
||||
if (oldRef) {
|
||||
auto ret = pm.removeAfterInstall(*oldRef, newRef, modules);
|
||||
if (!ret) {
|
||||
auto msg = fmt::format("Failed to remove old reference {} after install {}: {}",
|
||||
oldRef->toString(),
|
||||
newRef.toString(),
|
||||
ret.error().message());
|
||||
|
||||
task.reportError(LINGLONG_ERRV(msg, utils::error::ErrorCode::AppInstallFailed));
|
||||
return LINGLONG_ERR(ret);
|
||||
}
|
||||
} else {
|
||||
this->repo.exportReference(newRef);
|
||||
}
|
||||
auto result = pm.tryGenerateCache(newRef);
|
||||
if (!result) {
|
||||
task.reportError(
|
||||
LINGLONG_ERRV("Failed to generate some cache.\n" + result.error().message(),
|
||||
utils::error::ErrorCode::AppInstallFailed));
|
||||
return LINGLONG_ERR(result);
|
||||
}
|
||||
}
|
||||
|
||||
auto ret = pm.executePostInstallHooks(newRef);
|
||||
if (!ret) {
|
||||
task.updateState(linglong::api::types::v1::State::Failed,
|
||||
"Failed to execute postInstall hooks.\n" + ret.error().message());
|
||||
return LINGLONG_ERR(ret);
|
||||
}
|
||||
|
||||
transaction.commit();
|
||||
task.updateState(linglong::api::types::v1::State::Succeed,
|
||||
fmt::format("Install {} (from repo: {}) success",
|
||||
newRef.toString(),
|
||||
operation.newRef->repo.name));
|
||||
|
||||
return LINGLONG_OK;
|
||||
}
|
||||
|
||||
} // namespace linglong::service
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "linglong/package/fuzzy_reference.h"
|
||||
#include "linglong/package_manager/action.h"
|
||||
#include "linglong/repo/remote_packages.h"
|
||||
#include "linglong/utils/transaction.h"
|
||||
|
||||
namespace linglong::service {
|
||||
|
||||
class RefInstallationAction : public Action
|
||||
{
|
||||
public:
|
||||
static std::shared_ptr<RefInstallationAction>
|
||||
create(package::FuzzyReference fuzzyRef,
|
||||
std::vector<std::string> modules,
|
||||
PackageManager &pm,
|
||||
repo::OSTreeRepo &repo,
|
||||
api::types::v1::CommonOptions options,
|
||||
std::optional<api::types::v1::Repo> usedRepo);
|
||||
|
||||
static void checkModules(const std::vector<std::string> &modules,
|
||||
bool &hasBinary,
|
||||
bool &extraModule);
|
||||
static bool extraModuleOnly(const std::vector<std::string> &modules);
|
||||
|
||||
virtual ~RefInstallationAction() = default;
|
||||
|
||||
virtual utils::error::Result<void> prepare() override;
|
||||
virtual utils::error::Result<void> doAction(PackageTask &task) override;
|
||||
|
||||
virtual std::string getTaskName() const override { return taskName; }
|
||||
|
||||
protected:
|
||||
virtual utils::error::Result<void> preInstall(PackageTask &task);
|
||||
virtual utils::error::Result<void> install(PackageTask &task);
|
||||
virtual utils::error::Result<void> postInstall(PackageTask &task);
|
||||
|
||||
private:
|
||||
RefInstallationAction(package::FuzzyReference fuzzyRef,
|
||||
std::vector<std::string> modules,
|
||||
PackageManager &pm,
|
||||
repo::OSTreeRepo &repo,
|
||||
api::types::v1::CommonOptions options,
|
||||
std::optional<api::types::v1::Repo> usedRepo);
|
||||
|
||||
package::FuzzyReference fuzzyRef;
|
||||
std::vector<std::string> modules;
|
||||
|
||||
bool prepared = false;
|
||||
bool extraOnly = false;
|
||||
ActionOperation operation;
|
||||
std::string taskName;
|
||||
utils::Transaction transaction;
|
||||
std::optional<api::types::v1::Repo> usedRepo;
|
||||
repo::RemotePackages candidates;
|
||||
};
|
||||
|
||||
} // namespace linglong::service
|
||||
|
|
@ -125,8 +125,6 @@ utils::error::Result<void> UabInstallationAction::checkUABLayersConstrain(
|
|||
}
|
||||
|
||||
const auto &front = layers.front().info;
|
||||
bool hasBinary = false;
|
||||
bool extraModule = false;
|
||||
for (const auto &layer : layers) {
|
||||
auto arch = package::Architecture::parse(layer.info.arch[0]);
|
||||
if (!arch) {
|
||||
|
|
@ -144,21 +142,15 @@ utils::error::Result<void> UabInstallationAction::checkUABLayersConstrain(
|
|||
if (layer.info.version != front.version) {
|
||||
return LINGLONG_ERR("modules have different version");
|
||||
}
|
||||
}
|
||||
|
||||
const auto &module = layer.info.packageInfoV2Module;
|
||||
if (module == "binary" || module == "runtime") {
|
||||
hasBinary = true;
|
||||
} else {
|
||||
extraModule = true;
|
||||
if (extraModuleOnly(layers)) {
|
||||
auto fuzzyRef =
|
||||
package::FuzzyReference::create(front.channel, front.id, front.version, std::nullopt);
|
||||
if (!fuzzyRef) {
|
||||
return LINGLONG_ERR(fuzzyRef);
|
||||
}
|
||||
}
|
||||
|
||||
auto fuzzyRef =
|
||||
package::FuzzyReference::create(front.channel, front.id, front.version, std::nullopt);
|
||||
if (!fuzzyRef) {
|
||||
return LINGLONG_ERR(fuzzyRef);
|
||||
}
|
||||
if (extraModule && !hasBinary) {
|
||||
auto localRef = repo.clearReference(*fuzzyRef,
|
||||
{
|
||||
.forceRemote = false,
|
||||
|
|
@ -178,70 +170,23 @@ utils::error::Result<void> UabInstallationAction::checkUABLayersConstrain(
|
|||
return LINGLONG_OK;
|
||||
}
|
||||
|
||||
utils::error::Result<TaskAction>
|
||||
UabInstallationAction::getTaskAction(repo::OSTreeRepo &repo, const CheckedLayers &checkedLayers)
|
||||
bool UabInstallationAction::extraModuleOnly(const std::vector<api::types::v1::UabLayer> &layers)
|
||||
{
|
||||
LINGLONG_TRACE("get task action");
|
||||
|
||||
const api::types::v1::UabLayer &toCheck =
|
||||
checkedLayers.first.empty() ? checkedLayers.second.front() : checkedLayers.first.front();
|
||||
|
||||
auto fuzzyRef = package::FuzzyReference::create(toCheck.info.channel,
|
||||
toCheck.info.id,
|
||||
std::nullopt,
|
||||
std::nullopt);
|
||||
if (!fuzzyRef) {
|
||||
return LINGLONG_ERR(fuzzyRef);
|
||||
}
|
||||
|
||||
auto checkRef = package::Reference::fromPackageInfo(toCheck.info);
|
||||
if (!checkRef) {
|
||||
return LINGLONG_ERR(checkRef);
|
||||
}
|
||||
|
||||
const auto &kind = toCheck.info.kind;
|
||||
TaskAction action;
|
||||
action.additionalMessage.remoteRef = checkRef->toString();
|
||||
auto installedRef = repo.latestLocalReference(*fuzzyRef);
|
||||
if (installedRef) {
|
||||
action.additionalMessage.localRef = installedRef->toString();
|
||||
if (checkRef->version == installedRef->version) {
|
||||
action.policy = TaskAction::Policy::Overwrite;
|
||||
} else if (checkRef->version > installedRef->version) {
|
||||
if (kind == "app") {
|
||||
action.policy = TaskAction::Policy::Upgrade;
|
||||
action.msgType = api::types::v1::InteractionMessageType::Upgrade;
|
||||
} else {
|
||||
action.policy = TaskAction::Policy::Install;
|
||||
}
|
||||
} else {
|
||||
if (kind == "app") {
|
||||
action.policy = TaskAction::Policy::Downgrade;
|
||||
} else {
|
||||
action.policy = TaskAction::Policy::Install;
|
||||
}
|
||||
for (const auto &layer : layers) {
|
||||
const auto &module = layer.info.packageInfoV2Module;
|
||||
if (module == "binary" || module == "runtime") {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
action.policy = TaskAction::Policy::Install;
|
||||
}
|
||||
|
||||
action.kind = kind;
|
||||
action.newRef = std::move(checkRef).value();
|
||||
if (installedRef) {
|
||||
action.oldRef = std::move(installedRef).value();
|
||||
}
|
||||
|
||||
return action;
|
||||
return true;
|
||||
}
|
||||
|
||||
UabInstallationAction::UabInstallationAction(int uabFD,
|
||||
PackageManager &pm,
|
||||
repo::OSTreeRepo &repo,
|
||||
api::types::v1::CommonOptions opts)
|
||||
: fd(dup(uabFD))
|
||||
, pm(pm)
|
||||
, repo(repo)
|
||||
, options(std::move(opts))
|
||||
: Action(pm, repo, opts)
|
||||
, fd(dup(uabFD))
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -292,22 +237,6 @@ utils::error::Result<void> UabInstallationAction::prepare()
|
|||
checkedLayers = std::move(res).value();
|
||||
}
|
||||
|
||||
auto action = getTaskAction(repo, checkedLayers);
|
||||
if (!action) {
|
||||
return LINGLONG_ERR(action);
|
||||
}
|
||||
|
||||
if (action->policy == TaskAction::Policy::Overwrite) {
|
||||
return LINGLONG_ERR("package already installed",
|
||||
utils::error::ErrorCode::AppInstallAlreadyInstalled);
|
||||
}
|
||||
|
||||
if (action->policy == TaskAction::Policy::Downgrade && !options.force) {
|
||||
return LINGLONG_ERR("latest version already installed",
|
||||
utils::error::ErrorCode::AppInstallNeedDowngrade);
|
||||
}
|
||||
|
||||
this->action = std::move(action).value();
|
||||
this->taskName = fmt::format("Installing {}", uabFile->symLinkTarget());
|
||||
this->uabFile = std::move(uabFile);
|
||||
|
||||
|
|
@ -342,12 +271,36 @@ utils::error::Result<void> UabInstallationAction::preInstall(PackageTask &task)
|
|||
task.updateState(linglong::api::types::v1::State::Processing, "installing uab");
|
||||
task.updateSubState(linglong::api::types::v1::SubState::PreAction, "prepare environment");
|
||||
|
||||
if (action.policy == TaskAction::Policy::Upgrade && !options.skipInteraction) {
|
||||
if (!pm.waitConfirm(task, action.msgType, action.additionalMessage)) {
|
||||
const auto &toCheck = checkedLayers.first.empty() ? checkedLayers.second : checkedLayers.first;
|
||||
auto operation = getActionOperation(toCheck.front().info, extraModuleOnly(toCheck));
|
||||
if (!operation) {
|
||||
return LINGLONG_ERR(operation);
|
||||
}
|
||||
|
||||
if (operation->operation == ActionOperation::Overwrite) {
|
||||
return LINGLONG_ERR("package already installed",
|
||||
utils::error::ErrorCode::AppInstallAlreadyInstalled);
|
||||
}
|
||||
|
||||
if (operation->operation == ActionOperation::Downgrade && !options.force) {
|
||||
return LINGLONG_ERR("latest version already installed",
|
||||
utils::error::ErrorCode::AppInstallNeedDowngrade);
|
||||
}
|
||||
|
||||
if (operation->operation == ActionOperation::Upgrade && !options.skipInteraction) {
|
||||
auto additionalMessage = api::types::v1::PackageManager1RequestInteractionAdditionalMessage{
|
||||
.localRef = operation->oldRef->toString(),
|
||||
.remoteRef = operation->newRef->reference.toString()
|
||||
};
|
||||
if (!pm.waitConfirm(task,
|
||||
api::types::v1::InteractionMessageType::Upgrade,
|
||||
additionalMessage)) {
|
||||
return LINGLONG_ERR("action canceled");
|
||||
}
|
||||
}
|
||||
|
||||
this->operation = std::move(operation).value();
|
||||
|
||||
return LINGLONG_OK;
|
||||
}
|
||||
|
||||
|
|
@ -373,8 +326,8 @@ utils::error::Result<void> UabInstallationAction::postInstall(PackageTask &task)
|
|||
{
|
||||
LINGLONG_TRACE("uab installation postInstall");
|
||||
|
||||
const auto &newRef = action.newRef;
|
||||
const auto &oldRef = action.oldRef;
|
||||
const auto &newRef = operation.newRef->reference;
|
||||
const auto &oldRef = operation.oldRef;
|
||||
|
||||
if (!oldRef) {
|
||||
auto mergeRet = repo.mergeModules();
|
||||
|
|
@ -387,17 +340,17 @@ utils::error::Result<void> UabInstallationAction::postInstall(PackageTask &task)
|
|||
// 1. replace installed version
|
||||
// 2. export entries
|
||||
// 3. generate cache
|
||||
if (action.kind == "app") {
|
||||
if (operation.kind == "app") {
|
||||
if (oldRef) {
|
||||
auto ret = pm.removeAfterInstall(*oldRef, *newRef, repo.getModuleList(*oldRef));
|
||||
auto ret = pm.removeAfterInstall(*oldRef, newRef, repo.getModuleList(*oldRef));
|
||||
if (!ret) {
|
||||
LogE("remove old reference after install newer version failed: {}", ret.error());
|
||||
return LINGLONG_ERR(ret);
|
||||
}
|
||||
} else {
|
||||
// export directly
|
||||
this->repo.exportReference(*newRef);
|
||||
auto result = pm.tryGenerateCache(*newRef);
|
||||
this->repo.exportReference(newRef);
|
||||
auto result = pm.tryGenerateCache(newRef);
|
||||
if (!result) {
|
||||
auto msg =
|
||||
fmt::format("Failed to generate some cache: {}", result.error().message());
|
||||
|
|
@ -407,7 +360,7 @@ utils::error::Result<void> UabInstallationAction::postInstall(PackageTask &task)
|
|||
}
|
||||
}
|
||||
|
||||
auto ret = pm.executePostInstallHooks(*newRef);
|
||||
auto ret = pm.executePostInstallHooks(newRef);
|
||||
if (!ret) {
|
||||
task.reportError(std::move(ret).error());
|
||||
return LINGLONG_ERR("failed to execute post install hooks");
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
#include "linglong/api/types/v1/PackageManager1RequestInteractionAdditionalMessage.hpp"
|
||||
#include "linglong/api/types/v1/UabLayer.hpp"
|
||||
#include "linglong/package/uab_file.h"
|
||||
#include "linglong/package_manager/action.h"
|
||||
#include "linglong/package_manager/package_manager.h"
|
||||
#include "linglong/package_manager/package_task.h"
|
||||
#include "linglong/repo/ostree_repo.h"
|
||||
|
|
@ -18,25 +19,7 @@
|
|||
|
||||
namespace linglong::service {
|
||||
|
||||
struct TaskAction
|
||||
{
|
||||
enum Policy {
|
||||
Upgrade,
|
||||
Install,
|
||||
Remove,
|
||||
Overwrite,
|
||||
Downgrade,
|
||||
} policy;
|
||||
|
||||
api::types::v1::InteractionMessageType msgType;
|
||||
api::types::v1::PackageManager1RequestInteractionAdditionalMessage additionalMessage;
|
||||
|
||||
std::string kind;
|
||||
std::optional<package::Reference> oldRef;
|
||||
std::optional<package::Reference> newRef;
|
||||
};
|
||||
|
||||
class UabInstallationAction
|
||||
class UabInstallationAction : public Action
|
||||
{
|
||||
public:
|
||||
static std::shared_ptr<UabInstallationAction> create(int uabFD,
|
||||
|
|
@ -52,8 +35,7 @@ public:
|
|||
repo::OSTreeRepo &repo, const std::vector<linglong::api::types::v1::UabLayer> &layers);
|
||||
static utils::error::Result<void> checkUABLayersConstrain(
|
||||
repo::OSTreeRepo &repo, const std::vector<api::types::v1::UabLayer> &layers);
|
||||
static utils::error::Result<TaskAction> getTaskAction(repo::OSTreeRepo &repo,
|
||||
const CheckedLayers &checkedLayers);
|
||||
static bool extraModuleOnly(const std::vector<api::types::v1::UabLayer> &layers);
|
||||
|
||||
virtual ~UabInstallationAction();
|
||||
|
||||
|
|
@ -79,13 +61,10 @@ private:
|
|||
std::optional<std::string> subRef = std::nullopt);
|
||||
|
||||
int fd;
|
||||
PackageManager ±
|
||||
repo::OSTreeRepo &repo;
|
||||
api::types::v1::CommonOptions options;
|
||||
|
||||
TaskAction action;
|
||||
ActionOperation operation;
|
||||
std::string taskName;
|
||||
CheckedLayers checkedLayers;
|
||||
bool extraOnly = false;
|
||||
std::unique_ptr<package::UABFile> uabFile;
|
||||
utils::Transaction transaction;
|
||||
std::filesystem::path uabMountPoint;
|
||||
|
|
|
|||
|
|
@ -1755,49 +1755,33 @@ OSTreeRepo::searchRemote(const package::FuzzyReference &fuzzyRef,
|
|||
{
|
||||
LINGLONG_TRACE("list remote packages");
|
||||
|
||||
auto client = this->createClientV2(repo.url);
|
||||
request_fuzzy_search_req_t req{ nullptr, nullptr, nullptr, nullptr, nullptr };
|
||||
auto freeIfNotNull = utils::finally::finally([&req] {
|
||||
if (req.app_id != nullptr) {
|
||||
free(req.app_id); // NOLINT
|
||||
}
|
||||
if (req.channel != nullptr) {
|
||||
free(req.channel); // NOLINT
|
||||
}
|
||||
if (req.version != nullptr) {
|
||||
free(req.version); // NOLINT
|
||||
}
|
||||
if (req.arch != nullptr) {
|
||||
free(req.arch); // NOLINT
|
||||
}
|
||||
if (req.repo_name != nullptr) {
|
||||
free(req.repo_name); // NOLINT
|
||||
}
|
||||
});
|
||||
LogD("searchRemote use repo {}", nlohmann::json(repo).dump());
|
||||
|
||||
req.app_id = ::strndup(fuzzyRef.id.data(), fuzzyRef.id.size());
|
||||
if (req.app_id == nullptr) {
|
||||
auto client = this->createClientV2(repo.url);
|
||||
|
||||
char *app_id = strndup(fuzzyRef.id.data(), fuzzyRef.id.size());
|
||||
if (app_id == nullptr) {
|
||||
return LINGLONG_ERR(fmt::format("strndup app_id failed: {}", fuzzyRef.id));
|
||||
}
|
||||
req.repo_name = ::strndup(repo.name.data(), repo.name.size());
|
||||
if (req.repo_name == nullptr) {
|
||||
char *repo_name = strndup(repo.name.data(), repo.name.size());
|
||||
if (repo_name == nullptr) {
|
||||
return LINGLONG_ERR(fmt::format("strndup repo_name failed: {}", repo.name));
|
||||
}
|
||||
|
||||
char *channel = nullptr;
|
||||
if (fuzzyRef.channel) {
|
||||
auto channel = fuzzyRef.channel.value();
|
||||
req.channel = strndup(channel.data(), channel.size());
|
||||
if (req.channel == nullptr) {
|
||||
return LINGLONG_ERR(QString{ "strndup channel failed: %1" }.arg(channel.data()));
|
||||
channel = strndup(fuzzyRef.channel->data(), fuzzyRef.channel->size());
|
||||
if (channel == nullptr) {
|
||||
return LINGLONG_ERR(fmt::format("strndup channel failed: {}", *fuzzyRef.channel));
|
||||
}
|
||||
}
|
||||
|
||||
// use prefix matching on version strings when searching the remote server
|
||||
char *version = nullptr;
|
||||
if (fuzzyRef.version) {
|
||||
auto version = fuzzyRef.version.value();
|
||||
req.version = strndup(version.data(), version.size());
|
||||
if (req.version == nullptr) {
|
||||
return LINGLONG_ERR(QString{ "strndup version failed: %1" }.arg(version.data()));
|
||||
version = strndup(fuzzyRef.version->data(), fuzzyRef.version->size());
|
||||
if (version == nullptr) {
|
||||
return LINGLONG_ERR(fmt::format("strndup version failed: {}", *fuzzyRef.version));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1806,14 +1790,20 @@ OSTreeRepo::searchRemote(const package::FuzzyReference &fuzzyRef,
|
|||
return LINGLONG_ERR(defaultArch);
|
||||
}
|
||||
|
||||
auto arch = fuzzyRef.arch.value_or(*defaultArch);
|
||||
auto archStr = arch.toStdString();
|
||||
req.arch = strndup(archStr.data(), archStr.size());
|
||||
if (req.arch == nullptr) {
|
||||
return LINGLONG_ERR(QString{ "strndup arch failed: %1" }.arg(archStr.data()));
|
||||
auto arch = fuzzyRef.arch.value_or(*defaultArch).toStdString();
|
||||
char *archStr = strndup(arch.data(), arch.size());
|
||||
if (archStr == nullptr) {
|
||||
return LINGLONG_ERR(fmt::format("strndup arch failed: {}", arch));
|
||||
}
|
||||
|
||||
auto response = client->fuzzySearch(&req);
|
||||
auto req = request_fuzzy_search_req_create(app_id, archStr, channel, repo_name, version);
|
||||
if (!req) {
|
||||
return LINGLONG_ERR("failed to create request");
|
||||
}
|
||||
auto freeIfNotNull = utils::finally::finally([req] {
|
||||
request_fuzzy_search_req_free(req);
|
||||
});
|
||||
auto response = client->fuzzySearch(req);
|
||||
if (!response) {
|
||||
return LINGLONG_ERR("failed to send request to remote server\nIf the network is slow, "
|
||||
"set a longer timeout via the LINGLONG_CONNECT_TIMEOUT environment "
|
||||
|
|
@ -1834,11 +1824,7 @@ OSTreeRepo::searchRemote(const package::FuzzyReference &fuzzyRef,
|
|||
: utils::error::ErrorCode::NetworkError));
|
||||
}
|
||||
|
||||
if (response->data == nullptr) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (response->data->count == 0) {
|
||||
if (response->data == nullptr || response->data->count == 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
|
|
@ -1881,6 +1867,49 @@ OSTreeRepo::searchRemote(const package::FuzzyReference &fuzzyRef,
|
|||
return std::move(pkgInfos);
|
||||
}
|
||||
|
||||
utils::error::Result<repo::RemotePackages>
|
||||
OSTreeRepo::matchRemoteByPriority(const package::FuzzyReference &fuzzyRef,
|
||||
const std::optional<api::types::v1::Repo> &repo) const noexcept
|
||||
{
|
||||
repo::RemotePackages remotePackages;
|
||||
|
||||
if (repo) {
|
||||
auto list = this->searchRemote(fuzzyRef, *repo, true);
|
||||
if (!list) {
|
||||
LogW("failed to list remote packages from {}: {}", repo->name, list.error());
|
||||
return remotePackages;
|
||||
}
|
||||
|
||||
if (!list->empty()) {
|
||||
remotePackages.addPackages(*repo, std::move(list).value());
|
||||
}
|
||||
} else {
|
||||
auto repos = this->getPriorityGroupedRepos();
|
||||
for (const auto &repoGroup : repos) {
|
||||
for (const auto &repo : repoGroup) {
|
||||
auto list = this->searchRemote(fuzzyRef, repo, true);
|
||||
if (!list) {
|
||||
LogW("failed to list remote packages from {}: {}", repo.name, list.error());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (list->empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
remotePackages.addPackages(repo, std::move(list).value());
|
||||
}
|
||||
|
||||
// try a lower-priority repo when no matched result
|
||||
if (!remotePackages.empty()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return remotePackages;
|
||||
}
|
||||
|
||||
void OSTreeRepo::unexportReference(const std::string &layerDir) noexcept
|
||||
{
|
||||
QString layerDirStr = layerDir.c_str();
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
#include "linglong/package_manager/package_task.h"
|
||||
#include "linglong/repo/client_factory.h"
|
||||
#include "linglong/repo/config.h"
|
||||
#include "linglong/repo/remote_packages.h"
|
||||
#include "linglong/repo/repo_cache.h"
|
||||
#include "linglong/utils/error/error.h"
|
||||
|
||||
|
|
@ -60,7 +61,7 @@ public:
|
|||
[[nodiscard]] utils::error::Result<api::types::v1::Repo>
|
||||
getRepoByAlias(const std::string &alias) const noexcept;
|
||||
[[nodiscard]] std::vector<api::types::v1::Repo> getHighestPriorityRepos() const noexcept;
|
||||
[[nodiscard]] std::vector<std::vector<api::types::v1::Repo>>
|
||||
[[nodiscard]] virtual std::vector<std::vector<api::types::v1::Repo>>
|
||||
getPriorityGroupedRepos() const noexcept;
|
||||
repoPriority_t promotePriority(const std::string &alias) noexcept;
|
||||
void recoverPriority(const std::string &alias, const repoPriority_t &priority) noexcept;
|
||||
|
|
@ -107,10 +108,14 @@ public:
|
|||
const package::FuzzyReference &fuzzyRef,
|
||||
const api::types::v1::Repo &repo,
|
||||
bool semanticMatching = false) const noexcept;
|
||||
utils::error::Result<repo::RemotePackages> virtual matchRemoteByPriority(
|
||||
const package::FuzzyReference &fuzzyRef,
|
||||
const std::optional<api::types::v1::Repo> &repo = std::nullopt) const noexcept;
|
||||
|
||||
utils::error::Result<std::vector<api::types::v1::RepositoryCacheLayersItem>>
|
||||
listLayerItem() const noexcept;
|
||||
[[nodiscard]] utils::error::Result<std::vector<api::types::v1::RepositoryCacheLayersItem>>
|
||||
[[nodiscard]] virtual utils::error::Result<
|
||||
std::vector<api::types::v1::RepositoryCacheLayersItem>>
|
||||
listLocalBy(const linglong::repo::repoCacheQuery &query) const noexcept;
|
||||
utils::error::Result<int64_t>
|
||||
getLayerCreateTime(const api::types::v1::RepositoryCacheLayersItem &item) const noexcept;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#include "remote_packages.h"
|
||||
|
||||
namespace linglong::repo {
|
||||
|
||||
RemotePackages &RemotePackages::addPackages(Repo repo, std::vector<PackageInfoV2> packages)
|
||||
{
|
||||
repoPackages.emplace_back(std::make_pair(std::move(repo), std::move(packages)));
|
||||
return *this;
|
||||
}
|
||||
|
||||
const utils::error::Result<PackageWithRepo> RemotePackages::getLatestPackage() const
|
||||
{
|
||||
LINGLONG_TRACE("get latest package");
|
||||
|
||||
if (empty()) {
|
||||
return LINGLONG_ERR("packages is empty");
|
||||
}
|
||||
|
||||
auto compare = [](const auto &a, const auto &b) -> bool {
|
||||
auto versionA = package::Version::parse(a.version);
|
||||
auto versionB = package::Version::parse(b.version);
|
||||
|
||||
if (!versionB) {
|
||||
return false;
|
||||
} else if (!versionA) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return *versionA < *versionB;
|
||||
};
|
||||
|
||||
std::optional<PackageWithRepo> latest;
|
||||
for (const auto &packages : repoPackages) {
|
||||
auto max = std::max_element(packages.second.begin(), packages.second.end(), compare);
|
||||
if (max != packages.second.end() && (!latest || compare(latest->second.get(), *max))) {
|
||||
latest = std::make_pair(std::ref(packages.first), std::ref(*max));
|
||||
}
|
||||
}
|
||||
|
||||
return *latest;
|
||||
}
|
||||
|
||||
std::vector<std::string> RemotePackages::getReferenceModules(const package::Reference &ref) const
|
||||
{
|
||||
std::vector<std::string> modules;
|
||||
for (const auto &packages : repoPackages) {
|
||||
for (const auto &package : packages.second) {
|
||||
if (package.id == ref.id && package.channel == ref.channel
|
||||
&& package.version == ref.version.toString()
|
||||
&& package.arch[0] == ref.arch.toStdString()) {
|
||||
modules.emplace_back(package.packageInfoV2Module);
|
||||
}
|
||||
}
|
||||
}
|
||||
return modules;
|
||||
}
|
||||
|
||||
} // namespace linglong::repo
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "linglong/api/types/v1/PackageInfoV2.hpp"
|
||||
#include "linglong/api/types/v1/Repo.hpp"
|
||||
#include "linglong/package/reference.h"
|
||||
#include "linglong/utils/error/error.h"
|
||||
|
||||
#include <list>
|
||||
|
||||
namespace linglong::repo {
|
||||
|
||||
using linglong::api::types::v1::PackageInfoV2;
|
||||
using linglong::api::types::v1::Repo;
|
||||
|
||||
using PackagesWithRepo = std::pair<Repo, std::vector<PackageInfoV2>>;
|
||||
using PackageWithRepo =
|
||||
std::pair<std::reference_wrapper<const Repo>, std::reference_wrapper<const PackageInfoV2>>;
|
||||
|
||||
class RemotePackages
|
||||
{
|
||||
public:
|
||||
RemotePackages() = default;
|
||||
RemotePackages(RemotePackages &&) = default;
|
||||
RemotePackages &operator=(RemotePackages &&) = default;
|
||||
|
||||
RemotePackages &addPackages(Repo repo, std::vector<PackageInfoV2> packages);
|
||||
|
||||
const std::list<PackagesWithRepo> &getRepoPackages() const { return repoPackages; }
|
||||
|
||||
bool empty() const { return repoPackages.empty(); }
|
||||
|
||||
const utils::error::Result<PackageWithRepo> getLatestPackage() const;
|
||||
std::vector<std::string> getReferenceModules(const package::Reference &ref) const;
|
||||
|
||||
private:
|
||||
std::list<PackagesWithRepo> repoPackages;
|
||||
};
|
||||
|
||||
} // namespace linglong::repo
|
||||
|
|
@ -251,11 +251,8 @@ RepoCache::queryLayerItem(const repoCacheQuery &query) const noexcept
|
|||
}
|
||||
|
||||
if (query.deleted) {
|
||||
if (!layer.deleted) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (query.deleted.value() != layer.deleted.value()) {
|
||||
auto layerDeleted = layer.deleted.value_or(false);
|
||||
if (query.deleted.value() != layerDeleted) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ pfl_add_executable(
|
|||
src/linglong/package/uab_file_test.cpp
|
||||
src/linglong/package/version_test.cpp
|
||||
src/linglong/package/versionv2_test.cpp
|
||||
src/linglong/package_manager/action_test.cpp
|
||||
src/linglong/package_manager/uab_installation_test.cpp
|
||||
src/linglong/repo/client_factory_test.cpp
|
||||
src/linglong/repo/config_test.cpp
|
||||
|
|
|
|||
|
|
@ -0,0 +1,309 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "../../common/tempdir.h"
|
||||
#include "linglong/package_manager/action.h"
|
||||
#include "linglong/package_manager/package_manager.h"
|
||||
#include "linglong/repo/ostree_repo.h"
|
||||
#include "linglong/runtime/container_builder.h"
|
||||
#include "ocppi/cli/crun/Crun.hpp"
|
||||
|
||||
namespace linglong::runtime {
|
||||
class ContainerBuilder;
|
||||
}
|
||||
|
||||
namespace linglong::service {
|
||||
class PackageTask;
|
||||
}
|
||||
|
||||
using namespace linglong;
|
||||
using ::testing::_;
|
||||
using ::testing::Return;
|
||||
|
||||
class MockRepo : public repo::OSTreeRepo
|
||||
{
|
||||
public:
|
||||
MockRepo(const std::filesystem::path &path)
|
||||
: repo::OSTreeRepo(
|
||||
QDir(path.c_str()),
|
||||
api::types::v1::RepoConfigV2{ .defaultRepo = "", .repos = {}, .version = 2 })
|
||||
{
|
||||
}
|
||||
|
||||
MOCK_METHOD(utils::error::Result<std::vector<api::types::v1::RepositoryCacheLayersItem>>,
|
||||
listLocalBy,
|
||||
(const repo::repoCacheQuery &query),
|
||||
(override, const, noexcept));
|
||||
};
|
||||
|
||||
class MockAction : public service::Action
|
||||
{
|
||||
public:
|
||||
MockAction(service::PackageManager &pm,
|
||||
repo::OSTreeRepo &repo,
|
||||
api::types::v1::CommonOptions opts)
|
||||
: service::Action(pm, repo, opts)
|
||||
{
|
||||
}
|
||||
|
||||
utils::error::Result<service::ActionOperation>
|
||||
testActionOperation(const api::types::v1::PackageInfoV2 &target, bool extraModuleOnly)
|
||||
{
|
||||
return getActionOperation(target, extraModuleOnly);
|
||||
}
|
||||
|
||||
utils::error::Result<void> prepare() override { return utils::error::Result<void>(); }
|
||||
|
||||
utils::error::Result<void> doAction([[maybe_unused]] service::PackageTask &task) override
|
||||
{
|
||||
return utils::error::Result<void>();
|
||||
}
|
||||
|
||||
std::string getTaskName() const override { return "mock_action"; }
|
||||
};
|
||||
|
||||
class ActionTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
void SetUp() override
|
||||
{
|
||||
tempDir = std::make_unique<TempDir>();
|
||||
repo = std::make_unique<MockRepo>(tempDir->path());
|
||||
cli = ocppi::cli::crun::Crun::New(tempDir->path()).value();
|
||||
containerBuilder = std::make_unique<runtime::ContainerBuilder>(*cli);
|
||||
pm = std::make_unique<service::PackageManager>(*repo, *containerBuilder, nullptr);
|
||||
mockAction = std::make_unique<MockAction>(*pm, *repo, api::types::v1::CommonOptions());
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
mockAction.reset();
|
||||
pm.reset();
|
||||
containerBuilder.reset();
|
||||
cli.reset();
|
||||
repo.reset();
|
||||
tempDir.reset();
|
||||
}
|
||||
|
||||
std::unique_ptr<TempDir> tempDir;
|
||||
std::unique_ptr<ocppi::cli::crun::Crun> cli;
|
||||
std::unique_ptr<runtime::ContainerBuilder> containerBuilder;
|
||||
std::unique_ptr<MockRepo> repo;
|
||||
std::unique_ptr<service::PackageManager> pm;
|
||||
std::unique_ptr<MockAction> mockAction;
|
||||
};
|
||||
|
||||
TEST_F(ActionTest, InstallNewApp)
|
||||
{
|
||||
api::types::v1::PackageInfoV2 packageInfo = {
|
||||
.arch = std::vector<std::string>{ "x86_64" },
|
||||
.channel = "main",
|
||||
.id = "id1",
|
||||
.kind = "app",
|
||||
.packageInfoV2Module = "binary",
|
||||
.version = "1.0.0",
|
||||
};
|
||||
|
||||
EXPECT_CALL(*repo, listLocalBy(_))
|
||||
.WillOnce(Return(std::vector<api::types::v1::RepositoryCacheLayersItem>{}));
|
||||
|
||||
auto result = mockAction->testActionOperation(packageInfo, false);
|
||||
ASSERT_TRUE(result.has_value());
|
||||
EXPECT_EQ(result->operation, service::ActionOperation::Policy::Install);
|
||||
EXPECT_EQ(result->kind, "app");
|
||||
EXPECT_EQ(result->oldRef, std::nullopt);
|
||||
EXPECT_EQ(result->newRef->reference.toString(), "main:id1/1.0.0/x86_64");
|
||||
}
|
||||
|
||||
TEST_F(ActionTest, OverwriteApp)
|
||||
{
|
||||
api::types::v1::PackageInfoV2 packageInfo = {
|
||||
.arch = std::vector<std::string>{ "x86_64" },
|
||||
.channel = "main",
|
||||
.id = "id1",
|
||||
.kind = "app",
|
||||
.packageInfoV2Module = "binary",
|
||||
.version = "1.0.0",
|
||||
};
|
||||
|
||||
EXPECT_CALL(*repo, listLocalBy(_))
|
||||
.WillOnce(Return(std::vector<api::types::v1::RepositoryCacheLayersItem>{
|
||||
api::types::v1::RepositoryCacheLayersItem{
|
||||
.info = packageInfo,
|
||||
} }));
|
||||
|
||||
auto result = mockAction->testActionOperation(packageInfo, false);
|
||||
ASSERT_TRUE(result.has_value());
|
||||
EXPECT_EQ(result->operation, service::ActionOperation::Policy::Overwrite);
|
||||
EXPECT_EQ(result->kind, "app");
|
||||
EXPECT_EQ(result->oldRef->toString(), "main:id1/1.0.0/x86_64");
|
||||
EXPECT_EQ(result->newRef->reference.toString(), "main:id1/1.0.0/x86_64");
|
||||
}
|
||||
|
||||
TEST_F(ActionTest, UpgradeApp)
|
||||
{
|
||||
api::types::v1::PackageInfoV2 packageInfo = {
|
||||
.arch = std::vector<std::string>{ "x86_64" },
|
||||
.channel = "main",
|
||||
.id = "id1",
|
||||
.kind = "app",
|
||||
.packageInfoV2Module = "binary",
|
||||
.version = "2.0.0",
|
||||
};
|
||||
|
||||
EXPECT_CALL(*repo, listLocalBy(_))
|
||||
.WillOnce(Return(std::vector<api::types::v1::RepositoryCacheLayersItem>{
|
||||
api::types::v1::RepositoryCacheLayersItem{ .info = {
|
||||
.arch = std::vector<std::string>{ "x86_64" },
|
||||
.channel = "main",
|
||||
.id = "id1",
|
||||
.kind = "app",
|
||||
.packageInfoV2Module = "binary",
|
||||
.version = "1.0.0",
|
||||
} } }));
|
||||
|
||||
auto result = mockAction->testActionOperation(packageInfo, false);
|
||||
ASSERT_TRUE(result.has_value());
|
||||
EXPECT_EQ(result->operation, service::ActionOperation::Policy::Upgrade);
|
||||
EXPECT_EQ(result->kind, "app");
|
||||
EXPECT_EQ(result->oldRef->toString(), "main:id1/1.0.0/x86_64");
|
||||
EXPECT_EQ(result->newRef->reference.toString(), "main:id1/2.0.0/x86_64");
|
||||
}
|
||||
|
||||
TEST_F(ActionTest, DowngradeApp)
|
||||
{
|
||||
api::types::v1::PackageInfoV2 packageInfo = {
|
||||
.arch = std::vector<std::string>{ "x86_64" },
|
||||
.channel = "main",
|
||||
.id = "id1",
|
||||
.kind = "app",
|
||||
.packageInfoV2Module = "binary",
|
||||
.version = "1.0.0",
|
||||
};
|
||||
|
||||
EXPECT_CALL(*repo, listLocalBy(_))
|
||||
.WillOnce(Return(std::vector<api::types::v1::RepositoryCacheLayersItem>{
|
||||
api::types::v1::RepositoryCacheLayersItem{ .info = {
|
||||
.arch = std::vector<std::string>{ "x86_64" },
|
||||
.channel = "main",
|
||||
.id = "id1",
|
||||
.kind = "app",
|
||||
.packageInfoV2Module = "binary",
|
||||
.version = "2.0.0",
|
||||
} } }));
|
||||
|
||||
auto result = mockAction->testActionOperation(packageInfo, false);
|
||||
ASSERT_TRUE(result.has_value());
|
||||
EXPECT_EQ(result->operation, service::ActionOperation::Policy::Downgrade);
|
||||
EXPECT_EQ(result->kind, "app");
|
||||
EXPECT_EQ(result->oldRef->toString(), "main:id1/2.0.0/x86_64");
|
||||
EXPECT_EQ(result->newRef->reference.toString(), "main:id1/1.0.0/x86_64");
|
||||
}
|
||||
|
||||
TEST_F(ActionTest, InstallLowVersionRuntime)
|
||||
{
|
||||
api::types::v1::PackageInfoV2 packageInfo = {
|
||||
.arch = std::vector<std::string>{ "x86_64" },
|
||||
.channel = "main",
|
||||
.id = "id1",
|
||||
.kind = "runtime",
|
||||
.packageInfoV2Module = "binary",
|
||||
.version = "1.0.0",
|
||||
};
|
||||
|
||||
EXPECT_CALL(*repo, listLocalBy(_))
|
||||
.WillOnce(Return(std::vector<api::types::v1::RepositoryCacheLayersItem>{
|
||||
api::types::v1::RepositoryCacheLayersItem{ .info = {
|
||||
.arch = std::vector<std::string>{ "x86_64" },
|
||||
.channel = "main",
|
||||
.id = "id1",
|
||||
.kind = "runtime",
|
||||
.packageInfoV2Module = "binary",
|
||||
.version = "2.0.0",
|
||||
} } }));
|
||||
|
||||
auto result = mockAction->testActionOperation(packageInfo, false);
|
||||
ASSERT_TRUE(result.has_value());
|
||||
EXPECT_EQ(result->operation, service::ActionOperation::Policy::Install);
|
||||
EXPECT_EQ(result->kind, "runtime");
|
||||
EXPECT_EQ(result->oldRef, std::nullopt);
|
||||
EXPECT_EQ(result->newRef->reference.toString(), "main:id1/1.0.0/x86_64");
|
||||
}
|
||||
|
||||
TEST_F(ActionTest, InstallHighVersionRuntime)
|
||||
{
|
||||
api::types::v1::PackageInfoV2 packageInfo = {
|
||||
.arch = std::vector<std::string>{ "x86_64" },
|
||||
.channel = "main",
|
||||
.id = "id1",
|
||||
.kind = "runtime",
|
||||
.packageInfoV2Module = "binary",
|
||||
.version = "2.0.0",
|
||||
};
|
||||
|
||||
EXPECT_CALL(*repo, listLocalBy(_))
|
||||
.WillOnce(Return(std::vector<api::types::v1::RepositoryCacheLayersItem>{
|
||||
api::types::v1::RepositoryCacheLayersItem{ .info = {
|
||||
.arch = std::vector<std::string>{ "x86_64" },
|
||||
.channel = "main",
|
||||
.id = "id1",
|
||||
.kind = "runtime",
|
||||
.packageInfoV2Module = "binary",
|
||||
.version = "1.0.0",
|
||||
} } }));
|
||||
|
||||
auto result = mockAction->testActionOperation(packageInfo, false);
|
||||
ASSERT_TRUE(result.has_value());
|
||||
EXPECT_EQ(result->operation, service::ActionOperation::Policy::Install);
|
||||
EXPECT_EQ(result->kind, "runtime");
|
||||
EXPECT_EQ(result->oldRef, std::nullopt);
|
||||
EXPECT_EQ(result->newRef->reference.toString(), "main:id1/2.0.0/x86_64");
|
||||
}
|
||||
|
||||
TEST_F(ActionTest, InstallSameRuntime)
|
||||
{
|
||||
api::types::v1::PackageInfoV2 packageInfo = {
|
||||
.arch = std::vector<std::string>{ "x86_64" },
|
||||
.channel = "main",
|
||||
.id = "id1",
|
||||
.kind = "runtime",
|
||||
.packageInfoV2Module = "binary",
|
||||
.version = "1.0.0",
|
||||
};
|
||||
|
||||
EXPECT_CALL(*repo, listLocalBy(_)).WillOnce(Return(std::vector<api::types::v1::RepositoryCacheLayersItem>{
|
||||
api::types::v1::RepositoryCacheLayersItem{
|
||||
.info = {
|
||||
.arch = std::vector<std::string>{ "x86_64" },
|
||||
.channel = "main",
|
||||
.id = "id1",
|
||||
.kind = "runtime",
|
||||
.packageInfoV2Module = "binary",
|
||||
.version = "2.0.0",
|
||||
}
|
||||
},
|
||||
api::types::v1::RepositoryCacheLayersItem{
|
||||
.info = {
|
||||
.arch = std::vector<std::string>{ "x86_64" },
|
||||
.channel = "main",
|
||||
.id = "id1",
|
||||
.kind = "runtime",
|
||||
.packageInfoV2Module = "binary",
|
||||
.version = "1.0.0",
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
auto result = mockAction->testActionOperation(packageInfo, false);
|
||||
ASSERT_TRUE(result.has_value());
|
||||
EXPECT_EQ(result->operation, service::ActionOperation::Policy::Overwrite);
|
||||
EXPECT_EQ(result->kind, "runtime");
|
||||
EXPECT_EQ(result->oldRef->toString(), "main:id1/1.0.0/x86_64");
|
||||
EXPECT_EQ(result->newRef->reference.toString(), "main:id1/1.0.0/x86_64");
|
||||
}
|
||||
|
|
@ -249,137 +249,6 @@ TEST(CheckUABLayersConstrain, ExtraModuleWithBinary)
|
|||
EXPECT_TRUE(result.has_value());
|
||||
}
|
||||
|
||||
TEST(GetTaskAction, InstallNewApp)
|
||||
{
|
||||
LINGLONG_TRACE("InstallNewApp");
|
||||
|
||||
TempDir tempDir;
|
||||
MockOSTreeRepo mockRepo(tempDir.path());
|
||||
repo::OSTreeRepo &repo = mockRepo;
|
||||
|
||||
std::vector<api::types::v1::UabLayer> appLayers = {
|
||||
create_layer("id1", "1.0.0", "main", "app")
|
||||
};
|
||||
std::vector<api::types::v1::UabLayer> otherLayers;
|
||||
|
||||
EXPECT_CALL(mockRepo, latestLocalReference(_)).WillOnce(Return(LINGLONG_ERR("")));
|
||||
|
||||
auto result =
|
||||
UabInstallationAction::getTaskAction(repo, std::make_pair(appLayers, otherLayers));
|
||||
ASSERT_TRUE(result.has_value());
|
||||
EXPECT_EQ(result->policy, service::TaskAction::Policy::Install);
|
||||
}
|
||||
|
||||
TEST(GetTaskAction, OverwriteApp)
|
||||
{
|
||||
TempDir tempDir;
|
||||
MockOSTreeRepo mockRepo(tempDir.path());
|
||||
repo::OSTreeRepo &repo = mockRepo;
|
||||
auto currentArch = package::Architecture::currentCPUArchitecture()->toStdString();
|
||||
|
||||
std::vector<api::types::v1::UabLayer> appLayers = {
|
||||
create_layer("id1", "1.0.0", "main", "app")
|
||||
};
|
||||
std::vector<api::types::v1::UabLayer> otherLayers;
|
||||
|
||||
auto localRef = package::Reference::parse("main:id1/1.0.0/" + currentArch);
|
||||
ASSERT_TRUE(localRef.has_value());
|
||||
EXPECT_CALL(mockRepo, latestLocalReference(_)).WillOnce(Return(*localRef));
|
||||
|
||||
auto result =
|
||||
UabInstallationAction::getTaskAction(repo, std::make_pair(appLayers, otherLayers));
|
||||
ASSERT_TRUE(result.has_value());
|
||||
EXPECT_EQ(result->policy, service::TaskAction::Policy::Overwrite);
|
||||
}
|
||||
|
||||
TEST(GetTaskAction, UpgradeApp)
|
||||
{
|
||||
TempDir tempDir;
|
||||
MockOSTreeRepo mockRepo(tempDir.path());
|
||||
repo::OSTreeRepo &repo = mockRepo;
|
||||
auto currentArch = package::Architecture::currentCPUArchitecture()->toStdString();
|
||||
|
||||
std::vector<api::types::v1::UabLayer> appLayers = {
|
||||
create_layer("id1", "2.0.0", "main", "app")
|
||||
};
|
||||
std::vector<api::types::v1::UabLayer> otherLayers;
|
||||
|
||||
auto localRef = package::Reference::parse("main:id1/1.0.0/" + currentArch);
|
||||
ASSERT_TRUE(localRef.has_value());
|
||||
EXPECT_CALL(mockRepo, latestLocalReference(_)).WillOnce(Return(*localRef));
|
||||
|
||||
auto result =
|
||||
UabInstallationAction::getTaskAction(repo, std::make_pair(appLayers, otherLayers));
|
||||
ASSERT_TRUE(result.has_value());
|
||||
EXPECT_EQ(result->policy, service::TaskAction::Policy::Upgrade);
|
||||
}
|
||||
|
||||
TEST(GetTaskAction, DowngradeApp)
|
||||
{
|
||||
TempDir tempDir;
|
||||
MockOSTreeRepo mockRepo(tempDir.path());
|
||||
repo::OSTreeRepo &repo = mockRepo;
|
||||
auto currentArch = package::Architecture::currentCPUArchitecture()->toStdString();
|
||||
|
||||
std::vector<api::types::v1::UabLayer> appLayers = {
|
||||
create_layer("id1", "1.0.0", "main", "app")
|
||||
};
|
||||
std::vector<api::types::v1::UabLayer> otherLayers;
|
||||
|
||||
auto localRef = package::Reference::parse("main:id1/2.0.0/" + currentArch);
|
||||
ASSERT_TRUE(localRef.has_value());
|
||||
EXPECT_CALL(mockRepo, latestLocalReference(_)).WillOnce(Return(*localRef));
|
||||
|
||||
auto result =
|
||||
UabInstallationAction::getTaskAction(repo, std::make_pair(appLayers, otherLayers));
|
||||
ASSERT_TRUE(result.has_value());
|
||||
EXPECT_EQ(result->policy, service::TaskAction::Policy::Downgrade);
|
||||
}
|
||||
|
||||
TEST(GetTaskAction, InstallLowVersionRuntime)
|
||||
{
|
||||
TempDir tempDir;
|
||||
MockOSTreeRepo mockRepo(tempDir.path());
|
||||
repo::OSTreeRepo &repo = mockRepo;
|
||||
auto currentArch = package::Architecture::currentCPUArchitecture()->toStdString();
|
||||
|
||||
std::vector<api::types::v1::UabLayer> appLayers;
|
||||
std::vector<api::types::v1::UabLayer> otherLayers = {
|
||||
create_layer("runtime.id1", "2.0.0", "main", "runtime")
|
||||
};
|
||||
|
||||
auto localRef = package::Reference::parse("main:runtime.id1/1.0.0/" + currentArch);
|
||||
ASSERT_TRUE(localRef.has_value());
|
||||
EXPECT_CALL(mockRepo, latestLocalReference(_)).WillOnce(Return(*localRef));
|
||||
|
||||
auto result =
|
||||
UabInstallationAction::getTaskAction(repo, std::make_pair(appLayers, otherLayers));
|
||||
ASSERT_TRUE(result.has_value());
|
||||
EXPECT_EQ(result->policy, service::TaskAction::Policy::Install);
|
||||
}
|
||||
|
||||
TEST(GetTaskAction, InstallHighVersionRuntime)
|
||||
{
|
||||
TempDir tempDir;
|
||||
MockOSTreeRepo mockRepo(tempDir.path());
|
||||
repo::OSTreeRepo &repo = mockRepo;
|
||||
auto currentArch = package::Architecture::currentCPUArchitecture()->toStdString();
|
||||
|
||||
std::vector<api::types::v1::UabLayer> appLayers;
|
||||
std::vector<api::types::v1::UabLayer> otherLayers = {
|
||||
create_layer("runtime.id1", "1.0.0", "main", "runtime")
|
||||
};
|
||||
|
||||
auto localRef = package::Reference::parse("main:runtime.id1/2.0.0/" + currentArch);
|
||||
ASSERT_TRUE(localRef.has_value());
|
||||
EXPECT_CALL(mockRepo, latestLocalReference(_)).WillOnce(Return(*localRef));
|
||||
|
||||
auto result =
|
||||
UabInstallationAction::getTaskAction(repo, std::make_pair(appLayers, otherLayers));
|
||||
ASSERT_TRUE(result.has_value());
|
||||
EXPECT_EQ(result->policy, service::TaskAction::Policy::Install);
|
||||
}
|
||||
|
||||
// checkExecModeUABLayers tests
|
||||
TEST(CheckExecModeUABLayers, NoAppLayers)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -430,6 +430,249 @@ TEST(OSTreeRepoTest, searchRemote_RemoteError)
|
|||
EXPECT_FALSE(result.has_value());
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class OSTreeRepoMock : public repo::OSTreeRepo
|
||||
{
|
||||
public:
|
||||
OSTreeRepoMock(const std::filesystem::path &path)
|
||||
: repo::OSTreeRepo(
|
||||
QDir(path.c_str()),
|
||||
api::types::v1::RepoConfigV2{ .defaultRepo = "", .repos = {}, .version = 2 })
|
||||
{
|
||||
}
|
||||
|
||||
MOCK_METHOD(utils::error::Result<std::vector<api::types::v1::PackageInfoV2>>,
|
||||
searchRemote,
|
||||
(const package::FuzzyReference &fuzzyRef,
|
||||
const api::types::v1::Repo &repo,
|
||||
bool semanticMatching),
|
||||
(override, const, noexcept));
|
||||
|
||||
MOCK_METHOD(std::vector<std::vector<api::types::v1::Repo>>,
|
||||
getPriorityGroupedRepos,
|
||||
(),
|
||||
(override, const, noexcept));
|
||||
};
|
||||
|
||||
TEST(OSTreeRepoTest, matchRemoteByPriority_SpecifiedRepo)
|
||||
{
|
||||
TempDir tempDir;
|
||||
OSTreeRepoMock mockRepo(tempDir.path());
|
||||
repo::OSTreeRepo &repo = mockRepo;
|
||||
|
||||
auto fuzzyRef = package::FuzzyReference::parse("com.example.app");
|
||||
auto repoConfig = api::types::v1::Repo{ .name = "test", .url = "http://localhost:8080" };
|
||||
|
||||
EXPECT_CALL(mockRepo, searchRemote(_, _, true))
|
||||
.WillOnce(Return(std::vector<api::types::v1::PackageInfoV2>{
|
||||
api::types::v1::PackageInfoV2{ .id = "com.example.app", .version = "1.0.0" },
|
||||
}));
|
||||
|
||||
auto result = repo.matchRemoteByPriority(*fuzzyRef, repoConfig);
|
||||
|
||||
EXPECT_TRUE(result.has_value());
|
||||
EXPECT_FALSE(result->empty());
|
||||
const auto &repoPackages = result->getRepoPackages();
|
||||
EXPECT_EQ(repoPackages.size(), 1);
|
||||
EXPECT_EQ(repoPackages.front().first.name, "test");
|
||||
EXPECT_EQ(repoPackages.front().second[0].id, "com.example.app");
|
||||
EXPECT_EQ(repoPackages.front().second[0].version, "1.0.0");
|
||||
}
|
||||
|
||||
TEST(OSTreeRepoTest, matchRemoteByPriority_NoRepoSpecified_Success)
|
||||
{
|
||||
TempDir tempDir;
|
||||
OSTreeRepoMock mockRepo(tempDir.path());
|
||||
repo::OSTreeRepo &repo = mockRepo;
|
||||
|
||||
auto fuzzyRef = package::FuzzyReference::parse("com.example.app");
|
||||
|
||||
EXPECT_CALL(mockRepo, getPriorityGroupedRepos())
|
||||
.WillOnce(Return(std::vector<std::vector<api::types::v1::Repo>>{
|
||||
std::vector<api::types::v1::Repo>{
|
||||
api::types::v1::Repo{ .name = "repo1", .priority = 2, .url = "http://localhost:8081" },
|
||||
},
|
||||
std::vector<api::types::v1::Repo>{
|
||||
api::types::v1::Repo{ .name = "repo2", .priority = 1, .url = "http://localhost:8081" },
|
||||
api::types::v1::Repo{ .name = "repo3", .priority = 1, .url = "http://localhost:8082" } },
|
||||
}));
|
||||
|
||||
EXPECT_CALL(mockRepo, searchRemote(_, _, true))
|
||||
.WillOnce(Return(std::vector<api::types::v1::PackageInfoV2>{
|
||||
api::types::v1::PackageInfoV2{ .id = "com.example.app", .version = "1.0.0" },
|
||||
}));
|
||||
|
||||
auto result = repo.matchRemoteByPriority(*fuzzyRef);
|
||||
|
||||
EXPECT_TRUE(result.has_value());
|
||||
EXPECT_FALSE(result->empty());
|
||||
const auto &repoPackages = result->getRepoPackages();
|
||||
EXPECT_EQ(repoPackages.size(), 1);
|
||||
EXPECT_EQ(repoPackages.front().first.name, "repo1");
|
||||
EXPECT_EQ(repoPackages.front().second[0].id, "com.example.app");
|
||||
EXPECT_EQ(repoPackages.front().second[0].version, "1.0.0");
|
||||
}
|
||||
|
||||
TEST(OSTreeRepoTest, matchRemoteByPriority_FallbackToLowerPriority)
|
||||
{
|
||||
TempDir tempDir;
|
||||
OSTreeRepoMock mockRepo(tempDir.path());
|
||||
repo::OSTreeRepo &repo = mockRepo;
|
||||
|
||||
auto fuzzyRef = package::FuzzyReference::parse("com.example.app");
|
||||
|
||||
EXPECT_CALL(mockRepo, getPriorityGroupedRepos())
|
||||
.WillOnce(Return(std::vector<std::vector<api::types::v1::Repo>>{
|
||||
std::vector<api::types::v1::Repo>{
|
||||
api::types::v1::Repo{ .name = "repo1", .priority = 2, .url = "http://localhost:8081" },
|
||||
},
|
||||
std::vector<api::types::v1::Repo>{
|
||||
api::types::v1::Repo{ .name = "repo2", .priority = 1, .url = "http://localhost:8081" },
|
||||
api::types::v1::Repo{ .name = "repo3", .priority = 1, .url = "http://localhost:8082" } },
|
||||
}));
|
||||
|
||||
EXPECT_CALL(mockRepo, searchRemote(_, _, true))
|
||||
.WillOnce(Return(std::vector<api::types::v1::PackageInfoV2>{}))
|
||||
.WillOnce(Return(std::vector<api::types::v1::PackageInfoV2>{
|
||||
api::types::v1::PackageInfoV2{ .id = "com.example.app", .version = "1.0.0" },
|
||||
}))
|
||||
.WillOnce(Return(std::vector<api::types::v1::PackageInfoV2>{}));
|
||||
|
||||
auto result = repo.matchRemoteByPriority(*fuzzyRef);
|
||||
|
||||
EXPECT_TRUE(result.has_value());
|
||||
EXPECT_FALSE(result->empty());
|
||||
const auto &repoPackages = result->getRepoPackages();
|
||||
EXPECT_EQ(repoPackages.size(), 1);
|
||||
EXPECT_EQ(repoPackages.front().first.name, "repo2");
|
||||
EXPECT_EQ(repoPackages.front().second[0].id, "com.example.app");
|
||||
EXPECT_EQ(repoPackages.front().second[0].version, "1.0.0");
|
||||
}
|
||||
|
||||
TEST(OSTreeRepoTest, matchRemoteByPriority_AllReposEmpty)
|
||||
{
|
||||
TempDir tempDir;
|
||||
OSTreeRepoMock mockRepo(tempDir.path());
|
||||
repo::OSTreeRepo &repo = mockRepo;
|
||||
|
||||
auto fuzzyRef = package::FuzzyReference::parse("com.example.app");
|
||||
|
||||
EXPECT_CALL(mockRepo, getPriorityGroupedRepos())
|
||||
.WillOnce(Return(std::vector<std::vector<api::types::v1::Repo>>{
|
||||
std::vector<api::types::v1::Repo>{
|
||||
api::types::v1::Repo{ .name = "repo1", .priority = 2, .url = "http://localhost:8081" },
|
||||
},
|
||||
std::vector<api::types::v1::Repo>{
|
||||
api::types::v1::Repo{ .name = "repo2", .priority = 1, .url = "http://localhost:8081" },
|
||||
api::types::v1::Repo{ .name = "repo3", .priority = 1, .url = "http://localhost:8082" } },
|
||||
}));
|
||||
|
||||
EXPECT_CALL(mockRepo, searchRemote(_, _, true))
|
||||
.WillOnce(Return(std::vector<api::types::v1::PackageInfoV2>{}))
|
||||
.WillOnce(Return(std::vector<api::types::v1::PackageInfoV2>{}))
|
||||
.WillOnce(Return(std::vector<api::types::v1::PackageInfoV2>{}));
|
||||
|
||||
auto result = repo.matchRemoteByPriority(*fuzzyRef);
|
||||
|
||||
EXPECT_TRUE(result.has_value());
|
||||
EXPECT_TRUE(result->empty());
|
||||
}
|
||||
|
||||
TEST(OSTreeRepoTest, matchRemoteByPriority_AllReposError)
|
||||
{
|
||||
LINGLONG_TRACE("matchRemoteByPriority_AllReposError");
|
||||
TempDir tempDir;
|
||||
OSTreeRepoMock mockRepo(tempDir.path());
|
||||
repo::OSTreeRepo &repo = mockRepo;
|
||||
|
||||
auto fuzzyRef = package::FuzzyReference::parse("com.example.app");
|
||||
|
||||
EXPECT_CALL(mockRepo, getPriorityGroupedRepos())
|
||||
.WillOnce(Return(std::vector<std::vector<api::types::v1::Repo>>{
|
||||
std::vector<api::types::v1::Repo>{
|
||||
api::types::v1::Repo{ .name = "repo1", .priority = 2, .url = "http://localhost:8081" },
|
||||
},
|
||||
std::vector<api::types::v1::Repo>{
|
||||
api::types::v1::Repo{ .name = "repo2", .priority = 1, .url = "http://localhost:8081" },
|
||||
api::types::v1::Repo{ .name = "repo3", .priority = 1, .url = "http://localhost:8082" } },
|
||||
}));
|
||||
|
||||
EXPECT_CALL(mockRepo, searchRemote(_, _, true))
|
||||
.WillOnce(Return(LINGLONG_ERR("error")))
|
||||
.WillOnce(Return(LINGLONG_ERR("error")))
|
||||
.WillOnce(Return(LINGLONG_ERR("error")));
|
||||
|
||||
auto result = repo.matchRemoteByPriority(*fuzzyRef);
|
||||
|
||||
EXPECT_TRUE(result.has_value());
|
||||
EXPECT_TRUE(result->empty());
|
||||
}
|
||||
|
||||
TEST(OSTreeRepoTest, matchRemoteByPriority_NoReposConfigured)
|
||||
{
|
||||
TempDir tempDir;
|
||||
OSTreeRepoMock mockRepo(tempDir.path());
|
||||
repo::OSTreeRepo &repo = mockRepo;
|
||||
|
||||
auto fuzzyRef = package::FuzzyReference::parse("com.example.app");
|
||||
|
||||
EXPECT_CALL(mockRepo, getPriorityGroupedRepos())
|
||||
.WillOnce(Return(std::vector<std::vector<api::types::v1::Repo>>{}));
|
||||
|
||||
auto result = repo.matchRemoteByPriority(*fuzzyRef);
|
||||
|
||||
EXPECT_TRUE(result.has_value());
|
||||
EXPECT_TRUE(result->empty());
|
||||
}
|
||||
|
||||
TEST(OSTreeRepoTest, matchRemoteByPriority_UseHighestPriority)
|
||||
{
|
||||
TempDir tempDir;
|
||||
OSTreeRepoMock mockRepo(tempDir.path());
|
||||
repo::OSTreeRepo &repo = mockRepo;
|
||||
|
||||
auto fuzzyRef = package::FuzzyReference::parse("com.example.app");
|
||||
|
||||
EXPECT_CALL(mockRepo, getPriorityGroupedRepos())
|
||||
.WillOnce(Return(std::vector<std::vector<api::types::v1::Repo>>{
|
||||
std::vector<api::types::v1::Repo>{
|
||||
api::types::v1::Repo{ .name = "repo1", .priority = 3, .url = "http://localhost:8081" },
|
||||
},
|
||||
std::vector<api::types::v1::Repo>{
|
||||
api::types::v1::Repo{ .name = "repo2", .priority = 2, .url = "http://localhost:8081" },
|
||||
api::types::v1::Repo{ .name = "repo3", .priority = 2, .url = "http://localhost:8082" } },
|
||||
std::vector<api::types::v1::Repo>{
|
||||
api::types::v1::Repo{ .name = "repo4", .priority = 1, .url = "http://localhost:8081" },
|
||||
},
|
||||
}));
|
||||
|
||||
EXPECT_CALL(mockRepo, searchRemote(_, _, true))
|
||||
.WillOnce(Return(std::vector<api::types::v1::PackageInfoV2>{}))
|
||||
.WillOnce(Return(std::vector<api::types::v1::PackageInfoV2>{
|
||||
api::types::v1::PackageInfoV2{ .id = "com.example.app", .version = "2.0.0" },
|
||||
}))
|
||||
.WillOnce(Return(std::vector<api::types::v1::PackageInfoV2>{
|
||||
api::types::v1::PackageInfoV2{ .id = "com.example.app", .version = "3.0.0" },
|
||||
}));
|
||||
|
||||
auto result = repo.matchRemoteByPriority(*fuzzyRef, std::nullopt);
|
||||
|
||||
EXPECT_TRUE(result.has_value());
|
||||
EXPECT_FALSE(result->empty());
|
||||
|
||||
const auto &repoPackages = result->getRepoPackages();
|
||||
EXPECT_EQ(repoPackages.size(), 2);
|
||||
EXPECT_EQ(repoPackages.front().first.name, "repo2");
|
||||
EXPECT_EQ(repoPackages.front().second[0].id, "com.example.app");
|
||||
EXPECT_EQ(repoPackages.front().second[0].version, "2.0.0");
|
||||
EXPECT_EQ(repoPackages.back().first.name, "repo3");
|
||||
EXPECT_EQ(repoPackages.back().second[0].id, "com.example.app");
|
||||
EXPECT_EQ(repoPackages.back().second[0].version, "3.0.0");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
} // namespace
|
||||
|
||||
} // namespace linglong::repo::test
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ int main(int argc, char **argv)
|
|||
{
|
||||
qputenv("QT_FORCE_STDERR_LOGGING", QByteArray("1"));
|
||||
linglong::utils::global::installMessageHandler();
|
||||
linglong::utils::global::initLinyapsLogSystem(argv[0]);
|
||||
linglong::utils::global::initLinyapsLogSystem(linglong::utils::log::LogBackend::Console);
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@
|
|||
|
||||
#include "configure.h"
|
||||
#include "linglong/common/strings.h"
|
||||
#include "linglong/utils/log/log.h"
|
||||
|
||||
#include <qcoreapplication.h>
|
||||
#include <qloggingcategory.h>
|
||||
|
|
@ -158,7 +157,7 @@ LogBackend parseLogBackend(const char *backends)
|
|||
|
||||
} // namespace
|
||||
|
||||
void initLinyapsLogSystem(const char *command)
|
||||
void initLinyapsLogSystem(linglong::utils::log::LogBackend backend)
|
||||
{
|
||||
LogLevel logLevel = LogLevel::Info;
|
||||
LogBackend logBackend = LogBackend::None;
|
||||
|
|
@ -172,13 +171,7 @@ void initLinyapsLogSystem(const char *command)
|
|||
if (logBackendEnv) {
|
||||
logBackend = parseLogBackend(logBackendEnv);
|
||||
} else {
|
||||
if (command == std::string("ll-builder")) {
|
||||
logBackend = LogBackend::Console;
|
||||
} else if (command == std::string("ll-cli")) {
|
||||
logBackend = LogBackend::Journal;
|
||||
} else if (command == std::string("ll-package-manager")) {
|
||||
logBackend = LogBackend::Journal;
|
||||
}
|
||||
logBackend = backend;
|
||||
|
||||
if (isatty(STDERR_FILENO)) {
|
||||
logBackend = logBackend | LogBackend::Console;
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "linglong/utils/log/log.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include <atomic>
|
||||
|
|
@ -16,7 +18,7 @@ void applicationInitialize(bool appForceStderrLogging = false);
|
|||
void installMessageHandler();
|
||||
bool linglongInstalled();
|
||||
void cancelAllTask() noexcept;
|
||||
void initLinyapsLogSystem(const char *command);
|
||||
void initLinyapsLogSystem(linglong::utils::log::LogBackend backend);
|
||||
|
||||
class GlobalTaskControl : public QObject
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue