利用 iCloud Drive 来同步 Xcode 配置

前言

多年以前一位老程序员告诉笔者代码片段(code snippets)是程序员的财富,他有一个 U 盘,里面装着他的财富。每当他需要切换电脑写代码的时候,他就会把把精心配置的字体、主题、代码片段等部署到新电脑上,然后开始高速编码。每次看他写代码都是一种享受,不过这是另一个故事了。

需求

多年之后,笔者也终于凑够了钱买了自己的 Mac,闲暇无事的时候也会写写代码祭奠下逝去的青春。但是某些时候总会觉得很别扭,例如感觉字体和单位的有细小的差距,或者一个代码片段怎么也按不出来——最后发现是没有在这台电脑配置这段代码片段。这种事发生的事情多了之后,就会感觉厌烦,同样的操作为什么得重复两次、三次?或者拿出吃了几年灰的 U 盘抽插在各地的电脑上人工同步?就不能有什么办法可以一次更改多次应用?笔者稍微一拍脑门,想到了今天的主角——iCloud Drive

  1. 为什么使用 iCloud Drive?
    • 因为这是苹果本家的网盘,嵌入系统中,只要开启我们就无需关心上传下载,正如 OneDrive 在 Windows 一样,我们只需要把文件放进去,他就会自动开始上传,并在你的每一台苹果设备上同步。利用这点我们就能方便的做到在不同的设备上同步 Xcode 配置文件,无需手动同步或者上传下载。
  2. 其他的替代方案
    • GitHub 之类的大型同性交友网站
      • 目前想来用 git 应该更好更方便,不过实现起来有点复杂,有能力的朋友可以自己动手
    • OneDrive/坚果云等网盘
      • 我觉得能有自带的还是用自带的吧

思路

总所周知 Xcode 的代码片段是保存在~/Library/Developer/Xcode/UserData/CodeSnippets路径下的,附近位置还有主题等配置信息。基于笔者的经验我们只需要备份同级目录下的 CodeSnippets、FontAndColorThemes 和 KeyBindings 三个子目录就行了。每当我们修改了代码片段、主题或者快捷键,把对应的文件放在 iCloud Drive 同步,当在其他电脑上时就使用最新的覆盖到对应目录即可。

脚本

虽说思路如此,但是笔者肯定不敢把这种三岁小孩子就能分析出来的东西发出来糊弄人。所以为了简化这个繁琐而又机械的操作,笔者编写了这样一个脚本:

#!/usr/bin/env bash

set -euo pipefail

################# variable define ##########
now=`date "+%Y%m%d%H%M%S"`

red=`tput setaf 1`
green=`tput setaf 2`
yellow=`tput setaf 3`
reset=`tput sgr0`

xcode_dir="${HOME}/Library/Developer/Xcode/UserData"
cloud_backup_dir="${HOME}/Library/Mobile Documents/com~apple~CloudDocs/XcodeBackup"
local_backup_dir="${HOME}/资源/归档/XcodeBackup"

code_snippets="CodeSnippets"
font_and_color_themes="FontAndColorThemes"
key_bindings="KeyBindings"

########### MAIN ##################
# check directory exist
if [ ! -d "${cloud_backup_dir}" ]; then
    echo "${red}iCloud Drive备份路径不存在!${reset}"
    mkdir -p "${cloud_backup_dir}"
    echo "${green}自动创建iCloud Drive备份路径:${reset}${cloud_backup_dir}"
    else
    echo "${green}iCloud Drive备份路径:${reset}${cloud_backup_dir}"
fi

if [ ! -d "${local_backup_dir}" ]; then
    echo "${red}本地备份路径不存在!${reset}"
    mkdir -p "${local_backup_dir}"
    echo "${green}自动创建本地备份路径:${reset}${local_backup_dir}"
    else
    echo "${green}本地备份路径:${reset}${cloud_backup_dir}"
fi

# zip files
cd "${xcode_dir}"
zip -r "${cloud_backup_dir}/XcodeBackup+${now}.zip" "${code_snippets}" "${font_and_color_themes}" "${key_bindings}" &
zip -r "${local_backup_dir}/XcodeBackup+${now}.zip" "${code_snippets}" "${font_and_color_themes}" "${key_bindings}" &

wait

# delete unnecessary backup files
num=`ls -l "${cloud_backup_dir}" |grep "^-"|wc -l`
if [ ${num} -gt 5 ]; then
    num=`expr ${num} - 5`
    cd "${cloud_backup_dir}"
    ls -tr "${cloud_backup_dir}" | head -${num} | xargs rm
fi

num=`ls -l "${local_backup_dir}" |grep "^-"|wc -l`
if [ ${num} -gt 5 ]; then
    num=`expr ${num} - 5`
    cd "${local_backup_dir}"
    ls -tr "${local_backup_dir}" | head -${num} | xargs rm
fi

