Nexus-Maven私服
Nexus-Maven私服
安装部署
Docker
## amd
docker run -d -p 8081:8081 -v D:\Documents\test\nexus3\nexus-data:/nexus-data --name nexus sonatype/nexus3
docker run -d --privileged --restart=always -p 8081:8081 --name nexus sonatype/nexus3
## arm
docker pull klo2k/nexus3:latest --platform=arm64
mkdir -p /data/soft/docker/nexus3/nexus-data
docker run -d --name nexus -p 18081:8081 -v /data/soft/docker/nexus3/nexus-data:/nexus-data klo2k/nexus3:latest
登录
http://localhost:8081/ 账号:admin 密码:在/nexus-data/admin.password中
Maven
Nexus配置
匿名访问
登录后会出现弹窗
Configure Anonymous Access 配置匿名访问
Enable anonymous access means that by default, users can search, browse and download components from repositories without credentials. Please consider the security implications for your organization.
启用匿名访问意味着默认情况下,用户无需凭证即可从存储库搜索、浏览和下载组件。请考虑贵组织的安全隐患。
Disable anonymous access should be chosen with care, as it will require credentials for all users and/or build tools.
禁用匿名访问应谨慎选择,因为它将需要所有用户和/或构建工具的凭据。
Enable anonymous access 启用匿名访问
Disable anonymous access 禁用匿名访问
创建仓库
左侧 Browse 会显示默认的仓库
默认仓库说明
- maven-central:maven中央库,默认从https://repo1.maven.org/maven2/ 拉取jar
- maven-releases:私库发行版jar,初次安装请将Deployment policy设置为Allow redeploy
- maven-snapshots:私库快照(调试版本)jar
- maven-public:仓库分组,把上面三个仓库组合在一起对外提供服务,在本地maven基础配置settings.xml或项目pom.xml中使用 仓库类型
- Group:这是一个仓库聚合的概念,用户仓库地址选择Group的地址,即可访问Group中配置的,用于方便开发人员自己设定的仓库。maven-public就是一个Group类型的仓库,内部设置了多个仓库,访问顺序取决于配置顺序,3.x默认Releases,Snapshots, Central,当然你也可以自己设置。
- Hosted:私有仓库,内部项目的发布仓库,专门用来存储我们自己生成的jar文件
- 3rd party:未发布到公网的第三方jar (3.x去除了)
- Snapshots:本地项目的快照仓库
- Releases: 本地项目发布的正式版本
- Proxy:代理类型,从远程中央仓库中寻找数据的仓库(可以点击对应的仓库的Configuration页签下Remote Storage属性的值即被代理的远程仓库的路径),如可配置阿里云maven仓库
- Central:中央仓库
- Apache Snapshots:Apache专用快照仓库(3.x去除了)
创建仓库
点上方设置⚙️
左侧菜单 Repositories --> + create repository
选择类型,如maven2,设置阿里的代理仓库为例,就选择 maven2(proxy)
配置 --- 这里配置阿里的代理,只需要配置名称和URL,其他默认即可
此存储库的唯一标识符,这里填aliyun Name: A unique identifier for this repository 如果选中,存储库将接受传入请求 Onlien: If checked, the repository accepts incoming requests Maven 2 配置Maven2仓库 ,这里默认即可 版本策略:该存储库存储了哪些类型的工件? - Release 发布 - Snapshot 快照 - Mixed 混合 Version policy: What type of artifacts does this repository store? 布局策略:验证所有路径都是 Maven 工件或元数据路径 - Strict 严格 - Permissive 宽松 Layout policy:Validate that all paths are maven artifact or metadata paths 内容配置:添加 Content-Disposition 标头作为“附件”,以禁止某些内容在浏览器中内联。 - Inline 排队 - Attachment 附件 Content Disposition: Add Content-Disposition header as 'Attachment' to disable some content from being inline in a browser. Proxy 配置代理 远程仓库:被代理的远程存储库的位置,例如 https://repo1.maven.org/maven2/,这里配置阿里的 --- http://maven.aliyun.com/nexus/content/groups/public Remote storage:Location of the remote repository being proxied, e.g. https://repo1.maven.org/maven2/ 阻止:阻止存储库上的出站连接 Blocked:Block outbound connections on the repository 启用自动阻止:如果检测到远程对等点无法访问/无响应,则自动阻止存储库上的出站连接 Auto blocking enablbled:Auto-block outbound connections on the repository if remote peer is detected as unreachable/unresponsive 部件最大使用年限:在重新检查远程存储库之前缓存工件的时间(以分钟为单位)。发布存储库应使用 -1。 Maximum component age: How long (in minutes) to cache artifacts before rechecking the remote repository. Release repositories should use -1. 元数据最大使用年限:在重新检查远程存储库之前缓存元数据的时间(以分钟为单位)。 Maximum metadata age:How long (in minutes) to cache metadata before rechecking the remote repository. ...
阿里的仓库地址官网说明:https://developer.aliyun.com/mvn/guide
上传jar包说明
- 点击最上方齿轮旁边的立方体
- 左侧菜单栏 upload
- 上传jar包及填写groupID等内容
配置maven
settings.xml
<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.2.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.2.0 https://maven.apache.org/xsd/settings-1.2.0.xsd">
<localRepository>D:\dev\apache-maven-3.9.8-bin\repositories-180-2</localRepository>
<pluginGroups>
</pluginGroups>
<proxies>
</proxies>
<servers>
<server>
<id>my-neuse-releases</id>
<username>admin</username>
<password>hy123</password>
</server>
<server>
<id>my-neuse-snapshot</id>
<username>admin</username>
<password>hy123</password>
</server>
</servers>
<mirrors>
<mirror>
<id>nexus</id>
<mirrorOf>*</mirrorOf>
<url>http://192.168.209.252:8081/repository/maven-central/</url>
</mirror>
</mirrors>
<profiles>
<profile>
<id>nexus</id>
<repositories>
<repository>
<id>central</id>
<url>http://192.168.209.252:8081/repository/maven-central/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>central</id>
<url>http://192.168.209.252:8081/repository/maven-central/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</profile>
</profiles>
<!-- activeProfiles
| List of profiles that are active for all builds.
-->
<activeProfiles>
<activeProfile>nexus</activeProfile>
</activeProfiles>
</settings>
pom.xml
<distributionManagement>
<repository>
<id>releases</id>
<name>releases</name>
<url>http://192.168.209.252:8081/repository/maven-releases/</url>
</repository>
<snapshotRepository>
<id>snapshots</id>
<name>snapshots</name>
<url>http://192.168.209.252:8081/repository/maven-snapshots/</url>
</snapshotRepository>
</distributionManagement>
迁移
本地仓库到私服
编写脚本mavenimport.sh文件
#!/bin/bash
# 使用 getopts 处理命令行参数
while getopts ":r:u:p:" opt; do
case $opt in
r)
REPO_URL="$OPTARG" # 设置仓库 URL
;;
u)
USERNAME="$OPTARG" # 设置用户名
;;
p)
PASSWORD="$OPTARG" # 设置密码
;;
\?)
echo "无效的选项: -$OPTARG" >&2 # 处理无效选项
exit 1
;;
:)
echo "选项 -$OPTARG 需要一个参数" >&2 # 处理缺少参数的情况
exit 1
;;
esac
done
# 查找当前目录下的所有文件,排除特定路径和文件
find . -type f \
-not -path './mavenimport.sh*' \
-not -path '*/.*' \
-not -path '*/^archetype-catalog.xml*' \
-not -path '*/^maven-metadata-local*.xml' \
-not -path '*/^maven-metadata-deployment*.xml' \
-not -path '*/_remote.repositories*' \
-not -path '*/^.lastUpdated*' \
| sed "s|^\./||" \
| xargs -I '{}' curl -u "$USERNAME:$PASSWORD" -X PUT -v -T {} ${REPO_URL}/{}
# 解释:
# find . -type f: 查找当前目录下的所有文件
# -not -path: 排除特定路径和文件
# sed "s|^\./||": 去掉路径前的 ./
# xargs -I '{}': 将每个文件路径传递给 curl 命令
# curl -u "$USERNAME:$PASSWORD" -X PUT -v -T {}: 使用 curl 命令上传文件
# ${REPO_URL}/{}: 构建完整的上传 URL
find . -type f -not -path './mavenimport\.sh*' -not -path '*/\.*' -not -path '*/\^archetype\-catalog\.xml*' -not -path '*/\^maven\-metadata\-local*\.xml' -not -path '*/\^maven\-metadata\-deployment*\.xml' | sed "s|^\./||" | xargs -I '{}' curl -u "$USERNAME:$PASSWORD" -X PUT -v -T {} ${REPO_URL}/{} ;
放在仓库所在的目录下,执行
chmod +x mavenimport.sh
./mavenimport.sh -u admin -p 123 -r http://192.168.60.133:8081/repository/my_repo/
bat脚本
@echo off
setlocal enabledelayedexpansion
:: 默认值
set "REPO_URL="
set "USERNAME="
set "PASSWORD="
set "LOG_FILE=upload_log.txt"
set "MAX_RETRIES=3" :: 最大重试次数
:: 清空日志文件
echo 上传开始 %date% %time% > "%LOG_FILE%"
:: 排除的文件列表
set "EXCLUDE_LIST=.git .svn .DS_Store mavenimport.sh archetype-catalog.xml maven-metadata-local.xml maven-metadata-deployment.xml"
:: 参数解析
:parse_args
if "%~1"=="" goto done
if "%~1"=="-r" (
set "REPO_URL=%~2"
shift
) else if "%~1"=="-u" (
set "USERNAME=%~2"
shift
) else if "%~1"=="-p" (
set "PASSWORD=%~2"
shift
) else (
echo 无效的选项: %~1
exit /b 1
)
shift
goto parse_args
:done
:: 检查参数是否完整
if "%REPO_URL%"=="" (
echo 需要提供仓库 URL (-r)
exit /b 1
)
if "%USERNAME%"=="" (
echo 需要提供用户名 (-u)
exit /b 1
)
if "%PASSWORD%"=="" (
echo 需要提供密码 (-p)
exit /b 1
)
:: 遍历文件并上传
for /r %%F in (*) do (
set "file=%%F"
set "skip=0"
:: 检查文件是否在排除列表中
for %%E in (%EXCLUDE_LIST%) do (
if /i "!file!" neq "!file:\%%E=!" (
set "skip=1"
)
)
:: 如果文件需要排除,跳过
if !skip! equ 1 (
continue
)
:: 上传文件
call :upload_file "!file!"
)
echo 上传完成 %date% %time% >> "%LOG_FILE%"
endlocal
exit /b
:: 上传文件函数
:upload_file
set "file=%~1"
set "retries=0"
:upload_retry
:: 增加上传日志
echo 正在上传: %file% >> "%LOG_FILE%"
:: 上传文件并重定向输出到日志
curl -u "%USERNAME%:%PASSWORD%" -X PUT -T "%file%" "%REPO_URL%/%file%" >> "%LOG_FILE%" 2>&1
:: 如果上传失败,重试
if !errorlevel! neq 0 (
set /a retries+=1
if !retries! leq %MAX_RETRIES% (
echo 上传失败,正在尝试重试(%retries%/%MAX_RETRIES%): %file% >> "%LOG_FILE%"
timeout /t 5 >nul
goto upload_retry
) else (
echo 上传失败: %file% (已重试 %MAX_RETRIES% 次) >> "%LOG_FILE%"
)
) else (
echo 上传成功: %file% >> "%LOG_FILE%"
)
exit /b
PowerShell 脚本 ---- .\upload.ps1 -RepoUrl "https://repo.example.com" -Username "yourUsername" -Password "yourPassword"
param (
[string]$RepoUrl, # 仓库URL
[string]$Username, # 用户名
[string]$Password, # 密码
[string]$LogFile = "upload_log.txt", # 日志文件路径
[int]$MaxRetries = 3 # 最大重试次数
)
# 检查参数是否提供
if (-not $RepoUrl -or -not $Username -or -not $Password) {
Write-Host "需要提供仓库URL、用户名和密码"
exit 1
}
# 排除的文件和文件夹列表
$excludeList = @('.git', '.svn', '.DS_Store', 'mavenimport.sh', 'archetype-catalog.xml', 'maven-metadata-local.xml', 'maven-metadata-deployment.xml')
# 清空日志文件
Out-File -FilePath $LogFile -Force -InputObject "上传开始: $(Get-Date)"
# 上传文件函数
function Upload-File {
param (
[string]$file
)
$retries = 0
$success = $false
# 判断文件是否需要排除
if ($excludeList -contains (Split-Path $file -Leaf)) {
Write-Host "跳过文件: $file"
return
}
while ($retries -lt $MaxRetries -and -not $success) {
try {
Write-Host "正在上传: $file"
# 使用 curl 进行文件上传
$curlCommand = "curl -u $Username:$Password -X PUT -T `"$file`" `"$RepoUrl/$file`""
Invoke-Expression $curlCommand
# 如果上传成功,则标记为成功
$success = $true
Add-Content -Path $LogFile -Value "上传成功: $file"
}
catch {
$retries++
Add-Content -Path $LogFile -Value "上传失败: $file (重试 $retries/$MaxRetries)"
Write-Host "上传失败,重试 ($retries/$MaxRetries)"
Start-Sleep -Seconds 5 # 等待 5 秒后重试
}
}
# 如果重试次数已用完仍然失败,记录失败日志
if (-not $success) {
Add-Content -Path $LogFile -Value "上传失败: $file (已重试 $MaxRetries 次)"
Write-Host "上传失败: $file (已重试 $MaxRetries 次)"
}
}
# 获取所有文件并上传
Get-ChildItem -Recurse | ForEach-Object {
$file = $_.FullName
if (-not $_.PSIsContainer) { # 排除文件夹
Start-Job -ScriptBlock {
Upload-File -file $using:file
}
}
}
# 等待所有任务完成
Get-Job | Wait-Job
# 记录上传完成时间
Add-Content -Path $LogFile -Value "上传完成: $(Get-Date)"
Write-Host "上传完成"
配置docker
报错:Error response from daemon: Get https://: http: server gave HTTP response to HTTPS client
docker 默认非 http 请求均为不信任的请求,都不允许访问。(如果nexus仓库地址是https,则不需要此项配置)
vim /etc/docker/daemon.json ## 没有就添加
---------
{
"insecure-registries" : [
"127.0.0.1:8082"
]
}
systemctl daemon-reload && systemctl restart docker
## 查看生效
docker info
------
Insecure Registries:
127.0.0.1:8082
127.0.0.0/8
Live Restore Enabled: false
其他 https://blog.csdn.net/qq_40875048/article/details/136434557
报错
权限
启动Nexus 报错:Could not lock User prefs. Lock file access denied.
Cannot open file ../sonatype-work/nexus3/log/jvm.log due to Permission denied
docker run之前给挂载的目录分配权限
chown -hR 200 /data/soft/docker/nexus3/nexus-data ## 给用户ID为200的授权(200就是nexus用户ID)
settings.xml 配置不生效
若配置不生效,试试将settings.xml改回这个名字
Npm
368aba11386f818b10734fef2fc37ee04af1b9036c9c50f32845ee4774f695d2
b1878dd98348be6382995e2797ae9efa3d5dcec2d2e0d36123633c697b7bdba1
4c16870f316135198624890f1ae2a2b58661b16c98d4928df6def13d64baea3d
bbacb8f9c5ebc806ca142e65f26297465b49b1cbc9b588e4b2aeb8382a73506c
ebb5c193656474a3c4015095a9fd1cbad17ef00fd66f2a226749297425c378bc
Nexus配置
nexus 中先创建角色,然后给角色绑定不同的管理权限,接着创建账号,将账号绑定给不同的角色
role ID mynpm-role
role Name mynpm-role
Privileges
批量上传脚本
#!/bin/bash
# 设定需要发布的node_modules目录的绝对路径
SOURCE_DIR = "D:/xxxxxxx"
# 申明私服仓库的地址
REPOSITORY="http://xxx.xxx.xxx.xxx:port:/repository/npm-public"
# 进行登录,需要用有上传权限的用户进行
npm login --registry=$(REPOSITORY)
for dir in "$SOURCE_DIR"/*; do # 遍历申明的node_modules目录
if [ -d "$dir" ]; do
dirname = $(basename "$dir") # 获取子目录的名称,如果是以@开头的子目录,
if [[ "$dirname" == @/* ]]; then # 说明目录下有多个组件,需要再次遍历
for subdir in "$dir"/*; do
if [ -d "$subdir" ]; then
cd "$subdir" || continue
npm pack # 执行npm的打包命令
tgz_file=$(find . -maxdepth 1 -name "*.tgz") # 查找是否生成tgz组件文件,需要是在具有package.json的目录中进行打包
if [ -n "$tgz_file" ]; then
npm publish --registry=${REPOSITORY} "$tgz_file" 执行npm的发布命令
rm -f "$tgz_file"
fi
fi
done
else
cd "$dir" || continue
npm pack
tgz_file=$(find . -maxdepth 1 -name "*.tgz")
if [ -n "$tgz_file" ]; then
npm publish --registry=${REPOSITORY} "$tgz_file"
rm -f "$tgz_file"
fi
fi
fi
done
pip私服
Python脚本
使用 Nexus 管理本地下载的 Python 依赖包时,可以通过 twine 工具批量上传到 Nexus 的 PyPI 仓库。以下是一个自动扫描本地包目录并批量上传到 Nexus的脚本:
前提条件
安装必要工具:
pip install twine # 用于上传 PyPI 包Nexus 已配置好 PyPI 宿主仓库(Hosted Repository),并记录:
- 仓库 URL(例如:
http://your-nexus-ip:8081/repository/pypi-hosted/) - 具有上传权限的 Nexus 用户名和密码
- 仓库 URL(例如:
脚本功能
- 扫描指定目录下的所有
.whl和.tar.gz包 - 自动过滤非 Python 包文件
- 批量上传到 Nexus 的 PyPI 仓库
- 跳过已上传的包(避免重复)
脚本代码(upload_to_nexus.py)
#!/usr/bin/env python3
import os
import sys
import subprocess
from pathlib import Path
def is_python_package(filename: str) -> bool:
"""判断是否为 Python 包文件"""
return filename.endswith(('.whl', '.tar.gz', '.tar.bz2', '.zip'))
def upload_package(package_path: Path, nexus_url: str, username: str, password: str) -> bool:
"""上传单个包到 Nexus"""
try:
# 执行 twine 上传命令
result = subprocess.run(
[
'twine', 'upload',
'--repository-url', nexus_url,
'--username', username,
'--password', password,
str(package_path)
],
capture_output=True,
text=True
)
# 检查上传结果(忽略已存在的包)
if result.returncode == 0:
print(f"✅ 成功上传: {package_path.name}")
return True
elif "File already exists" in result.stderr:
print(f"ℹ️ 已存在,跳过: {package_path.name}")
return True
else:
print(f"❌ 上传失败: {package_path.name}")
print(f" 错误信息: {result.stderr.strip()}")
return False
except Exception as e:
print(f"❌ 上传异常 {package_path.name}: {str(e)}")
return False
def batch_upload(local_dir: Path, nexus_url: str, username: str, password: str):
"""批量上传目录下的所有包"""
if not local_dir.exists():
print(f"错误:目录不存在 - {local_dir}")
return
# 扫描所有 Python 包
packages = []
for root, _, files in os.walk(local_dir):
for file in files:
if is_python_package(file):
packages.append(Path(root) / file)
if not packages:
print("未找到任何 Python 包文件")
return
print(f"发现 {len(packages)} 个包,开始上传到 Nexus...")
success = 0
fail = 0
for pkg in packages:
if upload_package(pkg, nexus_url, username, password):
success += 1
else:
fail += 1
print(f"\n上传完成:成功 {success} 个,失败 {fail} 个")
if __name__ == "__main__":
if len(sys.argv) != 5:
print("用法:python upload_to_nexus.py <本地包目录> <Nexus仓库URL> <Nexus用户名> <Nexus密码>")
print("示例:python upload_to_nexus.py /data/packages http://nexus:8081/repository/pypi-hosted/ admin admin123")
sys.exit(1)
local_dir = Path(sys.argv[1])
nexus_url = sys.argv[2]
username = sys.argv[3]
password = sys.argv[4]
batch_upload(local_dir, nexus_url, username, password)
使用方法
保存脚本为
upload_to_nexus.py执行命令(替换为你的实际参数):
python upload_to_nexus.py \ /path/to/your/local/packages \ # 本地下载的包所在目录 http://your-nexus-ip:8081/repository/pypi-hosted/ \ # Nexus PyPI 仓库 URL your-nexus-username \ # Nexus 用户名 your-nexus-password # Nexus 密码
关键说明
- Nexus 仓库类型:必须是
PyPI (Hosted)类型(非代理仓库),否则无法上传。 - 权限:确保 Nexus 用户有
nx-repository-admin-pypi-*-edit权限(可在 Nexus 控制台的「Security → Roles」配置)。 - 重复上传处理:脚本会自动跳过已存在的包(基于 Nexus 返回的「File already exists」错误)。
- 支持的包格式:
.whl(Wheel 包)、.tar.gz/.tar.bz2/.zip(源码包)。
如果需要定时同步指定包到 Nexus(例如从 PyPI 镜像下载后自动上传),可以告诉我,我可以补充一个带自动下载功能的增强版脚本。
shell脚本
以下是一个Shell脚本,用于批量上传本地Python依赖包到Nexus的PyPI仓库。脚本会扫描指定目录下的.whl和.tar.gz文件,自动过滤非Python包,并使用twine上传,同时处理重复上传的情况。
脚本功能
- 批量上传指定目录(包括子目录)下的Python包
- 跳过已存在的包(避免重复上传)
- 输出详细的上传日志(成功/失败/跳过)
- 支持自定义Nexus仓库地址、用户名和密码
脚本代码(upload_to_nexus.sh)
#!/bin/bash
# 检查参数是否正确
if [ $# -ne 4 ]; then
echo "用法:$0 <本地包目录> <Nexus仓库URL> <Nexus用户名> <Nexus密码>"
echo "示例:$0 /data/packages http://nexus:8081/repository/pypi-hosted/ admin admin123"
exit 1
fi
# 变量定义
LOCAL_PACKAGE_DIR="$1"
NEXUS_URL="$2"
NEXUS_USER="$3"
NEXUS_PASS="$4"
# 检查本地目录是否存在
if [ ! -d "$LOCAL_PACKAGE_DIR" ]; then
echo "错误:本地包目录不存在 - $LOCAL_PACKAGE_DIR"
exit 1
fi
# 检查twine是否安装
if ! command -v twine &> /dev/null; then
echo "错误:未找到twine工具,请先安装:pip install twine"
exit 1
fi
# 查找所有Python包(.whl 或 .tar.gz)
echo "正在扫描 $LOCAL_PACKAGE_DIR 下的Python包..."
PACKAGE_FILES=$(find "$LOCAL_PACKAGE_DIR" -type f \( -name "*.whl" -o -name "*.tar.gz" \))
# 检查是否有包文件
if [ -z "$PACKAGE_FILES" ]; then
echo "未找到任何Python包文件(.whl 或 .tar.gz)"
exit 0
fi
# 统计包数量
TOTAL=$(echo "$PACKAGE_FILES" | wc -l | tr -d ' ')
echo "发现 $TOTAL 个包,开始上传到Nexus仓库: $NEXUS_URL"
# 初始化计数器
SUCCESS=0
FAILED=0
SKIPPED=0
# 循环上传每个包
while IFS= read -r pkg; do
echo -n "处理 $pkg ... "
# 执行twine上传
UPLOAD_OUTPUT=$(twine upload \
--repository-url "$NEXUS_URL" \
--username "$NEXUS_USER" \
--password "$NEXUS_PASS" \
"$pkg" 2>&1)
# 判断上传结果
if echo "$UPLOAD_OUTPUT" | grep -q "File already exists"; then
echo "已存在,跳过"
((SKIPPED++))
elif echo "$UPLOAD_OUTPUT" | grep -q "Upload successful"; then
echo "上传成功"
((SUCCESS++))
else
echo "上传失败"
echo "错误详情: $UPLOAD_OUTPUT"
((FAILED++))
fi
done <<< "$PACKAGE_FILES"
# 输出总结
echo "----------------------------------------"
echo "上传完成:"
echo "总数量:$TOTAL"
echo "成功:$SUCCESS"
echo "失败:$FAILED"
echo "已跳过:$SKIPPED"
使用方法
保存脚本为
upload_to_nexus.sh赋予执行权限:
chmod +x upload_to_nexus.sh执行脚本(替换为你的实际参数):
./upload_to_nexus.sh \ /path/to/your/local/packages \ # 本地包存放目录(支持子目录) http://your-nexus-ip:8081/repository/pypi-hosted/ \ # Nexus的PyPI宿主仓库URL your-nexus-username \ # 有上传权限的Nexus用户名 your-nexus-password # 对应密码
关键说明
- Nexus仓库要求:必须是
PyPI (Hosted)类型仓库(非代理/组仓库),否则无法上传。 - 权限配置:确保Nexus用户拥有
nx-repository-admin-pypi-<仓库名>-edit权限(在Nexus控制台的Security → Roles中配置)。 - 依赖工具:依赖
twine(Python包上传工具),如果未安装,脚本会提示错误。 - 支持的包类型:
.whl(Wheel包)和.tar.gz(源码包),其他格式会被自动忽略。
如果需要进一步优化(例如:只上传特定版本的包、忽略临时文件等),可以告诉我你的具体需求,我可以调整脚本。
bat脚本
以下是适用于 Windows 系统的 BAT 脚本,用于批量上传本地 Python 包到 Nexus 的 PyPI 仓库。脚本功能与 Shell 版本一致,支持扫描本地目录、批量上传、跳过已存在的包,并输出详细日志。
脚本功能
- 扫描指定目录(包括子目录)下的
.whl和.tar.gz包 - 自动使用
twine上传到 Nexus 仓库 - 识别并跳过已上传的包(避免重复)
- 统计上传结果(成功/失败/跳过数量)
脚本代码(upload_to_nexus.bat)
@echo off
setlocal enabledelayedexpansion
:: 检查参数是否正确
if "%~4"=="" (
echo 用法:%0 本地包目录 Nexus仓库URL Nexus用户名 Nexus密码
echo 示例:%0 D:\packages http://nexus:8081/repository/pypi-hosted/ admin admin123
exit /b 1
)
:: 定义变量
set "LOCAL_PACKAGE_DIR=%~1"
set "NEXUS_URL=%~2"
set "NEXUS_USER=%~3"
set "NEXUS_PASS=%~4"
:: 检查本地目录是否存在
if not exist "%LOCAL_PACKAGE_DIR%" (
echo 错误:本地包目录不存在 - %LOCAL_PACKAGE_DIR%
exit /b 1
)
:: 检查 twine 是否安装
where twine >nul 2>nul
if %errorlevel% neq 0 (
echo 错误:未找到 twine 工具,请先安装:pip install twine
exit /b 1
)
:: 查找所有 Python 包(.whl 和 .tar.gz)
echo 正在扫描 %LOCAL_PACKAGE_DIR% 下的 Python 包...
set "PACKAGE_FILES="
for /r "%LOCAL_PACKAGE_DIR%" %%f in (*.whl *.tar.gz) do (
if exist "%%f" (
set "PACKAGE_FILES=!PACKAGE_FILES! "%%f""
)
)
:: 检查是否有包文件
if "!PACKAGE_FILES!"=="" (
echo 未找到任何 Python 包文件(.whl 或 .tar.gz)
exit /b 0
)
:: 统计包数量
set TOTAL=0
for %%f in (!PACKAGE_FILES!) do (
set /a TOTAL+=1
)
echo 发现 !TOTAL! 个包,开始上传到 Nexus 仓库: %NEXUS_URL%
:: 初始化计数器
set SUCCESS=0
set FAILED=0
set SKIPPED=0
:: 循环上传每个包
for %%f in (!PACKAGE_FILES!) do (
echo 处理 %%~nxf ...
twine upload ^
--repository-url "%NEXUS_URL%" ^
--username "%NEXUS_USER%" ^
--password "%NEXUS_PASS%" ^
"%%f" >nul 2>upload_error.tmp
:: 判断上传结果
findstr /i "File already exists" upload_error.tmp >nul
if !errorlevel! equ 0 (
echo 已存在,跳过
set /a SKIPPED+=1
) else (
findstr /i "Upload successful" upload_error.tmp >nul
if !errorlevel! equ 0 (
echo 上传成功
set /a SUCCESS+=1
) else (
echo 上传失败
type upload_error.tmp
set /a FAILED+=1
)
)
)
:: 清理临时文件
del upload_error.tmp >nul 2>nul
:: 输出总结
echo.
echo ----------------------------------------
echo 上传完成:
echo 总数量:!TOTAL!
echo 成功:!SUCCESS!
echo 失败:!FAILED!
echo 已跳过:!SKIPPED!
endlocal
使用方法
保存脚本为
upload_to_nexus.bat双击运行或在命令提示符(CMD)中执行,参数格式:
upload_to_nexus.bat "D:\本地包目录" "http://nexus-ip:8081/repository/pypi-hosted/" "用户名" "密码"例如:
upload_to_nexus.bat "D:\python_packages" "http://192.168.1.100:8081/repository/my-pypi/" "admin" "123456"
关键说明
- 依赖工具:需安装
twine(执行pip install twine安装),且确保twine在系统 PATH 中(可通过twine --version验证)。 - Nexus 配置:仓库必须是
PyPI (Hosted)类型,用户需有上传权限(参考 Nexus 控制台的Security → Roles配置)。 - 路径包含空格:如果本地包目录路径包含空格,需用英文引号包裹(例如
"D:\my packages")。 - 错误排查:失败的包会显示具体错误信息(如权限不足、包格式错误等),可根据提示调整。
如果需要适配特殊场景(如只上传特定版本、忽略某些文件),可以告诉我,我会进一步优化脚本。
