内网 Nexus 离线依赖搬运工具。
将 Maven、npm、PyPI、Cargo、Conan 依赖从外网下载,再导入内网 Nexus 仓库。
- 内网 Nexus 无法访问公网,需要人工搬运依赖
- 需要批量下载依赖及其所有传递依赖
- 需要可重复、可自动化的跨网依赖同步流程
- Nexus 可直接联网(应使用 Nexus 代理仓库)
- 构建时实时解析依赖(本工具是离线批量工具)
- 下载过程中需要平台特定原生编译的依赖
| 类型 | 下载 | 导入 Nexus | 说明 |
|---|---|---|---|
| Maven | ✅ | Maven 仓库 | mvn dependency:get 递归下载;按 Maven2 布局逐文件上传 |
| npm | ✅ | npm 仓库 | 打包依赖树中全部包,按 npm publish 协议发布(含传递依赖) |
| PyPI | ✅ | PyPI 仓库 | pip download + twine upload |
| Cargo | ✅ | Cargo 仓库 | 复制真实 .crate,按 Cargo registry publish 协议发布到原生 Cargo 仓库 |
| Conan | ✅ | raw 仓库 | conan install 缓存配方(Nexus 原生不支持,走 raw 兜底) |
重要:npm 与 Cargo 的导入使用各自的原生发布协议(npm publish 文档、Cargo
PUT /api/v1/crates/new), 而不是把文件堆到任意路径。这样上传后的包才能被npm install/cargo build正常解析和下载。 导入端全部通过 HTTP 实现,无需安装 npm / cargo(仅 PyPI 仍依赖twine)。
| 工具 | 用途 | 阶段 |
|---|---|---|
| Docker | 运行下载器容器 | 外网下载 |
| twine | PyPI 导入 | 内网导入(可选) |
从 GitHub Releases 下载对应平台的预编译版本:
| 平台 | 文件 |
|---|---|
| Linux x86_64 | dep-porter-linux-amd64.tar.gz |
| Linux ARM64 | dep-porter-linux-arm64.tar.gz |
| Windows x86_64 | dep-porter-windows-amd64.zip |
| macOS x86_64 | dep-porter-macos-amd64.tar.gz |
| macOS ARM64 | dep-porter-macos-arm64.tar.gz |
解压后得到 dep-porter(Windows 为 dep-porter.exe)。
# Linux/macOS
tar -xzf dep-porter-linux-amd64.tar.gz
./dep-porter --help
# Windows (PowerShell)
Expand-Archive dep-porter-windows-amd64.zip
.\dep-porter.exe --help下载时会自动拉取 Docker 镜像 gudaoxuri/dep-downloader:latest。
# Maven(正式版本)
dep-porter download --kind maven --name org.apache.commons:commons-lang3 --version 3.14.0
# Maven(SNAPSHOT 快照版本)
dep-porter download --kind maven --name org.apache.commons:commons-lang3 --version 3.15.1-SNAPSHOT
# npm
dep-porter download --kind npm --name @h-ai/serv --version 0.1.0-alpha.31
# PyPI
dep-porter download --kind pypi --name requests --version 2.32.3
# Cargo
dep-porter download --kind cargo --name tardis --version 0.1.0-rc.19
# Conan
dep-porter download --kind conan --name zlib --version 1.2.13
# 跳过安全漏洞检查(默认会检查)
dep-porter download --kind maven --name log4j:log4j --version 1.2.17 --no-check-security
# 跳过许可证商用风险检查(默认会检查)
dep-porter download --kind npm --name lodash --version 4.17.21 --no-check-license
# 自定义或关闭下载缓存(默认开启)
dep-porter download --kind npm --name lodash --version 4.17.21 --cache-dir D:\dep-cache
dep-porter download --kind npm --name lodash --version 4.17.21 --no-cache每个命令生成一个目录:{类型}_{安全名称}_{版本}/,包含所有下载的依赖及其传递依赖。导入时整个目录的所有依赖(包括传递依赖)都会上传到 Nexus。
下载时默认使用国内镜像源加速,可通过环境变量自定义:
| 环境变量 | 默认值 | 说明 |
|---|---|---|
MAVEN_MIRROR |
https://maven.aliyun.com/repository/central |
Maven 中央仓库镜像 |
NPM_MIRROR |
https://registry.npmmirror.com |
npm 镜像 |
PYPI_MIRROR |
https://mirrors.aliyun.com/pypi/simple |
PyPI 镜像 |
CARGO_MIRROR |
https://mirrors.ustc.edu.cn/crates.io-index |
Cargo 镜像 |
# 使用默认镜像源(国内加速)
dep-porter download --kind maven --name junit:junit --version 4.13.2
# 自定义镜像源
MAVEN_MIRROR=https://maven-central.storage.googleapis.com \
dep-porter download --kind maven --name junit:junit --version 4.13.2
# 使用官方源(海外服务器)
MAVEN_MIRROR=https://repo1.maven.org/maven2 \
NPM_MIRROR=https://registry.npmjs.org \
PYPI_MIRROR=https://pypi.org/simple \
CARGO_MIRROR=https://index.crates.io \
dep-porter download --kind maven --name junit:junit --version 4.13.2下载缓存默认开启,默认根目录为当前工作目录下的 .dep-porter-cache。每种包管理器使用一份固定的全局缓存(例如 Maven 使用 .dep-porter-cache/v3/maven),因此下载同一组件的不同版本或不同根依赖时,均可复用已经存在的公共依赖;切换镜像源或升级下载器镜像也不会重新分配缓存目录。
Maven 会使用“全局共享缓存 + 本次临时仓库”:解析时复用全局制品,但导出时只收集本次依赖图,历史包不会混入当前导出目录。为兼容旧版下载器镜像,同一份 Maven 缓存会同时挂载到新路径 /workspace/dep-cache 和原生路径 /root/.m2/repository。
缓存目录配置优先级为:--cache-dir > DEP_PORTER_CACHE_DIR > .dep-porter-cache。
# 使用默认缓存
dep-porter download --kind maven --name junit:junit --version 4.13.2
# 命令行指定缓存根目录
dep-porter download --kind maven --name junit:junit --version 4.13.2 --cache-dir /data/dep-cache
# 通过环境变量指定缓存根目录
DEP_PORTER_CACHE_DIR=/data/dep-cache \
dep-porter download --kind maven --name junit:junit --version 4.13.2
# 本次下载不使用持久化缓存
dep-porter download --kind maven --name junit:junit --version 4.13.2 --no-cache缓存分别接入 Maven、npm、pip、Cargo 和 Conan 的原生缓存机制。删除缓存根目录即可清空缓存,不会影响已经生成的依赖导出目录。
将以下内容拷贝到内网机器:
dep-porter二进制文件- 下载目录(如
maven_org.apache.commons_commons-lang3_3.14.0/) config.toml配置文件
创建 config.toml:
[nexus]
base_url = "http://nexus.internal.example.com"
username = "admin"
password = "admin123"
[repositories]
maven = "maven-releases"
# maven_snapshots = "maven-snapshots" # 可选,不填则 SNAPSHOT 版本也发到 maven
npm = "npm-hosted"
pypi = "pypi-hosted"
# cargo = "cargo-hosted" # 可选,不填则走 raw 兜底
raw = "raw-hosted"执行导入:
# Maven 正式版本 → 发到 maven 仓库
dep-porter import --kind maven --name org.apache.commons:commons-lang3 --version 3.14.0
# Maven SNAPSHOT → 自动发到 maven_snapshots 仓库(如果配置了的话)
dep-porter import --kind maven --name org.apache.commons:commons-lang3 --version 3.14.0-SNAPSHOT
dep-porter import --kind npm --name lodash --version 4.17.21
dep-porter import --kind pypi --name requests --version 2.32.3
# Cargo 优先走 cargo 仓库,失败自动降级到 raw
dep-porter import --kind cargo --name serde --version 1.0.203
dep-porter import --kind conan --name zlib --version 1.2.13
# 覆盖模式:已存在则覆盖(受仓库写策略限制)
dep-porter import --kind maven --name junit:junit --version 4.13.2 --overwrite
--config默认读取当前目录的config.toml,可省略。如需指定其他路径:--config /path/to/config.toml
导入前需要在 Nexus 创建以下仓库:
| 仓库类型 | 建议名称 | 格式 | 类型 | 说明 |
|---|---|---|---|---|
| Maven releases | maven-releases | maven2 | hosted | 正式版本 |
| Maven snapshots | maven-snapshots | maven2 | hosted | 快照版本(可选,不配则也发到 maven) |
| npm | npm-hosted | npm | hosted | 原生 npm 格式 |
| PyPI | pypi-hosted | pypi | hosted | 原生 pypi 格式 |
| Cargo | cargo-hosted | cargo | hosted | 原生 Cargo 格式(需 Nexus 3.74+,仅支持 sparse 协议);未配置时降级 raw |
| raw(兜底) | raw-hosted | raw | hosted | Conan 以及无 Cargo 仓库时的 fallback |
Maven:版本号包含 -SNAPSHOT 的自动发到 maven_snapshots 仓库,其余发到 maven 仓库。
Cargo:上传到原生 cargo 格式仓库(Nexus 3.74+),使用 Cargo registry publish 协议(PUT /api/v1/crates/new)逐个发布真实 .crate,发布后可通过 sparse 协议被 cargo 解析。若未配置 cargo 仓库,则降级把文件堆到 raw(此时 cargo 客户端无法使用,仅作留存)。
npm:使用 npm publish 协议发布依赖树中的每一个 tarball(目标包 + 全部传递依赖),发布后可被 npm install 正常解析。
覆盖模式:
- 默认模式(skip-if-exists):上传前先 HEAD 检查,已存在则跳过
--overwrite模式:直接 PUT 覆盖,如果仓库写策略禁止覆盖(如ALLOW_ONCE)会报错
导入完成后,内网开发机需要把包管理器指向 Nexus 才能使用这些依赖。
Cargo(在项目根或 ~/.cargo/config.toml):
[registries.nexus]
index = "sparse+http://nexus.internal.example.com/repository/cargo/"
# 让所有 crates.io 依赖都从 Nexus 解析
[source.crates-io]
replace-with = "nexus"
[source.nexus]
registry = "sparse+http://nexus.internal.example.com/repository/cargo/"Nexus Cargo 仓库需要认证,凭据以 token 形式提供(Basic base64(user:password)):
# 例如 admin:admin123 → Basic YWRtaW46YWRtaW4xMjM=
export CARGO_REGISTRIES_NEXUS_TOKEN="Basic YWRtaW46YWRtaW4xMjM="
cargo buildnpm(.npmrc):
registry=http://nexus.internal.example.com/repository/npm/
//nexus.internal.example.com/repository/npm/:_auth=BASE64(user:password)npm install # 依赖将从 Nexus 拉取Maven(~/.m2/settings.xml):把 <mirror> 指向 maven-releases/maven-public,即可解析已导入的构件。
PyPI:pip install --index-url http://user:password@nexus.internal.example.com/repository/pypi/simple/ <包名>。
{kind}_{safe_name}_{version}
safe_name 将 /、:、@、\ 替换为 _。
示例:
maven_org.apache.commons_commons-lang3_3.14.0
npm_lodash_4.17.21
pypi_requests_2.32.3
cargo_serde_1.0.203
conan_zlib_1.2.13
下载时默认开启漏洞扫描,通过 OSV.dev API 查询已知 CVE。如需跳过检查,使用 --no-check-security:
# 默认会检查安全漏洞
dep-porter download --kind maven --name log4j:log4j --version 2.14.1
# 跳过安全检查
dep-porter download --kind maven --name log4j:log4j --version 2.14.1 --no-check-security如果发现漏洞,会显示详情并提示是否继续:
=== 安全警告 ===
maven log4j:log4j@2.14.1
Found 2 known vulnerability(ies):
[1] GHSA-26fg-89j6-3q3j (CVSS: 9.8) — Apache Log4j2 Remote Code Execution
[2] CVE-2021-44228 (CVSS: 10.0) — Log4j 2.x JNDI RCE
=========================
Continue download anyway? [y/N]
- 输入
y继续下载,输入其他或直接回车取消 - 支持 Maven/npm/PyPI/Cargo,Conan 暂不支持(OSV.dev 无此生态)
下载时默认开启许可证检查,通过 deps.dev API 查询包版本的许可证:
# 默认会检查许可证
dep-porter download --kind npm --name lodash --version 4.17.21
# 跳过许可证检查
dep-porter download --kind npm --name lodash --version 4.17.21 --no-check-license- 常见宽松许可证(如 MIT、Apache-2.0、BSD)会记录检测结果并继续下载
- Copyleft 许可证会提醒可能存在的分发、修改或网络服务开源义务,并询问是否继续
- 疑似非商用、非标准、未知或缺失许可证会提醒人工审查,并询问是否继续
- 支持 Maven/npm/PyPI/Cargo;deps.dev 暂不覆盖 Conan
- 检测结论仅用于风险提示,不构成法律意见;正式商用前仍应核验许可证原文和实际使用方式
Q: 没有 Docker 能用吗? 下载阶段需要 Docker,导入阶段不需要。
Q: 内网机器没有 Docker 怎么办? Docker 只在下载阶段使用(联网机器)。导入阶段通过 HTTP 直接上传到 Nexus。
Q: 传递依赖怎么处理? 各包管理器原生解析,下载时获取完整依赖树,导入时逐个上传(包含所有传递依赖):
- Maven:
mvn dependency:get -Dtransitive=true,拷贝本地仓库整体上传 - npm:
npm install解析依赖树,对树中每个包npm pack取真实 tarball - PyPI:
pip download(含传递依赖) - Cargo:
cargo fetch拉取整个依赖图,从本地 registry 缓存拷贝真实.crate+ 保存 crates.io sparse-index 元数据 - Conan:
conan install
Q: Maven SNAPSHOT 版本怎么处理?
版本号包含 -SNAPSHOT 的自动发到 maven_snapshots 仓库(如果配置了),否则也发到 maven 仓库。下载缓存开启时仍会强制检查远端是否有更新的时间戳快照。
Q: Cargo/Conan 在 Nexus 里不支持怎么办?
Nexus 3.74+ 原生支持 Cargo(sparse 协议),在 config.toml 配置 cargo 仓库名即可。若 Nexus 无 Cargo 支持,不配 cargo,将降级到 raw 仓库兜底(仅留存,cargo 客户端无法直接使用)。Conan 始终走 raw 兜底。
Q: 能下载同一包的多个版本吗?
可以,每个版本执行一次 download,各自生成独立目录。
Q: 导入时已存在的制品怎么处理?
默认跳过(skip-if-exists),加 --overwrite 则覆盖。覆盖受 Nexus 仓库写策略限制,ALLOW_ONCE 策略下覆盖会返回 4xx 错误。
Q: 安全检查会拖慢下载速度吗?
安全检查是单次 HTTP 请求(OSV.dev API),通常 < 1 秒。使用 --no-check-security 可跳过检查。
Q: 许可证检查会阻止下载吗?
常见宽松许可证不会阻止下载。遇到 Copyleft、疑似非商用、非标准或许可证信息缺失时会询问是否继续;API 查询失败时只告警,不阻断下载。使用 --no-check-license 可跳过检查。
Q: 如何清理下载缓存?
删除 .dep-porter-cache(或 --cache-dir / DEP_PORTER_CACHE_DIR 指定的目录)即可。清理不会删除已下载的导出目录。
Q: 支持 Windows 吗? 支持。CLI 是 Rust 二进制,Windows/Linux 均可运行。Docker 路径挂载会自动处理 Windows 路径。
- 语言:Rust 2021 edition
- CLI:clap 4(derive 宏)
- 配置:serde + toml
- HTTP:reqwest(blocking)
- Docker 调用:std::process::Command
- 错误处理:anyhow + thiserror
dep-porter/
├── Cargo.toml # Rust 项目配置
├── Dockerfile.downloader # Docker 下载器镜像(多阶段构建)
├── SKILL.md # LLM 使用指南
├── config.example.toml # Nexus 配置示例
├── .github/
│ └── workflows/
│ ├── docker-publish.yml # GitHub Actions: Docker 镜像构建与发布
│ └── build-release.yml # GitHub Actions: 跨平台程序构建
├── scripts/
│ └── download.sh # 容器内下载脚本(Maven/npm/PyPI/Cargo/Conan)
├── src/
│ ├── main.rs # 入口
│ ├── cli.rs # clap 参数解析
│ ├── config.rs # TOML 配置读取
│ ├── docker.rs # Docker 调用 + 镜像自动拉取
│ ├── import.rs # Nexus 上传逻辑(含 overwrite/skip)
│ ├── registry.rs # 原生发布载荷构造(Cargo publish / npm publish)
│ ├── model.rs # 数据类型定义
│ ├── license.rs # 许可证商用风险检查(deps.dev API)
│ ├── security.rs # SCA 安全检查(OSV.dev API)
│ └── util.rs # 工具函数(目录命名、路径转换)
└── tests/
├── e2e.rs # 本地逻辑测试 + 可选 Docker/Nexus E2E
└── docker_e2e.rs # Docker E2E 测试(覆盖全部 5 种依赖类型)
cd dep-porter
cargo build --release产物:target/release/dep-porter(Windows: target\release\dep-porter.exe)
src/model.rs:在DepKind枚举中添加新变体scripts/download.sh:添加对应的download_xxx()函数和 case 分支src/import.rs:添加对应的导入逻辑(如需原生发布协议,在src/registry.rs添加可单测的载荷构造函数)tests/docker_e2e.rs:添加工具可用性测试和下载测试Dockerfile.downloader:如需新工具,在 builder/runtime 阶段安装
cargo test测试内容:目录命名、Maven 坐标解析、配置解析、错误处理等。
需要 Docker 环境:
# 运行 Docker E2E 测试(覆盖 Maven/npm/PyPI/Cargo/Conan 全部 5 种类型)
# 首次运行会自动拉取 gudaoxuri/dep-downloader:latest 镜像
$env:RUN_DOCKER_E2E="1" # PowerShell
cargo test --test docker_e2e -- --test-threads=1测试内容:镜像拉取、全部工具可用性(mvn/java/node/npm/python/pip/twine/cargo/rustc/conan)、每种依赖类型的下载功能、目录命名验证、错误处理。
Nexus E2E 测试会向真实运行的 Nexus 发布各类型构件,并验证它们能以正确的原生格式被检索/使用(即真正可用)。测试默认读取项目根目录的 config.toml,也可用 NEXUS_* 环境变量覆盖。
覆盖的验证(tests/e2e.rs):
| 测试 | 内容 |
|---|---|
test_e2e_cargo_publish_and_resolve |
发布带传递依赖的 crate,校验 sparse index 列出版本与依赖、download 端点可下载 |
test_e2e_npm_publish_and_resolve |
发布目标包与传递依赖,校验 packument 记录依赖、dist tarball 可下载 |
test_e2e_maven_publish_and_resolve |
上传 jar+pom,校验构件可检索 |
test_e2e_raw_upload |
Conan/raw 兜底上传,校验可检索 |
测试会为每次运行生成唯一版本号,避免与已发布构件冲突。
方式 A:使用项目 config.toml(推荐)
确保根目录 config.toml 指向可用的 Nexus(默认 http://localhost:8081,仓库名 maven-releases/npm/pypi/cargo/raw):
# Linux/macOS
export RUN_NEXUS_E2E=1
cargo test --test e2e -- --test-threads=1# PowerShell
$env:RUN_NEXUS_E2E="1"
cargo test --test e2e -- --test-threads=1方式 B:用环境变量覆盖仓库/凭据
export RUN_NEXUS_E2E=1
export NEXUS_BASE_URL=http://localhost:8081
export NEXUS_USERNAME=admin
export NEXUS_PASSWORD=<密码>
export NEXUS_MAVEN_REPO=maven-releases
export NEXUS_NPM_REPO=npm
export NEXUS_PYPI_REPO=pypi
export NEXUS_CARGO_REPO=cargo
export NEXUS_RAW_REPO=raw
# 可选:export NEXUS_MAVEN_SNAPSHOTS_REPO=maven-snapshots
cargo test --test e2e -- --test-threads=1若要自建测试 Nexus,需创建以下 hosted 仓库:maven2(maven-releases)、npm、pypi、cargo(format=cargo,Nexus 3.74+)、raw。
验证从下载到导入的完整流程:
# 1. 启动 Nexus
docker run -d --name nexus-test -p 8081:8081 sonatype/nexus3:latest
# 等待 1-2 分钟启动完成
# 2. 获取密码
NEXUS_PASS=$(docker exec nexus-test cat /nexus-data/admin.password)
echo "Nexus password: $NEXUS_PASS"
# 3. 创建仓库(通过 Nexus REST API)
curl -u "admin:$NEXUS_PASS" -X POST "http://localhost:8081/service/rest/v1/repositories/maven2/hosted" \
-H "Content-Type: application/json" \
-d '{"name":"maven-releases","online":true,"storage":{"blobStoreName":"default","strictContentTypeValidation":true,"writePolicy":"ALLOW_ONCE"}}'
curl -u "admin:$NEXUS_PASS" -X POST "http://localhost:8081/service/rest/v1/repositories/maven2/hosted" \
-H "Content-Type: application/json" \
-d '{"name":"maven-snapshots","online":true,"storage":{"blobStoreName":"default","strictContentTypeValidation":true,"writePolicy":"ALLOW_ONCE"},"maven":{"versionPolicy":"SNAPSHOT","layoutPolicy":"STRICT"}}'
curl -u "admin:$NEXUS_PASS" -X POST "http://localhost:8081/service/rest/v1/repositories/raw/hosted" \
-H "Content-Type: application/json" \
-d '{"name":"raw-hosted","online":true,"storage":{"blobStoreName":"default","strictContentTypeValidation":false,"writePolicy":"ALLOW_ONCE"}}'
# 4. 下载一个小型 Maven 依赖
cargo run -- download --kind maven --name junit:junit --version 4.13.2
# 5. 创建配置文件
cat > config.toml <<EOF
[nexus]
base_url = "http://localhost:8081"
username = "admin"
password = "$NEXUS_PASS"
[repositories]
maven = "maven-releases"
maven_snapshots = "maven-snapshots"
npm = "npm-hosted"
pypi = "pypi-hosted"
cargo = "cargo-hosted"
raw = "raw-hosted"
EOF
# 6. 导入到 Nexus
cargo run -- import --kind maven --name junit:junit --version 4.13.2 --config config.toml
# 7. 验证 SNAPSHOT 路由:下载一个 SNAPSHOT 版本并导入
cargo run -- download --kind maven --name org.example:my-snapshot --version 1.0.0-SNAPSHOT
cargo run -- import --kind maven --name org.example:my-snapshot --version 1.0.0-SNAPSHOT --config config.toml
# → 自动发到 maven-snapshots 仓库
# 8. 验证:在浏览器打开 http://localhost:8081
# - maven-releases 仓库有 junit:junit:4.13.2
# - maven-snapshots 仓库有 org.example:my-snapshot:1.0.0-SNAPSHOT
# 9. 清理
docker rm -f nexus-test
rm -rf maven_junit_junit_4.13.2 maven_org.example_my-snapshot_1.0.0-SNAPSHOT config.toml项目配置了 GitHub Actions 自动化构建:
当 Dockerfile.downloader 或 scripts/download.sh 有变更时,自动构建并发布到:
- Docker Hub:
gudaoxuri/dep-downloader - GitHub Container Registry:
ghcr.io/200hub/dep-porter/dep-downloader
支持手动触发(workflow_dispatch)。
当 Cargo.toml 中的 version 对应的 release 不存在时,自动构建以下平台的可执行文件:
- Linux AMD64 / ARM64
- Windows AMD64
- macOS AMD64 / ARM64
自动创建 GitHub Release,包含:
- 所有平台的二进制文件
- 自动生成的发布说明(提交记录)
发布流程:
# 1. 更新 Cargo.toml 中的 version
# version = "1.1.0"
# 2. 推送到 main/master
git add Cargo.toml
git commit -m "bump version to 1.1.0"
git push
# 3. GitHub Actions 自动:
# - 检查 v1.1.0 release 是否存在
# - 如果不存在,构建所有平台二进制文件
# - 创建 v1.1.0 标签
# - 发布 Release支持手动触发(workflow_dispatch)。