简化了这个繁琐的操作,仅需在开机的时候跑一下,就能达到自动备份的效果。功能也是十分的简单:

  1. 首先创建了两个备份 Xcode 配置文件的路径,一个在云端,一个在本地(本地路径大家可以自行配置,一般也不会用上)。
  2. 然后把 Xcode 归档到这两处各一份,笔者这里选用 zip 包而不是更高压缩比的 7zip 等是因为想做通用一点便于大家开箱即用,不需要额外安装其他软件。
  3. 最后将多次运行后生成的老包删除,只保留最新的 5 个,以便节约宝贵的空间(毕竟笔者比较穷只舍得用免费的 5g 版)

有了这个脚本之后,大家只需要坚持开机的时候跑一跑就行了。笔者喜欢每天开机就更新下 cocoapods、brew、brew cask 这类的,所以就写了个脚本,刚好顺便也就备份一下。脚本思路大致如下,因为和主题无关就不细说了。

#!/usr/bin/env bash

open 自用魔法丝袜之影

wait

pod repo update --verbose &
更新Homebrew cask &
备份各种币钱包 &

备份Xcode等IDE配置文件 &

wait

killall 自用魔法丝袜之影

不过这样其实也不是很方便,毕竟打开 terminal 输入指令都很烦了,难道还要手动计算这台电脑的配置是否是最新的?然后再考虑是不是需要把云盘里面的配置解压到指定的位置覆盖?而且很有可能在做这些前已经把这台电脑的配置当最新版上传到云盘里了。

让所有的电脑用同一个版本的配置

笔者再次进行了思考。如果可以根据这些文件的最后修改日期和备份的文件进行比较,谁新就用哪个版本,那么不就实现了吗?只要我们确保每次修改都跑一次脚本,每次开机都跑一次,就能达到我们想要的效果了。至于如何判断文件的最后修改时间,笔者认为只需要一个根据文件名生成的 key 和一个对应的文件的最后修改时间做 value 的数据结构就行了(虽说也可以把备份的文件展开比较,但是因为笔者才疏学浅,尚不知如何操作,就只能通过键值对来判断了)
不过实际操作起来,再次彰显了笔者的才疏学浅,笔者也不知道如何在 bash 中创建一个高效并能持久化的键值对,如果哪位大佬知道请务必告诉笔者。
最后笔者想到 Mac 自带的 SQLite3,虽说这样一个小小的功能上数据库是有一点高射炮打蚊子,但是能跑就行吧。脚本如下;

#!/usr/bin/env bash

set -euo pipefail

################# variable define ##########
now=`date "+%Y%m%d%H%M%S"`

red=`tput setaf 1`
green=`tput setaf 2`
yellow=`tput setaf 3`
reset=`tput sgr0`

xcode_dir="${HOME}/Library/Developer/Xcode/UserData"
cloud_backup_dir="${HOME}/Library/Mobile Documents/com~apple~CloudDocs/XcodeBackup"
local_backup_dir="${HOME}/资源/归档/XcodeBackup"

xcode_backup_database="${HOME}/Library/Mobile Documents/com~apple~CloudDocs/.BackupDatabase"

code_snippets="CodeSnippets"
font_and_color_themes="FontAndColorThemes"
key_bindings="KeyBindings"

temp="DoNotModify"
database="${xcode_backup_database}/${temp}"

########### MAIN ##################
# check directory exist
if [ ! -d "${cloud_backup_dir}" ]; then
    echo "${red}iCloud Drive备份路径不存在!${reset}"
    mkdir -p "${cloud_backup_dir}"
    echo "${green}自动创建iCloud Drive备份路径:${reset}${cloud_backup_dir}"
else
    echo "${green}iCloud Drive备份路径:${reset}${cloud_backup_dir}"
fi

if [ ! -d "${local_backup_dir}" ]; then
    echo "${red}本地备份路径不存在!${reset}"
    mkdir -p "${local_backup_dir}"
    echo "${green}自动创建本地备份路径:${reset}${local_backup_dir}"
else
    echo "${green}本地备份路径:${reset}${cloud_backup_dir}"
fi

if [ ! -d "${xcode_backup_database}" ]; then
    echo "${red}同步数据库路径不存在!${reset}"
    mkdir -p "${xcode_backup_database}"
    echo "${green}自动创建数据库路径:${reset}${local_backup_dir}"
else
    echo "${green}数据库路径:${reset}${cloud_backup_dir}"
fi

sqlite3 "${database}" 'create table if not exists backupXcode(id integer primary key not NULL,key integer unique not NULL,value integer not NULL);'

#获取最后修改时间
cd "${xcode_dir}"
if [ -d "./${code_snippets}" ]; then
    find "./${code_snippets}" -type f >> ${temp}
fi
if [ -d "./${font_and_color_themes}" ]; then
    find "./${font_and_color_themes}" -type f >> ${temp}
