2个版本
0.1.1 | 2024年4月2日 |
---|---|
0.1.0 | 2024年3月16日 |
#302 in 文件系统
67 每月下载量
160KB
2.5K SLoC
Patchify
Patchify包是一个自动更新库,为Rust应用程序提供自动更新自身的功能。
它包括将功能嵌入应用程序本身和Web-based API服务器的功能,使应用程序能够检查更新,而服务器可以完全处理这些更新。库模块的使用非常简单,只需要简单的配置和最少的代码即可启动。
可以配备自动更新功能的应用程序可以是任何持久运行的东西:Web服务器、桌面应用程序、命令行工具等。
项目有一个路线图,概述了计划发布的版本及其相关功能,并根据预期目标指示了当前状态。
具有完整的测试覆盖率,包括单元测试、集成测试和端到端测试,利用各种类型的模拟。还有有用的示例。
本README的主要部分包括
功能
需要注意的主要高阶要点是
- 客户端应用程序
- 完全自主的更新检查和升级过程
- 可配置的检查间隔
- 自动应用程序重启
- 注册和管理关键操作以编排升级的能力
- 更新状态广播器,用于应用级别的状态更新
- 使用SHA256散列验证发布文件
- 使用公钥验证HTTP响应签名
- API服务器
- 与Web服务器无关,但与Axum完全集成
- 使用Tokio Tracing记录HTTP请求和事件
- 流式传输大型发布文件以提高内存效率
- 使用私钥对HTTP响应进行签名
- 无需配置即可工作的完整且最少的示例
- 使用Figment从配置文件和环境变量中进行配置
- 使用Tokio Hyper的高性能异步HTTP服务器
- 基于强大且易于使用的Web框架Axum
- 完整的代码库文档
- 单元测试和集成测试的完整测试覆盖率
签名和验证
HTTP响应将通过服务器的私钥进行签名,使连接的客户端能够验证它们未被篡改。使用的密钥格式是Ed25519,比RSA更快更安全。
哈希和验证
服务器启动时会检查发布文件的SHA256哈希值,客户端也会验证下载文件的哈希值。这确保了文件是原始文件的准确副本,且未被篡改。
流式传输
服务器将流式传输大发布文件到客户端,这比在发送之前将整个文件读入内存更高效。这是完全可配置的。
模块
目前,提供了以下模块
这些模块被设计成可以独立使用。
客户端
client
模块提供功能,可以嵌入到应用程序中,使其具有自动更新功能。
示例
客户端模块的使用基本上如下
- 引入
patchify::client
模块。 - 创建一个新的
Updater
实例,通过一个Config
实例传入适当的配置。
这就是全部!Updater
实例将在后台以指定的间隔检查更新,并记录其活动。您可以使用提供的Updater.subscribe()
方法监听这些活动。
此功能的示例提供为examples/cli-app-*.rs
。有两个版本,这样就可以看到升级功能在实际中的运用。
要运行示例,请使用以下命令
cargo run --example cli-app-v1
cargo run --example cli-app-v2
以下要点值得关注
- 使用
tokio::signal::ctrl_c()
函数等待Ctrl-C
信号,以保持应用程序运行,以便可以观察更新过程。 - 示例允许配置应用程序名称、API服务器位置、API服务器的公钥和更新间隔。
这更详细地在本指南的端到端示例中介绍。
服务器
server
模块提供功能,可以嵌入到基于Web的API服务器中,使其能够为应用程序提供更新和相关信息的功能。除了关键功能外,它还提供了适合与Axum一起使用的端点实现。
示例
提供了一个简单但全面的如何使用服务器模块的示例,作为examples/axum-server.rs
。这个示例基本上是标准API服务器集成测试的扩展版本,依赖于所有集成测试使用的相同通用代码,但增加了接受配置的能力。它是实际服务器实现的一个很好的起点。
要运行示例,请使用以下命令
cargo run --example axum-server
以下要点值得关注
- 完整的实现逻辑可以在
tests/common/server.rs
中找到,该文件被集成测试使用。它提供了initialize()
、create_patchify_api_server()
和patchify_api_routes()
函数。 tokio::signal::ctrl_c()
函数用于等待Ctrl-C
信号,这有助于服务器干净地关闭。这不是强制性的,但是一种常见的模式,也是良好的实践。它在集成测试中使用,以确保在需要时测试服务器继续在后台运行,并在测试完成后可以将其关闭。- 示例允许配置应用程序名称、运行在上的主机和端口、包含发布文件的文件夹以及具有相关哈希的版本列表。为了使用随机分配的端口,将值设置为
0
。
默认情况下,示例中不会配置版本。要添加一些,应在配置的发布文件夹中创建发布文件,并适当地命名,然后计算哈希并将其添加到列表中。然后示例将为请求这些文件和哈希的客户提供服务。这在端到端示例中有更详细的说明。
设置
使用Patchify设置项目的过程简单且标准。您需要一个合理近期的Rust环境,在Linux机器上。目前没有超出构建标准Rust项目所需之外的特殊要求。
环境
在选择环境时应注意一些关键点
- Debian和Ubuntu是首选的Linux发行版,尽管其他发行版也应工作良好,因为没有特殊要求。
- 目前没有针对Windows原生运行或测试的目标,但计划在将来正确地支持它。在WSL上运行是可行的。
- 在MacOS上原生运行尚未测试,尽管没有已知的技术原因说明它不会工作。
配置
使用Config
结构体配置Patchify,这些结构体被传递到客户端的Updater
实例以及服务器端的Core
实例。这些内容在此处有文档说明
集成
对于客户端,只需创建一个具有合适Config
的Updater
实例即可。对于服务器,除了创建一个Core
实例外,还有选择是否与Axum集成或直接从您自己的端点函数调用Core
方法。
值得注意的是,客户端功能提供了两个主要的接触点来帮助协调升级过程:Updater.subscribe()
方法和关键操作计数器。
关键操作计数器
关键操作计数器是一个简单的计数器,可以增加和减少,用于确定在升级后何时安全地重新启动应用程序。这是一种确保应用程序在重启时不在关键操作中的简单方法。
基本前提是,如果应用程序即将进行不应被中断的操作,可以增加计数器,在操作完成后减少。然后,Updater
实例只有在计数器为零时才会重新启动应用程序。如果更新器即将重新启动应用程序,则它将拒绝启动任何新的关键操作,直到重启完成。
这使得将升级过程集成到应用程序中变得非常容易,并确保确切知道何时将重新启动。
如果需要更细致地控制重启方式,建议在应用程序启动时注册一个关键操作,并且永远不要注销它,而是依赖于状态变化事件来检测何时需要重启,并按自定义方式处理。
状态事件订阅
方法Updater.subscribe()
允许应用程序监听Updater
实例的活动,并对它广播的状态变化做出反应。这有助于更新应用程序的UI或用于日志记录。如果需要,它还提供了一种手动控制升级过程的方法。
端到端示例
无法直接提供100%正常工作的端到端示例,因为需要生成和将发布文件注册到服务器。然而,所有必要的组件都已提供,通过遵循本节中的几个简短步骤,您可以很快地设置一个完全工作的示例。
先决条件
您需要在Linux上设置Rust环境,并克隆Patchify存储库。本例中的步骤假设命令是在存储库根目录下运行的。这只是为了演示目的,在现实世界的场景中,您将使用自己的应用程序存储库,并将Patchify作为依赖项包含在内,如本README中所述。
1. 准备发布目录
假设是全新的克隆,为服务器创建一个新的目录,以便从中提供发布版本
mkdir -p /tmp/patchify-releases
2. 构建应用程序发布版本
现在我们需要编译客户端示例。虽然可以直接运行它们,但它们在测试运行时也会被编译,所以这是构建它们的简单方法,同时也确保在您的环境中所有测试都通过。
cargo test
3. 复制发布文件
测试完成后,需要将编译的二进制文件复制到发布目录
cp target/debug/examples/cli-app-v1 /tmp/patchify-releases/cli-app-1.0.0
cp target/debug/examples/cli-app-v2 /tmp/patchify-releases/cli-app-2.0.0
注意文件名的变化。这是因为Cargo希望每个示例都有一个不同的crate,而我们在这里实际上想创建两个相同应用程序的不同版本。因此,示例被命名为v1
和v2
,分别对应版本1.0.0
和2.0.0
。
为了能够在以后运行客户端应用程序,将应用程序二进制文件复制到当前目录
cp target/debug/examples/cli-app-v1 ./cli-app
再次注意文件名的变化。这是因为我们不在乎应用程序的版本,我们只想运行它。我们复制的文件将在更新程序运行时被新版本替换。
注意,如果您的本地Cargo设置用于构建的不同目录,则需要相应地调整路径。
4. 配置API服务器
现在需要运行服务器示例。这将提供我们刚刚创建的发布文件,并为客户端应用程序提供与之交互的API端点。
但是,首先我们需要一些配置。将examples/axum-server.toml
文件复制到当前工作目录
cp examples/axum-server.toml .
现在编辑它,将releases
值设置为/tmp/patchify-releases
,并添加两个版本及其关联的哈希。文件看起来应该像这样
appname = "cli-app"
host = "127.0.0.1"
port = 8000
releases = "/tmp/patchify-releases"
[versions]
"1.0.0" = "beef1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f"
"2.0.0" = "cafe1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f"
要获取哈希值,可以使用sha256sum
命令
sha256sum /tmp/patchify-releases/cli-app-1.0.0
sha256sum /tmp/patchify-releases/cli-app-2.0.0
默认情况下,服务器将在端口 8000 上运行,并从本地主机提供服务。请随意更改这些值以适应您的环境。
5. 运行API服务器
现在您可以运行服务器示例了。请注意,您需要在不同终端窗口中保持此窗口打开,因此从现在起您将有两个终端窗口打开。
cargo run --example axum-server
服务器在启动时会检查发布文件的合法性,如果存在错误,则退出。请注意,这可能需要几秒钟的时间,因为需要读取每个文件的全部内容以计算哈希值。
6. 配置客户端应用程序
将 examples/cli-app.toml
文件复制到您的当前工作目录
cp examples/cli-app.toml .
现在编辑它,并将 updater_api_server
的值设置为服务器正在运行的地址。这应该是您配置的,但服务器在启动时会打印出地址和公钥。您还需要将此密钥添加到客户端配置中,作为 updater_api_key
appname = "cli-app"
updater_api_server = "http://127.0.0.1:8000/api/"
updater_api_key = "beef1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f"
update_on_startup = false
update_interval = 10
示例服务器每次启动都会生成一个新的密钥,因此每次重启服务器时都需要从服务器的输出中复制它。这是为了使演示过程更加健壮,而不是在服务器配置中接受私钥,以防错误生成,这可能会引起挫败感。
注意,在上面的配置示例中,update_on_startup
的值设置为 false
。这是因为我们想看到更新过程,所以不希望应用程序在启动时更新自己。此外,update_interval
设置为 10
,这样应用程序将每10秒检查一次更新,这不会等待得太久。您可以随意尝试这些值,并观察行为上的差异。
7. 运行客户端应用程序
我们现在可以运行客户端应用程序了!
./cli-app
应用程序将启动并按您在配置中指定的间隔检查更新。如果有更新可用,它将下载、验证、安装它们并重新启动自己。您应该看到状态变更事件被打印到控制台,并且当应用程序重新启动时,您应该看到打印的版本号发生变化。
依赖项
~15–29MB
~473K SLoC