python脚本-Maven私库依赖上传

工具 2026-02-27
python maven nexus

python脚本-Maven私库依赖上传

分类: 工具 更新: 2026-02-27
python maven nexus
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>

文档目录