自动化配置iOS开发环境

前言:最近公司让我写个自动签名的教程出来,方便后面培训实习生,刚开始我也是老老实实的写的,但令我万万没想到的是第一版交出去后,领导直接给我打回来了,说我写的他看得懂,实习生不一定就能看得懂,尤其是让我把配置开发环境这些都要一步一步的写出来,还要配上贴图,让我写的再详细点,好吧,尽管心里不愿意,但手上可还是很诚实的开始写上了,然后我想,如果按照领导的要求来写,那的写到猴年马月啊,而且写的越多,说不定到时候改的也就越多,于是,我就想到了写个脚本来自动化配置开发环境,说干就干,经过一通鼓捣,一个强大的自动化配置iOS开发环境的脚本就诞生了...

这个脚本支持安装Homebrew、GPG/GPG2、RVM、Ruby、CocoaPods、SVN(SVN是否安装请根据自己实际需求来决定,不需要的可以注释掉。)

使用方法,例如将脚本命名为AutoConfig.py, 则终端中直接CD到脚本所在目录,然后输入命令 python3 AutoConfig.py,接着按照提示一步一步安装即可。

在这里给大家推荐几个我写的工具,如果大家觉得不错的话,请给颗鼓励的小星星哦...

import os
import re
import sys
import subprocess
from shutil import which

# 用户配置信息
CONFIG = {
    'gpg_key': None, # 设置为 None 以自动生成并获取GPG key,或者指定 GPG key
    'gpg_username': '你的名字',
    'gpg_email': 'nn@aliyun.com',
    'gpg_comment': 'comment is null',
    'gpg_passphrase': 'GPGPWD', # 设置GPG密钥的密码
    'ruby_install_version': None, # 设置为 None 以自动获取最新版本,或者指定版本号
    'homebrew_url_install': 'https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh',
    'homebrew_url_uninstall': 'https://gitee.com/cunkai/HomebrewCN/raw/master/HomebrewUninstall.sh',
    'rubygems_source_remove': 'https://rubygems.org/',
    'rubygems_source_custom': 'https://gems.ruby-china.com/',
    'cocoapods_repo_url': 'https://mirrors.tuna.tsinghua.edu.cn/git/CocoaPods/Specs.git',
    'trunk_path': '~/.cocoapods/repos/trunk',
    'master_path': '~/.cocoapods/repos/master'
}

# 打印并执行命令的函数
def run_command(command, check=True, description=""):
    print(f"{description}")
    result = subprocess.run(command, shell=True)
    if check and result.returncode != 0:
        print(f"{description}时出错: {result.returncode}")
        sys.exit(1)
    print(f"{description}")
    return result

# 询问并确认是否需要安装指定工具
def check_tool_install(tool=""):
    result = input(f"检测到您目前尚未安装 {tool}, 是否需要下载并安装? (y/n): ").strip().lower()
    if result == 'y':
        return True
    else:
        return False

# 检查并安装 Homebrew
def check_and_install_homebrew():
    result = run_command("brew --version", check=False)
    if result.returncode == 0:
        reinstall = input("检测到已安装Homebrew,是否需要卸载后重新安装最新版本? (y/n): ").strip().lower()
        if reinstall == 'y':
            run_command(f"/bin/zsh -c \"$(curl -fsSL {CONFIG['homebrew_url_uninstall']})\"", description="卸载 Homebrew")
            run_command(f"/bin/zsh -c \"$(curl -fsSL {CONFIG['homebrew_url_install']})\"", description="重新安装 Homebrew")
    else:
        if check_tool_install("Homebrew"):
            run_command(f"/bin/zsh -c \"$(curl -fsSL {CONFIG['homebrew_url_install']})\"", description="安装 Homebrew")

# 检查 Homebrew 是否安装,若未安装则不执行后续安装
def check_homebrew_installed():
    try:
        run_command("brew --version")
        return True
    except subprocess.CalledProcessError:
        return False

# 检查并安装 GPG
def check_and_install_gpg():
    if check_homebrew_installed():
        result = run_command("gpg2 --version", check=False)
        if result.returncode == 0:
            reinstall = input("检测到已安装gpg2, 是否需要卸载后重新安装最新版本? (y/n): ").strip().lower()
            if reinstall == 'y':
                run_command("brew uninstall --force gpg2",description="卸载GPG/GPG2")
                install_gpg()
        else:
            if check_tool_install("GPG"):
                install_gpg()
    else:
        print("Homebrew 未正确安装,请先安装 Homebrew")
        sys.exit(1)

