iOS CI

如何部署CI

Posted by Genie on April 20, 2019

Just Record

shortcut shift + command + . show hidden file and directory

iOS持续集成构建

set name package.sh

1
2
3
4
5
pod install --verbose --no-repo-update &&  \
xcodebuild -workspace LZAlbum.xcworkspace -scheme LZAlbum archive -archivePath ./build/LZAlbum.xcarchive && \
rm -rf ./build/LZAlbum.ipa && \
xcodebuild -exportArchive -exportFormat ipa -archivePath build/LZAlbum.xcarchive -exportPath build/LZAlbum.ipa && \
fir p build/LZAlbum.ipa -T a28f662136fa0813766ffac83d02e7ff

set name build and upload Pgyer.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
Project_name="GACTravel_ClientUAT"

#根据配置环境构建 Release or Debug
buildType=Release

#自动更新SVN代码  start
#svn update
echo "git pull starting"
git pull
echo "git pull ending"

#输出打包文件目录
output_path=~/Desktop/ipa-build1
if [ -d $output_path ];then
rm -rf $output_path
fi
mkdir $output_path

#记录执行脚本的初始化目录
project_path=$(pwd)
compiled_path=${project_path}/build/Release-iphoneos

appdirname=$Project_name
echo "Current workspace is $(pwd)"

#清理工程
/usr/bin/xcodebuild -target $Project_name clean
#编译工程
xcodebuild -workspace *.xcwork* -scheme $Project_name -configuration ${buildType} \
CONFIGURATION_BUILD_DIR=${compiled_path} \
ONLY_ACTIVE_ARCH=NO \
CODE_SIGN_IDENTITY="iPhone Developer: mengying liu (M482273DM5)" || exit

