跳至内容
liuzhen932 的小窝
返回

使用 Forgejo 搭建私有 Cargo 包注册中心:从配置到发布的完整指南

在多人协作的 Rust 开发中,搭建私有的 Cargo 包注册中心是保护代码资产和管理内部依赖的重要需求。这不闲着没事,想着把自己写的几个 Rust 小工具整理一下,搭个私有的包注册中心。选来选去,最后决定用 Forgejo,主要是因为 Forgejo 提供了完整的包注册中心功能,支持包括 Cargo 在内的多种包管理器。

其实一开始我是想用 crates.io 的,毕竟省事。但是有些个人项目不太适合开源,而且自己搭一个注册中心感觉挺酷的,就像拥有了自己的小小生态系统。

0x00 前提条件

在开始之前,确保您已经:

0x01 创建 Cargo 索引仓库

Forgejo 的文档说 Cargo 注册中心需要一个特殊的索引仓库,名字必须是 _cargo-index。这个设计挺有意思的,相当于用 Git 仓库来存储包的元数据,充分利用了 Git 的版本控制和分布式特性。

我们这里通过 WebUI 创建索引仓库:

  1. 进入用户或组织的设置页面(git.w/user/settings/packages
  2. 在左侧目录找到「软件包」选项卡
  3. Forgejo 提供了一键创建 _cargo-index 仓库的功能,所以我们只需要点击「创建索引仓库」按钮,系统会自动创建仓库并生成必要的配置文件

0x02 镜像外部仓库

如果你已经将项目推送到了您的 Forgejo 实例,或者不打算关联仓库和软件包,则可以跳过此步骤。

  1. 在 Forgejo 右上角点击「+」按钮,选择 「开始迁移」 选项
  2. 选择来源仓库的类型,如 GitHub、GitLab 等,这样可以利用平台特定配置以获得更好的兼容性
  3. 输入源仓库 URL
  4. 设置同步周期并配置认证信息(如需要)
  5. 点击「创建仓库」,Forgejo 将自动开始首次同步

第三步:配置本地 Cargo 客户端

更新 Cargo 配置文件

配置文件看起来很简单:

# ~/.cargo/config.toml
[registry]
default = "forgejo"

[registries.forgejo]
# 推荐使用稀疏索引(Rust 1.68+ 默认)
index = "sparse+https://git.w/api/packages/liuzhen932/cargo/"

# 或使用传统 Git 索引
# index = "https://git.w/liuzhen932/_cargo-index.git"

# 如果使用 Git 索引,可能需要启用 CLI Git 获取
# [net]
# git-fetch-with-cli = true

配置过程中我们可以注意到有两种索引格式:传统的 Git 索引和新的稀疏索引。

传统格式是这样的:

# ~/.cargo/config.toml
index = "https://git.w/liuzhen932/_cargo-index.git"

稀疏索引是这样:

# ~/.cargo/config.toml
index = "sparse+https://git.w/api/packages/liuzhen932/cargo/"

好奇心驱使下,我两种都试了试。稀疏索引明显快很多,特别是在更新索引的时候。看了资料才知道,稀疏索引是 Rust 1.68 后的新特性,只下载需要的包信息,不用拉取整个索引仓库。

配置认证凭据

创建或编辑 ~/.cargo/credentials.toml 文件,然后配置认证:

# ~/.cargo/credentials.toml
[registries.myregistry]
token = "Bearer xxxxxxxxxxxxxxxxxxxxx"

请注意这里的 API Token 需要有 write:packages 权限,在生成的时候只勾选了基本权限是行不通的。

如何获取 API Token?

  1. 登录 Forgejo
  2. 进入”设置” → “应用” → “管理访问令牌”
  3. 创建新令牌,确保包含 write:packages 权限
  4. 复制生成的令牌,在前面添加 Bearer 前缀

获取 API Token

获取 API Token

0x04 发布你的第一个 Crate 包!

创建示例项目

# 创建新的 Rust 项目
cargo new my-private-crate
cd my-private-crate

配置 Cargo.toml

编辑 Cargo.toml 文件:

# Cargo.toml
[package]
name = "my-private-crate"
version = "0.1.0"
edition = "2021"
description = "A private crate for internal use"
license = "MIT"
repository = "https://git.w/liuzhen932/my-private-crate"
documentation = "https://git.w/liuzhen932/my-private-crate"

publish = ["forgejo"]

[dependencies]
tokio = { version = "1.0", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }

编写示例代码

更新 src/main.rs

// src/main.rs
//! 我的私有 Crate 示例
//!
//! 这是一个演示如何发布到私有注册中心的示例包。

use serde::{Deserialize, Serialize};

/// 配置结构体
#[derive(Debug, Serialize, Deserialize)]
pub struct Config {
    pub name: String,
    pub version: String,
    pub debug: bool,
}

impl Config {
    /// 创建新的配置实例
    pub fn new(name: String, version: String) -> Self {
        Self {
            name,
            version,
            debug: false,
        }
    }

    /// 启用调试模式
    pub fn with_debug(mut self) -> Self {
        self.debug = true;
        self
    }
}

/// 格式化配置信息
pub fn format_config(config: &Config) -> String {
    format!("{} v{} (debug: {})", config.name, config.version, config.debug)
}

fn main() {
    let config = Config::new("MyApp".to_string(), "1.0.0".to_string()).with_debug();
    println!("Config: {:#?}", config);
    println!("{}", format_config(&config));
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_config_creation() {
        let config = Config::new("test".to_string(), "1.0.0".to_string());
        assert_eq!(config.name, "test");
        assert_eq!(config.version, "1.0.0");
        assert!(!config.debug);
    }

    #[test]
    fn test_config_with_debug() {
        let config = Config::new("test".to_string(), "1.0.0".to_string())
            .with_debug();
        assert!(config.debug);
    }
}

发布到私有注册中心

# 检查包配置
cargo check

# 运行测试
cargo test

# 构建包
cargo build --release

# 发布到指定的私有注册中心
cargo publish --registry forgejo

提示发布完成后,我们可以在 Forgejo 的 WebUI 中看到新发布的包,并且可以通过 cargo search 命令来验证包是否可用。

在 WebUI 中,进入「软件包」部分,可以看到 my-private-crate 包的详细信息,包括版本、依赖关系和文档链接;我们可以点击右下角的「包设置」,将其关联到正确的仓库(如果没有推送源码,可略过):

Forgejo 包设置

Forgejo 包设置示意图

版本管理的小细节

发布了第一个版本后,我想发布一个测试版本。随手在 Cargo.toml 里写了 version = "0.1.1-test",结果 Cargo 拒绝了这个版本号。

原来 Cargo 严格遵循语义化版本规范,预发布版本必须是 0.1.1-alpha.1 这样的格式。看起来是小事,但这种一致性对生态系统很重要。

修正版本号后,发布过程变得很顺利:

- version = "0.1.1-test"
+ version = "0.1.1-alpha.1"

0x05 使用私有包

在其他项目中使用

在新项目的 Cargo.toml 中添加依赖:

# Cargo.toml
[dependencies]
my-private-crate = { version = "0.1.0", registry = "forgejo" } # 如果没有设置默认注册中心,则需要指定 registry

安装和更新

# 添加依赖
cargo add my-private-crate --registry forgejo

# 更新索引
cargo update

# 构建项目
cargo build

0x06 意外的收获

使用 cargo publish 上传的 ccb@0.1.1

使用 cargo publish 上传的 [email protected]

对包管理的新理解

这次折腾让我对 Rust 的包管理系统有了更深的理解。Cargo 的设计确实很巧妙,注册中心、索引、包仓库的分离让整个系统既灵活又高效。

相比之下,Python 的 PyPI 就显得有点臃肿,所有信息都集中在一个地方。Cargo 的分布式设计让私有部署变得相对简单。

文档的重要性

为了让包能被正确使用,我不得不认真写文档和示例。这个过程中我发现,好的 API 设计和好的文档是相辅相成的。如果文档很难写,往往说明 API 设计有问题。通常来说链式调用的 API 不仅使用起来更舒服,文档和示例也更容易写。

备份的重要性

有了自己的注册中心后,备份变成了必须考虑的问题。包数据丢失的话,依赖这些包的项目就无法正常构建了。就像我在之前的文章提到的,我使用 R2 储存软件包,这样可以确保数据的持久性和可靠性。

设置清理规则

在 Forgejo WebUI 中我们可以配置自动清理规则:

  1. 访问包设置

    • 进入组织或用户设置
    • 选择”包管理” → “清理规则”
  2. 创建清理规则

    规则类型:Cargo
    保留最新版本数:10
    保留版本匹配模式:v\d+\.\d+\.\d+$
    删除版本早于:90 天
    删除版本匹配模式:.*-alpha.*

0x07 故障排除

  1. 认证失败

    # 检查 API Token 是否正确
    curl -H "Authorization: Bearer your-token" \
         https://git.w/api/packages/liuzhen932/cargo/
  2. 索引同步问题

    • 在 Forgejo WebUI 中重建索引仓库
    • 检查 _cargo-index 仓库的访问权限
  3. 网络连接问题

    # 测试连接
    cargo search --registry forgejo --limit 1
  4. 版本冲突

    # 检查已存在的版本
    cargo search your-crate --registry forgejo
  5. 包发布失败

    • 确保 Cargo.toml 中的版本号符合语义化
    • 检查网络连接和 API Token 权限

0x08 结论

技术本身其实不复杂,关键是理解背后的设计思路。一旦理解了 Cargo 索引的工作机制,很多看似复杂的配置就变得合理了。

下次如果再有人想搭建私有 Cargo 注册中心,我会建议:

总的来说,这次折腾还是挺有收获的。现在我有了自己的包注册中心,可以更好地管理个人项目的依赖关系了。虽然过程有点曲折,但结果还是很满意的。记得定期更新 Forgejo 实例,关注安全更新,并根据使用情况调整配置参数。通过合理的规划和维护,私有 Cargo 注册中心将成为您 Rust 开发工具链中的重要组成部分。

0x09 参考资料


分享这篇文章:

上一篇
The Hidden Complexity of Email Addresses
下一篇
各厂商 DNS 的域名速查表及其介绍评测

人机验证:请刷新页面以加载评论区