#!/usr/bin/env python3 """ RustFS S3 Storage Toolkit 发布脚本 支持交互式和自动化发布 """ import subprocess import sys import os import argparse from pathlib import Path def run_command(command, description): """运行命令并显示结果""" print(f"🔧 {description}...") try: result = subprocess.run(command, shell=True, check=True, capture_output=True, text=True) print(f"✅ {description}成功") if result.stdout.strip(): print(f"输出: {result.stdout.strip()}") return True except subprocess.CalledProcessError as e: print(f"❌ {description}失败: {e}") if e.stdout: print(f"输出: {e.stdout}") if e.stderr: print(f"错误: {e.stderr}") return False def main(): """主发布流程""" parser = argparse.ArgumentParser(description="RustFS S3 Storage Toolkit 发布脚本") parser.add_argument("--auto", action="store_true", help="自动发布到 PyPI (跳过交互)") parser.add_argument("--test", action="store_true", help="发布到 TestPyPI") parser.add_argument("--check-only", action="store_true", help="仅构建和检查,不发布") args = parser.parse_args() print("🚀 RustFS S3 Storage Toolkit 发布脚本") print("=" * 50) # 检查当前目录 current_dir = Path.cwd() if not (current_dir / "pyproject.toml").exists(): print("❌ 请在项目根目录运行此脚本") sys.exit(1) print(f"✅ 项目目录: {current_dir}") # 检查 PyPI 配置 pypirc_path = Path.home() / ".pypirc" if not pypirc_path.exists() and not args.check_only: print("⚠️ 警告: 未找到 ~/.pypirc 配置文件") print("请先配置 PyPI 认证信息") sys.exit(1) # 1. 清理和构建 print("\n🧹 清理旧文件...") cleanup_commands = [ "rm -rf dist/", "rm -rf build/", "rm -rf *.egg-info/", "rm -rf src/*.egg-info/", ] for command in cleanup_commands: subprocess.run(command, shell=True, capture_output=True) print("✅ 清理完成") print("\n📦 开始构建...") if not run_command("python -m build", "构建包"): print("❌ 构建失败,无法继续发布") sys.exit(1) # 2. 检查构建结果 dist_dir = current_dir / "dist" if not dist_dir.exists() or not list(dist_dir.glob("*")): print("❌ 没有找到构建文件") sys.exit(1) built_files = list(dist_dir.glob("*")) print(f"\n📦 构建文件:") for file in built_files: print(f" - {file.name}") # 3. 验证包 print("\n🔍 验证包格式...") if not run_command("python -m twine check dist/*", "验证包"): print("❌ 包验证失败") sys.exit(1) # 如果只是检查,到此结束 if args.check_only: print("\n✅ 构建和验证完成!") return # 4. 确定发布类型 if args.test: publish_type = "test" elif args.auto: publish_type = "pypi" else: # 交互式选择 print("\n🎯 选择发布类型:") print("1. 测试发布 (TestPyPI)") print("2. 正式发布 (PyPI)") print("3. 取消") choice = input("\n请选择 (1/2/3): ").strip() if choice == "1": publish_type = "test" elif choice == "2": publish_type = "pypi" else: print("❌ 发布已取消") return # 5. 执行发布 if publish_type == "test": # 测试发布 print("\n🧪 发布到 TestPyPI...") if run_command("python -m twine upload --repository testpypi dist/*", "测试发布"): print("\n🎉 测试发布成功!") print("📖 测试安装命令:") print("pip install --index-url https://test.pypi.org/simple/ rustfs-s3-toolkit") else: print("❌ 测试发布失败") sys.exit(1) elif publish_type == "pypi": # 正式发布 if not args.auto: print("\n⚠️ 确认正式发布到 PyPI?") confirm = input("输入 'yes' 确认: ").strip().lower() if confirm != "yes": print("❌ 发布已取消") return print("\n🚀 发布到 PyPI...") if run_command("python -m twine upload dist/*", "正式发布"): print("\n🎉 正式发布成功!") print("📖 安装命令:") print("pip install rustfs-s3-toolkit") print("\n🔗 PyPI 链接:") print("https://pypi.org/project/rustfs-s3-toolkit/") else: print("❌ 正式发布失败") sys.exit(1) print("\n💡 后续步骤:") print("1. 检查 PyPI 页面确认发布成功") print("2. 测试安装: pip install rustfs-s3-toolkit") print("3. 更新版本号准备下次发布") if __name__ == "__main__": try: main() except KeyboardInterrupt: print("\n❌ 发布已中断") sys.exit(1) except Exception as e: print(f"\n❌ 发布失败: {e}") sys.exit(1)