def install_gpg():
    run_command("brew install gnupg gnupg2", description="安装GPG2")

    # 创建符号链接
    if not which("gpg2"):
        print("gpg2 命令不可用,尝试创建符号链接...")
        gpg_path = which("gpg")
        if gpg_path:
            # 创建符号链接
            run_command(f"ln -s {gpg_path} {gpg_path}2", description="创建符号链接")
        else:
            print("gpg2 未找到,请检查您的系统环境")
            sys.exit(1)

    # 检查 GPG2 是否安装成功
    if check_gpg_installed():
        print("GPG2 安装成功")
        gpg_key = get_gpg_key()
        command = f"gpg2 --keyserver hkp://pool.sks-keyservers.net --recv-keys {gpg_key}"
        run_command(command, check=False, description=f"设置 gpg key {gpg_key}")
    else:
        print("GPG2 安装失败,请检查安装过程中是否有错误或网络连接是否正常")
        sys.exit(1)

def get_gpg_key():
    """
    生成并返回新的 GPG 密钥对的公钥
    """
    if CONFIG['gpg_key']:
        return CONFIG['gpg_key']

    key_list = get_gpg_key_list()
    if len(key_list) >= 2:
        gpg_key = f"{key_list[0]} {key_list[1]}"
        return gpg_key

    key_details = f"""
        %echo Generating a basic OpenPGP key
        Key-Type: RSA
        Key-Length: 2048
        Subkey-Type: RSA
        Subkey-Length: 2048
        Name-Real: {CONFIG['gpg_username']}
        Name-Comment: {CONFIG['gpg_comment']}
        Name-Email: {CONFIG['gpg_email']}
        Expire-Date: 0
        Passphrase: {CONFIG['gpg_passphrase']}
        %commit
        %echo done
    """

    # 将密钥详情写入临时文件
    with open('gpg_key_details.txt', 'w') as f:
        f.write(key_details)

    # 生成 GPG 密钥对
    run_command("gpg2 --batch --generate-key gpg_key_details.txt", description="生成 GPG 密钥对")

    # 获取生成的 GPG 密钥 ID
    result = subprocess.run(
        ["gpg2", "--list-secret-keys", "--keyid-format", "LONG", CONFIG['gpg_email']],
        capture_output=True,
        text=True
    )

    key_output = result.stdout
    gpg_key_id = None

    # 提取 GPG key ID
    for line in key_output.split('\n'):
        if 'sec' in line and '/' in line:
            gpg_key_id = line.split('/')[1].split()[0]
            break

    if not gpg_key_id:
        print("GPG 密钥生成失败,请检查安装过程中是否有错误或配置是否正确")
        sys.exit(1)

    # 导出公钥
    result = subprocess.run(
        ["gpg2", "--armor", "--export", gpg_key_id],
        capture_output=True,
        text=True
    )

    gpg_public_key = result.stdout

    if not gpg_public_key:
        print("GPG 公钥导出失败,请检查安装过程中是否有错误")
        sys.exit(1)

    # 删除临时文件
    os.remove('gpg_key_details.txt')

    keys = get_gpg_key_list()

    if len(keys) < 2:
        # 获取到的 gpg keys 数量不足
        get_gpg_key()

    gpg_key = f"{keys[0]} {keys[1]}"

    return gpg_key

# 获取 gpg key列表
def get_gpg_key_list():

    # 查看key列表
    list_keys_result = subprocess.run(
        ["gpg2", "--list-keys"],
        capture_output=True,
        text=True
    )
    
    # 检查命令执行是否成功
    if list_keys_result.returncode != 0:
        print("获取gpg keys 失败")
        sys.exit(1)

    # 命令输出
    output = list_keys_result.stdout
    # 定义一个正则表达式来匹配公钥ID
    key_id_pattern = re.compile(r'\s+([A-F0-9]+)\s+')
    # 使用正则表达式查找所有公钥ID
    key_ids = key_id_pattern.findall(output)

    return key_ids


# 检查 GPG 是否安装,若未安装则不执行后续安装
def check_gpg_installed():
    try:
        run_command("gpg2 --version")
        return True
    except subprocess.CalledProcessError:
        return False