#打包ipa文件存放到桌面
xcrun -sdk iphoneos PackageApplication -v build/Release-iphoneos/*.app -o ${output_path}/${appdirname}.ipa || exit

#删除临时文件
rm -rf build

#打开ipa桌面目录
#open $output_path

#上传蒲公英网站
echo '/+++++++ 上传蒲公英 +++++++/'
PASSWORD=123456
#将git最后一次提交作为更新说明
MSG=`git log -10 --pretty=format:"%h - %cn, %cd : %s"`
curl -F "file=@$output_path/$appdirname.ipa" \
-F "uKey=333a7c7c0848515c7cf42d16ddf73aba" \
-F "_api_key=d6d3680bd40811728059f92dd6fe3b5d" \
-F "updateDescription=${MSG}" \
-F "password=${PASSWORD}" \
https://qiniu-storage.pgyer.com/apiv1/app/upload

set name autobuild.sh

name build_local.sh

1
2
3
4
5
6
7
8
9
10
export CONFIGURATION=Debug
export WORKSPACE=/Users/shaneochen/Work/src/gactravel_driver_ios
echo "WORKSPACE=$WORKSPACE"
export LOCAL_BUILD=1
echo "LOCAL_BUILD=$LOCAL_BUILD"
export SDK=$compileEnv
echo "SDK=$SDK"
sudo xcode-select -switch /Applications/Xcode.app/Contents/Developer
export XCODE_PATH=xcodebuild
sh ./build.sh

name build.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#----------------------
# 功能:构建脚本的入口函数,这里面体现脚本的构建流程,调用其他辅助函数
# ----------------------
function main()
{
  echo "进入环境变量设置过程"
  set_env
#   echo "进入编译前配置更新过程"
#   pre_config
  if [ ! -n "$LOCAL_BUILD" ]; then
    echo "进入编译前历史及临时目录清理过程"
    clean
  fi
  echo "进入项目编译过程"
  build
  echo "进入编译后打包归档过程"
  archive
}
 
#----------------------
# 功能:定义构建过程中所用到的所有变量
# ----------------------
function set_env()
{
   # 全局变量
   export TargetName='GACTravel_ClientUAT'
   echo "TargetName=$TargetName"
   export WorkspaceName='GACTravel_Client'
   echo "WorkspaceName=$WorkspaceName"
   echo "CONFIGURATION=$CONFIGURATION"
   export LC_ALL=en_US.UTF-8
   export DisplayDate=`date '+%m-%d_%H:%m'`
   echo "DisplayDate=$DisplayDate"
   export DerivedDir=$WORKSPACE/Temp
   echo "DerivedDir=$DerivedDir"
   export BUILD_FOLDER=$DerivedDir/Build/Products/$CONFIGURATION-iphoneos
   echo "BUILD_FOLDER=$BUILD_FOLDER"
   export appVersion=${MajorVersion}.${MinorVersion}.${FixVersion}
   echo "appVersion=$appVersion"
   export buildVersion=${BuildNo}
   echo "buildVersion=$buildVersion"
   export BM=${TargetName}_${CONFIGURATION}_${NumberVersion}
   echo "BM=$BM"

    if [ ! -n "$LOCAL_BUILD" ]; then
		export SDK=$compileEnv
		echo "SDK=$SDK"
		export XCODE_PATH=$XCODE_PATH$compileEnv
	fi
   echo "XCODE_PATH=$XCODE_PATH"
   export RESULT_FOLDER=$WORKSPACE/result
   echo "RESULT_FOLDER=$RESULT_FOLDER"
   export Plist_PATH=$WORKSPACE/GACTravel_Client/Resource/Plist/GACTravel_ClientUAT-Info.plist
   echo "Plist_PATH=$Plist_PATH"
   
   export BundleDisplayName="广汽出行"
}
#----------------------
# 功能:编译前项目个性化配置
# ----------------------
# function pre_config()
# {
#   # 自定义一些编译前需要修改的操作
# }
#----------------------
# 功能:对编译输出目录进行清理
# ----------------------
function clean()
{
    if [ "$IncrementalBuild" = "true" ]; then
        rm -rf $BUILD_FOLDER
    else
        if [ "$XCPRETTY" = "true" ]; then
            set -o pipefail && $XCODE_PATH -workspace $WorkspaceName.xcworkspace -scheme $TargetName -configuration $CONFIGURATION clean | xcpretty
        else
            $XCODE_PATH -workspace $WorkspaceName.xcworkspace -scheme $TargetName -configuration $CONFIGURATION clean -sdk $SDK | xcpretty
        fi
    fi
}
#----------------------
# 功能:项目构建执行过程脚本
# ----------------------
function build()
{
  if [ ! -n "$LOCAL_BUILD" ]; then
	  /usr/libexec/PlistBuddy -c "Set :CFBundleVersion ${buildVersion}" $Plist_PATH
	  /usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString ${appVersion}" $Plist_PATH

	  export newName=${BundleDisplayName}${BuildNo}
	  echo "newName=${newName}"
	  /usr/libexec/PlistBuddy -c "Set :CFBundleDisplayName ${newName}" $Plist_PATH
  fi
  
  echo "开始进行入编译执行过程"
  if [ "$XCPRETTY" != "false" ]; then
  	echo "set -o pipefail && $XCODE_PATH -workspace $WorkspaceName.xcworkspace -scheme $TargetName -configuration $CONFIGURATION -derivedDataPath $DerivedDir"
    set -o pipefail && $XCODE_PATH -workspace $WorkspaceName.xcworkspace -scheme $TargetName -configuration $CONFIGURATION -derivedDataPath $DerivedDir 
  else
    if [ ! -n "$LOCAL_BUILD" ]; then
    $XCODE_PATH -workspace $WorkspaceName.xcworkspace -scheme $TargetName -configuration $CONFIGURATION -derivedDataPath $DerivedDir -sdk $SDK | xcpretty
    else
    echo "$XCODE_PATH -workspace $WorkspaceName.xcworkspace -scheme $TargetName -configuration $CONFIGURATION -derivedDataPath $DerivedDir -sdk $SDK"
    $XCODE_PATH -workspace $WorkspaceName.xcworkspace -scheme $TargetName -configuration $CONFIGURATION -derivedDataPath $DerivedDir -sdk $SDK
	fi
  fi
  if ! [ $? = 0 ]; then
    echo "编译过程失败,退出编译执行"
    exit 1
  fi
}
#----------------------
# 功能:编译结束后将需要在RDM前端展示的产出归档到result目录
# ----------------------
function archive()
{
    echo "开始编译后归档"
    echo "cd $BUILD_FOLDER ..."
    cd $BUILD_FOLDER
    rm -rf Payload
    mkdir Payload
    echo "ls $BUILD_FOLDER ..."
    ls $BUILD_FOLDER
    cp -r $TargetName.app Payload
    zip -rq $TargetName.ipa Payload
    if ! [ $? = 0 ]; then
        echo "压缩失败终止编译"
        exit 1
    fi
    
    rm -rf ${RESULT_FOLDER}
    mkdir ${RESULT_FOLDER}
    echo "cp $TargetName.ipa ${RESULT_FOLDER}/${BM}.ipa"
    cp $TargetName.ipa ${RESULT_FOLDER}/${BM}.ipa
}

main

fastlane

fastlane实际是由Ruby写的,使用Ruby的Gem安装

fastlane 文档

fastlane 相关案例

sudo gem install fastlane -NV

将终端导航到项目目录并运行 fastlane init

有你的Fastfile配置写在斯威夫特(测试版) fastlane init swift

Fastfile里面包含的块类型有四种:

before_all 用于执行任务之前的操作,比如使用cocopods更新pod库 after_all 用于执行任务之后的操作,比如发送邮件,通知之前的 error 用于发生错误的操作 lane 定义用户的主要任务流程。例如打包ipa,执行测试等等

如下面,来讲解一下lane的组成。

1
2
3
4
5
desc "Push a new beta build to TestFlight"   //该任务的描述
lane :beta do  //定义名字为 beta 的任务
  build_app(workspace: "expample.xcworkspace", scheme: "example") //构建App,又叫gym
  upload_to_testflight //上传到testfilght,
end

执行 fastlane beta

那么如何写一个我们属于自己的 lane 呢? 就以发布 ipa 到 fir 为例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
desc "发布到Fir"
lane :pulish_to_fir do
  # 运行 pod install 
  cocoapods 
  # 构建和打包ipa
  gym(
    clean: true,
    output_directory: './firim',
    scheme: 'xxxx',
    configuration: 'Test',
    export_options: {
      method: 'development',
      provisioningProfiles: {
          "xxx.xxx.xxx": "match Development xxx.xxx.xxx"
      },
    }
  )
  # 上传ipa到fir.im服务器,在fir.im获取firim_api_token
  firim(firim_api_token: "fir_token")
end

有问题可以联系Email