fi
if [ -d "./${key_bindings}" ]; then
    find "./${key_bindings}" -type f >> ${temp}
fi

while read path; do
    key=`md5 -q -s "${path}"`
    value=`stat -f "%m" "${path}"`
    isModify=`sqlite3 "${database}" "select value from backupXcode where key == '${key}';"`
    if [ -z ${isModify} ]; then
        echo "${yellow}本地Xcode配置尚未同步${reset}!"
        num=`ls -l "${cloud_backup_dir}" |grep "^-"|wc -l`
        if [ ${num} -ge 1 ]; then
            echo "${green}找到最新的Xcode配置,开始自动替换${reset}!"

            cd "${xcode_dir}"
            ## backup before
            zip -r "XcodeBackup.zip" "${code_snippets}" "${font_and_color_themes}" "${key_bindings}" &
            wait

            cd "${cloud_backup_dir}"
            newBackup=`ls -t | head -1`

            unzip -u "${newBackup}" -d "${xcode_dir}" &
            wait

            cd "${xcode_dir}"
            rm ${temp}
            if [ -d "./${code_snippets}" ]; then
                find "./${code_snippets}" -type f >> ${temp}
            fi
            if [ -d "./${font_and_color_themes}" ]; then
                find "./${font_and_color_themes}" -type f >> ${temp}
            fi
            if [ -d "./${key_bindings}" ]; then
                find "./${key_bindings}" -type f >> ${temp}
            fi
            echo 更新数据库...
            while read path; do
                key=`md5 -q -s "${path}"`
                value=`stat -f "%m" "${path}"`
                sqlite3 "${database}" "insert or replace into backupXcode values(NULL,'${key}',${value});" &
            done < ${temp}
        fi
        break
    fi
    if [ ${isModify} != ${value} ]; then
        if [ ${isModify} -gt ${value} ]; then
            echo "${yellow}本地Xcode配置超前${reset}!"
        else
            echo "${yellow}本地Xcode配置已经过期${reset}!"

            num=`ls -l "${cloud_backup_dir}" |grep "^-"|wc -l`
            if [ ${num} -ge 1 ]; then
                echo "${green}找到最新的Xcode配置,开始自动替换${reset}!"

                cd "${xcode_dir}"
                ## backup before
                zip -r "XcodeBackup.zip" "${code_snippets}" "${font_and_color_themes}" "${key_bindings}" &
                wait

                cd "${cloud_backup_dir}"
                newBackup=`ls -t | head -1`

                unzip -o "${newBackup}" -d "${xcode_dir}" &
                wait
            fi
        fi
        cd "${xcode_dir}"
        rm ${temp}
        if [ -d "./${code_snippets}" ]; then
            find "./${code_snippets}" -type f >> ${temp}
        fi
        if [ -d "./${font_and_color_themes}" ]; then
            find "./${font_and_color_themes}" -type f >> ${temp}
        fi
        if [ -d "./${key_bindings}" ]; then
            find "./${key_bindings}" -type f >> ${temp}
        fi
        echo 更新数据库...
        while read path; do
            key=`md5 -q -s "${path}"`
            value=`stat -f "%m" "${path}"`
            sqlite3 "${database}" "insert or replace into backupXcode values(NULL,'${key}',${value});"
        done < ${temp}
        break
    fi
done < ${temp}

wait
rm ${temp}

# zip files
cd "${xcode_dir}"
zip -r "${cloud_backup_dir}/XcodeBackup+${now}.zip" "${code_snippets}" "${font_and_color_themes}" "${key_bindings}" &
zip -r "${local_backup_dir}/XcodeBackup+${now}.zip" "${code_snippets}" "${font_and_color_themes}" "${key_bindings}" &

wait

# delete unnecessary backup files
num=`ls -l "${cloud_backup_dir}" |grep "^-"|wc -l`
if [ ${num} -gt 5 ]; then
    num=`expr ${num} - 5`
    cd "${cloud_backup_dir}"
    ls -tr "${cloud_backup_dir}" | head -${num} | xargs rm
fi

num=`ls -l "${local_backup_dir}" |grep "^-"|wc -l`
if [ ${num} -gt 5 ]; then
    num=`expr ${num} - 5`
    cd "${local_backup_dir}"
    ls -tr "${local_backup_dir}" | head -${num} | xargs rm
fi

后记

笔者简单测试了一下,基本上能用。以此思路,应该也可用在 Alfred、vimrc 等配置文件。不过依旧不是很方便,不过笔者才疏学浅,目前也就这个水平了,希望能对大家有所帮助,不知道大家有没有什么好的建议?笔者认为可以在 Xcode 关闭时自动运行本脚本,但是尚未找到好的胡克点(:」∠)_,如果大家有什么好的建议,欢迎PR

就这样吧,下台鞠躬!!!

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

推荐阅读更多精彩内容