# 检查并安装 RVM
def check_and_install_rvm():
    if check_gpg_installed():
        result = run_command("rvm --version", check=False)
        if result.returncode == 0:
            reinstall = input("检测到RVM已安装,是否需要卸载后重新安装最新版本? (y/n): ").strip().lower()
            if reinstall == 'y':
                install_version = get_rvm_version()
                commands = [
                    f"rvm remove {install_version}",
                    "rvm implode",
                    "curl -L https://get.rvm.io | bash -s stable"
                ]
                for command in commands:
                    run_command(command, description=f"执行 {command}")
                install_rvm()
        else:
            if check_tool_install("RVM"):
                install_rvm()
    else:
        print("GPG/GPG2 未正确安装,请先安装 GPG/GPG2")
        sys.exit(1)

# 获取已安装的 RVM 的版本号
def get_rvm_version():
    result = subprocess.run(
        ["rvm", "--version"],
        capture_output=True,
        text=True
    )
    output = result.stdout.strip()
    version_match = re.search(r'\d+\.\d+(\.\d+)?', output)
    return version_match.group()

def install_rvm():
    """
    安装 RVM
    """
    rvm_commands = [
        "curl -sSL https://get.rvm.io | bash -s stable",
        "curl -L get.rvm.io | bash -s stable",
        "rvm get master"
    ]
    for command in rvm_commands:
        run_command(command, description=f"安装 RVM {command.split()[0]}")

# 检查并安装 Ruby
def check_and_install_ruby():
    if check_rvm_installed():
        result = run_command("ruby --version", check=False)
        if result.returncode == 0:
            reinstall = input("检测到Ruby已安装,是否需要重新安装最新版本? (y/n): ").strip().lower()
            if reinstall == 'y':
                install_ruby()
        else:
            if check_tool_install("Ruby"):
                install_ruby()
    else:
        print("RVM 未正确安装,请先安装 RVM")
        sys.exit(1)

def check_rvm_installed():
    try:
        run_command("rvm --version")
        return True
    except subprocess.CalledProcessError:
        return False

def install_ruby():
    """
    安装 Ruby 并设置默认版本
    """
    install_version = None
    if CONFIG['ruby_install_version'] is None:
        install_version = get_latest_ruby_version()
        if install_version is None:
            print("未找到可用的 Ruby 版本,请输入您想安装的 Ruby 版本号: ")
            install_version = input("Ruby 版本号: ").strip()
        else:
            print(f"安装最新 Ruby 版本: {install_version}")
    else:
        install_version = CONFIG['ruby_install_version']
        print(f"安装指定 Ruby 版本: {install_version}")

    run_command(f"rvm install {install_version}", description=f"安装 Ruby {install_version}")
    run_command(f"rvm use ruby-{install_version} --default", description=f"设置默认 Ruby 版本为 {install_version}")

# 获取 Ruby 最新版本
def get_latest_ruby_version():
    """
    自动获取 RVM 中最新的 Ruby 版本

    返回:
    str: 最新的 Ruby 版本号,如果获取失败则返回 None
    """
    print("正在自动获取 RVM 中最新的 Ruby 版本...")
    try:
        result = subprocess.run(
            ["rvm", "list", "known"],
            capture_output=True,
            text=True
        )
        output = result.stdout
        print("获取到的 RVM 输出:\n", output)

        # 提取版本号的正则表达式
        version_pattern = re.compile(r'\[ruby-]?(\d+\.\d+\.\d+|\d+\.\d+|\d+)')
        versions = version_pattern.findall(output)
        
        def is_valid_version(version):
            """
            检查是否为有效的版本号
            """
            try:
                tuple(map(int, re.split(r'\D+', version)))
                return True
            except ValueError:
                return False

        if versions:
            versions = [v for v in versions if is_valid_version(v)]
            versions.sort(key=lambda x: tuple(map(int, re.split(r'\D+', x))), reverse=True)
            return versions[0] if versions else None
        else:
            return None

    except subprocess.CalledProcessError as e:
        print("获取 Ruby 版本失败:", e)
        return None

# 检查并安装 CocoaPods
def check_and_install_cocoapods():
    if check_ruby_and_rvm_installed():

        result = run_command("pod --version", check=False)
        if result.returncode == 0:
            reinstall = input("检测到Cocoapods已安装,是否需要卸载后重新安装最新版本? (y/n): ").strip().lower()
            if reinstall == 'y':
                run_command("sudo gem uninstall cocoapods", description="卸载Cocoapods")
                run_command("rm -rf ~/.cocoapods/", description="移除 Cocoapods 缓存")
                install_cocoapods()
        else:
            if check_tool_install("CocoaPods"):
                install_cocoapods()
    else:
        sys.exit(1)

