python脚本
#!/usr/bin/env python3
import os
import sys
import subprocess
import tempfile
import xml.etree.ElementTree as ET
from pathlib import Path
## ================== 配置区 ==================
NEXUS_URL = "http://192.168.23.150:12600/repository/maven-releases/"
NEXUS_URL_SNAPSHOTS = "http://192.168.23.150:12600/repository/maven-snapshots/"
USERNAME = "admin"
PASSWORD = "admin123"
## 仓库 ID 配置
RELEASE_REPOSITORY_ID = "nexus-release"
SNAPSHOT_REPOSITORY_ID = "nexus-snapshot"
LOCAL_REPO = Path(
r"G:\畅享\项目\医保驾驶舱\文档\taobao\taobao\parent")
LOG_FILE = "upload_log.txt"
ERROR_FILE = "upload_errors.txt" # 添加错误文件路径
LOG_FILE_ERROR = "./upload_errors_back.txt"
## ==========================================
def parse_gav_from_pom(pom_path):
"""从 pom.xml 中提取 groupId, artifactId, version"""
try:
tree = ET.parse(pom_path)
root = tree.getroot()
ns = {'m': 'http://maven.apache.org/POM/4.0.0'} if root.tag.startswith('{') else {}
def get_text_or_parent(tag):
elem = root.find(f'm:{tag}', ns)
if elem is not None:
return elem.text
# 尝试从 parent 继承(简单处理,不递归)
parent = root.find('m:parent', ns)
if parent is not None:
elem = parent.find(f'm:{tag}', ns)
if elem is not None:
return elem.text
return None
def resolve_properties(text, properties_dict):
"""解析属性占位符"""
if text and '${' in text:
for prop_name, prop_value in properties_dict.items():
placeholder = f"$}"
if placeholder in text:
text = text.replace(placeholder, prop_value)
return text
def get_properties_from_pom(pom_root):
"""获取 POM 中定义的属性"""
properties = {}
properties_elem = pom_root.find('m:properties', ns)
if properties_elem is not None:
for prop in properties_elem:
prop_name = prop.tag.replace(f"}", "")
properties[prop_name] = prop.text or ""
return properties
# 获取当前 POM 的属性
current_properties = get_properties_from_pom(root)
group_id = get_text_or_parent('groupId')
artifact_id = get_text_or_parent('artifactId')
version = get_text_or_parent('version')
# 先尝试解析当前 POM 中定义的属性
if current_properties:
group_id = resolve_properties(group_id, current_properties) if group_id else None
artifact_id = resolve_properties(artifact_id, current_properties) if artifact_id else None
version = resolve_properties(version, current_properties) if version else None
# 如果版本仍然是占位符,检查父 POM 中是否有实际版本
if version and '${' in version:
parent = root.find('m:parent', ns)
if parent is not None:
parent_version_elem = parent.find('m:version', ns)
if parent_version_elem is not None and parent_version_elem.text and '${' not in parent_version_elem.text:
# 使用父 POM 中的实际版本
version = parent_version_elem.text
if not all([group_id, artifact_id, version]):
return None
# 最终检查版本是否仍包含占位符
if version and '${' in version:
print(f" ⚠️ 版本包含未解析的属性: {version}")
return None
return group_id.strip(), artifact_id.strip(), version.strip()
except Exception as e:
print(f" ❌ 解析 POM 失败: {e}")
return None
def run_mvn_deploy(gav, files_info):
group_id, artifact_id, version = gav
packaging = files_info.get("packaging", "jar")
# 根据版本是否包含 SNAPSHOT 选择不同的 URL 和 repositoryId
if "SNAPSHOT" in version.upper():
url = NEXUS_URL_SNAPSHOTS
repository_id = SNAPSHOT_REPOSITORY_ID
else:
url = NEXUS_URL
repository_id = RELEASE_REPOSITORY_ID
# 构建 mvn 命令
cmd = [
"mvn", "deploy:deploy-file",
f"-DgroupId={group_id}",
f"-DartifactId={artifact_id}",
f"-Dversion={version}",
f"-Dpackaging={packaging}",
f"-DrepositoryId={repository_id}", # 使用根据版本类型选择的 repositoryId
f"-Durl={url}" # 使用根据版本类型选择的 URL
]
if packaging == "pom":
cmd.append(f"-Dfile={files_info['pom']}")
cmd.append(f"-DpomFile={files_info['pom']}")
else:
cmd.extend([
f"-Dfile={files_info['main']}",
f"-DpomFile={files_info['pom']}"
])
# 附加 sources/javadoc(如果存在)
classifiers = []
types = []
files = []
if "sources" in files_info:
classifiers.append("sources")
types.append("jar")
files.append(files_info["sources"])
if "javadoc" in files_info:
classifiers.append("javadoc")
types.append("jar")
files.append(files_info["javadoc"])
if classifiers:
cmd.append(f"-Dfiles={','.join(files)}")
cmd.append(f"-Dclassifiers={','.join(classifiers)}")
cmd.append(f"-Dtypes={','.join(types)}")
# 设置 settings 文件路径
settings_path = r'E:\maven\apache-maven-3.6.0-bin\apache-maven-3.6.0\conf\dev2.xml'
cmd.append(f"-s")
cmd.append(settings_path)
# 打印命令
print(" ".join(cmd))
# 执行命令,保留 cwd 参数以确保在正确的目录下执行
result = subprocess.run(cmd, capture_output=True, text=True, cwd=os.path.dirname(files_info['pom']), shell=True)
if result.returncode == 0:
return True, " ".join(cmd), " "
else:
return False, " ".join(cmd), result.stderr or result.stdout
def get_files_info(pom_path, gav):
"""获取要上传的文件信息"""
group_id, artifact_id, version = gav
base_name = f"{artifact_id}-{version}"
base_dir = pom_path.parent
# 检查所有可能的文件
jar_path = base_dir / f"{base_name}.jar"
sources_path = base_dir / f"{base_name}-sources.jar"
javadoc_path = base_dir / f"{base_name}-javadoc.jar"
# 如果存在 JAR 文件,优先上传 JAR 包(包含 POM 作为描述文件)
if jar_path.exists():
files_info = {
"packaging": "jar",
"main": str(jar_path),
"pom": str(pom_path)
}
# 检查并添加 sources 和 javadoc
if sources_path.exists():
files_info["sources"] = str(sources_path)
if javadoc_path.exists():
files_info["javadoc"] = str(javadoc_path)
else:
# 仅上传 POM 文件
files_info = {
"packaging": "pom",
"pom": str(pom_path)
}
return files_info
def main(enable_error_log=False, log_file: str = LOG_FILE_ERROR):
pom_files = []
if enable_error_log:
print(f" ⚠️ 错误依赖重跑========================")
# 获取错误文件中的POM路径
with open(log_file, "r", encoding="utf-8") as error_file:
error_lines = error_file.readlines()
# 解析错误文件,获取需要重跑的POM文件路径
for line in error_lines:
line = line.strip()
if line.endswith('.pom') and os.path.exists(line):
pom_files.append(Path(line))
else:
pom_files = list(LOCAL_REPO.rglob("*.pom"))
total = len(pom_files)
success_count = 0
fail_count = 0
# 清空错误文件
with open(ERROR_FILE, "w", encoding="utf-8") as error_log:
error_log.write("失败的上传记录:\n")
error_log.write("=" * 50 + "\n")
with open(LOG_FILE, "w", encoding="utf-8") as log:
for i, pom_path in enumerate(pom_files, 1):
print(f"[{i}/{total}] 处理: {pom_path.relative_to(LOCAL_REPO)}")
log.write(f"Processing: {pom_path}\n")
gav = parse_gav_from_pom(pom_path)
if not gav:
msg = " ⚠️ 无法解析 GAV,跳过\n"
print(msg.strip())
log.write(msg)
# 记录到错误文件
with open(ERROR_FILE, "a", encoding="utf-8") as error_log:
error_log.write(f"{pom_path}\n")
fail_count += 1
continue
files_info = get_files_info(pom_path, gav)
# 验证必要的文件是否存在
if "main" in files_info:
main_file_path = Path(files_info["main"])
if not main_file_path.exists():
msg = f" ❌ 主文件不存在: {main_file_path}\n"
print(msg.strip())
log.write(msg)
# 记录到错误文件
with open(ERROR_FILE, "a", encoding="utf-8") as error_log:
error_log.write(f"{pom_path}\n")
fail_count += 1
continue
success, cmdStr, error_msg = run_mvn_deploy(gav, files_info)
if success:
msg = f" ✅ 成功: {':'.join(gav)}\n"
print(msg.strip())
log.write("\n"+msg + "\n" + cmdStr+"\n")
success_count += 1
else:
msg = f" ❌ 失败: {':'.join(gav)}\n 错误: {error_msg[:500]}\n"
print(msg.strip())
log.write("\n"+msg + "\n" + cmdStr+"\n")
# 将失败记录写入错误文件
with open(ERROR_FILE, "a", encoding="utf-8") as error_log:
error_log.write(f"{pom_path}\n")
fail_count += 1
print("\n" + "=" * 50)
print(f"完成!总计: {total}, 成功: {success_count}, 失败: {fail_count}")
print(f"详细日志见: {LOG_FILE}")
print(f"失败记录见: {ERROR_FILE}")
if __name__ == "__main__":
# 检查 mvn 是否可用
try:
subprocess.run(["mvn", "-v"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=True, shell=True)
except (subprocess.CalledProcessError, FileNotFoundError):
print("错误:未找到 'mvn' 命令,请确保 Maven 已安装并加入 PATH。")
sys.exit(1)
main()
maven配置文件
<!-- 本地仓库路径 -->
<localRepository>E:\maven_location_ybjsc</localRepository>
<pluginGroups></pluginGroups>
<!-- 镜像配置 - 修改为镜像所有仓库 -->
<mirrors>
<mirror>
<id>location-repo</id>
<mirrorOf>*</mirrorOf> <!-- 改为 * 镜像所有仓库,不只是 central -->
<name>location-repo</name>
<url>http://192.168.23.150:12600/repository/maven-public/</url>
</mirror>
</mirrors>
<!-- 服务器认证信息 -->
<servers>
<server>
<id>nexus-release</id>
<username>admin</username>
<password>admin123</password>
</server>
<server>
<id>nexus-snapshot</id>
<username>admin</username>
<password>admin123</password>
</server>
</servers>
<!-- Profile 配置 - 添加多个仓库源 -->
<profiles>
<profile>
<id>dev</id>
<repositories>
<!-- 主要仓库:私有 Nexus 仓库 -->
<repository>
<id>location-repo</id>
<name>location-repo</name>
<url>http://192.168.23.150:12600/repository/maven-public/</url>
<releases>
<enabled>true</enabled>
<updatePolicy>daily</updatePolicy>
<checksumPolicy>warn</checksumPolicy>
</releases>
<snapshots>
<enabled>true</enabled>
<updatePolicy>daily</updatePolicy>
<checksumPolicy>warn</checksumPolicy>
</snapshots>
</repository>
<!-- 备用仓库:Maven 中央仓库 -->
<repository>
<id>central</id>
<name>Maven Central</name>
<url>https://repo1.maven.org/maven2/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<!-- 阿里云镜像作为备用 -->
<repository>
<id>aliyun</id>
<name>Aliyun Repository</name>
<url>https://maven.aliyun.com/repository/public</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>location-repo</id>
<name>location-repo</name>
<url>http://192.168.23.150:12600/repository/maven-public/</url>
<releases>
<enabled>true</enabled>
<updatePolicy>daily</updatePolicy>
<checksumPolicy>warn</checksumPolicy>
</releases>
<snapshots>
<enabled>true</enabled>
<updatePolicy>daily</updatePolicy>
<checksumPolicy>warn</checksumPolicy>
</snapshots>
</pluginRepository>
<!-- Maven 中央插件仓库 -->
<pluginRepository>
<id>central</id>
<name>Maven Central Plugin Repository</name>
<url>https://repo1.maven.org/maven2/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</profile>
</profiles>
<activeProfiles>
<activeProfile>dev</activeProfile>
</activeProfiles>