def install_cocoapods():
    """
    安装 CocoaPods
    """
    commands = [
        f"gem sources --remove {CONFIG['rubygems_source_remove']}",
        f"gem sources --add {CONFIG['rubygems_source_custom']}",
        "gem sources -l",
        "sudo gem install -n /usr/local/bin cocoapods"
    ]
    for command in commands:
        run_command(command, description=f"执行 {command}")

    # 检查 CocoaPods 是否安装成功
    try:
        run_command(["pod", "--version"])
        print("CocoaPods 安装成功")
    except subprocess.CalledProcessError:
        print("CocoaPods 安装失败,请检查安装过程中是否有错误或者网络连接是否正常")
        sys.exit(1)

    # 克隆 CocoaPods 仓库
    repo_url = CONFIG['cocoapods_repo_url']

    # 设置 Trunk 仓库存储地址
    trunk_path = os.path.expanduser(CONFIG['trunk_path'])
    # 检查 CocoaPods Trunk 仓库是否已存在
    if os.path.isdir(trunk_path):
        print(f"Cocoapods仓库 trunk 已存在,跳过克隆 trunk 步骤")
    else:
        add_trunk_command = f"git clone {repo_url} {trunk_path}"
        cocoapods_warehouse_clone("trunk", add_trunk_command, trunk_path, "克隆 CocoaPods trunk 仓库")

    # 设置 Master 仓库存储地址
    master_path = os.path.expanduser(CONFIG['master_path'])
    # 检查 CocoaPods Master 仓库是否已存在
    if os.path.isdir(master_path):
        print(f"Cocoapods仓库 master 已存在,跳过克隆 master 步骤")
    else:
        add_master_command = f"pod repo add master {repo_url}"
        cocoapods_warehouse_clone("master", add_master_command, master_path, "设置 CocoaPods Master 源")

# Cocoapods 仓库克隆
def cocoapods_warehouse_clone(warehouse, command, save_path, description):
    run_command(command, description=description)
    # 检查 CocoaPods 仓库是否成功克隆
    if not os.path.isdir(save_path):
        retry = input(f"CocoaPods {warehouse} 仓库克隆失败, 是否重试? (y/n): ").strip().lower()
        if retry == 'y':
            cocoapods_warehouse_clone(warehouse, command, save_path, description)
        else:
            print(f"CocoaPods {warehouse} 仓库克隆失败,请检查网络连接或仓库地址是否正确。")
            sys.exit(1)

def check_ruby_and_rvm_installed():
    """
    检查 Ruby 和 RVM 是否安装
    """
    rvm_result = run_command("rvm --version", check=False)
    if rvm_result.returncode == 0:
        ruby_result = run_command("ruby --version", check=False)
        if ruby_result.returncode == 0:
            return True
        else:
            print("Ruby 未正确安装,请先安装Ruby")
            return False
    else:
        print("RVM 未正确安装,请先安装RVM")
        return False

# 检查并安装 SVN
def check_and_install_svn():
    if check_homebrew_installed():
        result = run_command("svn --version", check=False)
        if result.returncode == 0:
            reinstall = input("检测到SVN已安装,是否需要卸载后重新安装最新版本? (y/n): ").strip().lower()
            if reinstall == 'y':
                run_command("brew uninstall subversion", description="卸载 SVN")
                run_command("brew install svn", description="重新安装 SVN")
        else:
            if check_tool_install("SVN"):
                run_command("brew install svn", description="安装 SVN")
    else:
        print("Homebrew 未正确安装,请先安装 Homebrew")
        sys.exit(1)

def main():
    # 安装 Homebrew
    check_and_install_homebrew()
    # 安装 GPG
    check_and_install_gpg()
    # 安装 RVM
    check_and_install_rvm()
    # 安装 Ruby
    check_and_install_ruby()
    # 安装 CocoaPods
    check_and_install_cocoapods()
    # 安装 SVN
    check_and_install_svn()
    print("\niOS开发环境已配置(检查)完毕,可以愉快的撸代码了...\n")

if __name__ == "__main__":
    main()

如您在使用过程中发现BUG,或有好的意见建议,可发邮件至mobileAppDvlp@icloud.com联系我。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容