构建CICD

由于公司要求构建自己的CICD流程,基于公司内部搭建的服务平台去搭建自动打包流程,在这个过程中遇到了几个节点记录一下。

公司内部号称有自己的一套软管平台,实际内核都是基于Jekins的一套机制,不同的是有些参数可以自定义,由于公司是硬件起家,而App打包又没有既成的案例借鉴,所以这一套逻辑针对App还是有些难以入手的。

首先App的包是安装包,不同于前端和其他服务,直接生成运行环境供用户使用,而整体的软管平台是将所有的生成物打包成zip压缩文件,所以还是需要将成果物上传到测试环境的发包平台,以及testflight,最后再将成果物打包成zip供version记录。

其次打包的脚本实现,需要从svn拉去代码,然后配置服务器的打包环境,再使用脚本执行打包。

整个流程都是使用脚本实现,包括软管平台脚本和打包脚本
软管平台脚本如下:


#!/bin/bashsource /etc/profile#***********************************************************************
#FileName: buildPCI.sh
#Note:
#1、jenkins调用命令为./buildPCI.sh ${buildType} ${WORKSPACE}/${outputDir} ${userDefined}
#2、${WORKSPACE}/${outputDir}为绝对路径;${userDefined}为可选自定义参数,例子:userDefined="a b c" 则${3}=a、${4}=b、${5}=c
#3、组件下载参数:CI_COMMON_PATH为组件存放地址;modelList为组件名列表,调用copyDependent拷贝组件文件到源代码对应位置进行构建
#4、最终成果物为${OUTPUT_PATH}/*.zip 和${OUTPUT_PATH}/*.ini,其中OUTPUT_PATH=${2}
#升级包命名规则:请参考部门升级包命名规范
#5、ini文件内容如下:
#[prop]
#config=配置(STD、NEU、GM)
#language=语言(CN、EN、GML、ML)
#softVersion=Vx.y.z_yymmdd
#infileName=发布zip包名
#6、!!!开发仅需修改字符x所在处!!!
#**********************************自定义方法*************************************#入参的含义
function usage()
{echoecho "################### buildPCI.sh usage: ###################"echo "./buildPCI.sh \${buildType} \${outputDir} \${userDefined}"echo "@param [in] \$1: [release|debug] buildType"echo "@param [in] \$2: outputDir abs path,if not exist will create.if exist,the file will be added "echo "@param [in] \$3: [Produce] product type"echo "@param [in] \$4-..: product defined"echo -e "EGG:"echo -e "    ./buildPCI.sh release $(pwd) 1:$1-2:$2-3:$3-4:$4"echo "[ERROR]:***************************unknow param(three param least) ******************************"echo
}# 执行构建
function build()
{BUILD_PATH="${1}"BUILD_CMD="${2}"# copyDependentxxcd ${BUILD_PATH}echo "build_path:${BUILD_PATH}"chmod -R +x *.sh${BUILD_CMD}judgeResult "${BUILD_PATH} ${BUILD_CMD} build failed"cd -
}# 判断命令执行结果
function judgeResult()
{if [ $? -ne 0 ] ; thenjudgeResultPrint="${1}"echo "[ERROR]:***************************${judgeResultPrint}******************************"exit 1fi
}# generate_package_config_init config language softVersion infileName
function generate_package_config_init()
{#[prop]#config=配置(STD、NEU、GM)#language=语言(CN、EN、GML、ML)#softVersion=Vx.y.z_yymmdd#infileName=发布zip包名# 创建压缩包名称Product_File_ini="$1".iniecho "[prop]" >"$Product_File_ini"echo config="$2" >> "$Product_File_ini"echo language="$3" >> "$Product_File_ini"echo softVersion="$4">> "$Product_File_ini"echo infileName="$1.zip" >> "$Product_File_ini"
}# 遍历所有$1文件夹下所有文件拷贝到$2
function read_dir_cp_dst(){for file in `ls $1`doif [ -d $1"/"$file ]thenread_dir_cp_dst $1"/"$file $2elsefile_path=$1"/"$filecp $file_path $2fidone
}#************************************脚本开始执行***********************************# 打印所有的入参
echo "0:$0-1:$1-2:$2-3:$3-4:$4-5:$5"# 小于3个参数返回失败
if [ $# -le 3 ];thenusageexit 1
fi# 平台传递必选参数:buildType为release或debug;OUTPUT_PATH为绝对路径;
# commonPath为组件存放路径;component为依赖组件json信息,可解析成modelList组件名列表
buildType="${1}"
OUTPUT_PATH="${2}"
build_language="${3}"
ROOT_PATH=$(cd "$(dirname "$0")"/..;pwd)
softVersion="V${4}"PRODUCT_WORK_PATH=${ROOT_PATH}echo "PRODUCT_WORK_PATH:$PRODUCT_WORK_PATH"# 检查目标产品路径是否存在,如果不存在直接退出。
if [ ! -d "${PRODUCT_WORK_PATH}/build" ]; thenecho ${PRODUCT_WORK_PATH}" not exist exit"exit 1
fi# 使用平台配置的组件打包
# 定义运行脚本路径及参数BUILD_PATH=${PRODUCT_WORK_PATH}"/build"
if [ ${buildType} == "release" ]; thenshift 3BUILD_CMD="./build.sh ${build_tool_prefix} $@"
elif [ ${buildType} == "debug" ]; thenshift 3BUILD_CMD="./buildDebug.sh ${build_tool_prefix} $@"
elseecho "[ERROR]:***************************unknow buildType ${buildType}******************************"usageexit 1
fiecho "build ${BUILD_PATH} ${BUILD_CMD}"
# 执行构建
build "${BUILD_PATH}" "${BUILD_CMD}"#上述构建需生成同名的zip和ini文件,并且拷贝升级包到OUTPUT_PATH目录下
mkdir -p "$BUILD_PATH/xxx/"
# 进入打包的文件夹
cd "$BUILD_PATH/xxx/"# 创建压缩包名称
resultName="NB_$build_language""_STD_$softVersion""_$(date +%y%m%d)"
# 创建ini文件
generate_package_config_init "${resultName}" "STD" "$build_language" "$softVersion" "$resultName.zip"
zipName="${resultName}.zip"
iniName="${resultName}.ini"
# 将所有文件进行压缩
zip -q -r $zipName "${BUILD_PATH}/xxx/"
# 将压缩包剪切到output文件夹
mv -f "${BUILD_PATH}/xxx/$iniName" "${ROOT_PATH}/output/"
mv -f "${BUILD_PATH}/xxx/$zipName" "${ROOT_PATH}/output/"## 将打包的成果物和ini文件复制到output文件夹中
#rm -rf "${ROOT_PATH}/output/"
#mkdir -p "${ROOT_PATH}/output/"
#read_dir_cp_dst "${BUILD_PATH}/xxx/" "${ROOT_PATH}/output/"
### 定位到outpu文件夹
#cd "$ROOT_PATH/output/"# 获取当前目录下时间最新的zip文件名
#file_name=$(ls -lt "${BUILD_PATH}/xxx/" | grep '\.zip' | head -n 1 | awk '{print $NF}'|awk -F '.zip' '{print$1}')
#echo "mkdir file_name:$file_name+build_language:$build_language+softVersion:$softVersion+infileName:$infileName"
#generate_package_config_init "xxx" "STD" "$build_language" "$softVersion" "$infileName.zip"## 分析最新的zip文件名中的日期是否和当日的日期一致
#echo "fileDate:$file_name-$(echo $file_name|awk -F '_' '{print $NF}')"
#if [[ $(date +%y%m%d) == $(echo $file_name|awk -F '_' '{print $NF}') ]]; then
#    cp -rvf "$BUILD_PATH/xxx/${file_name}.zip" "${ROOT_PATH}/output/"
#    # generate ini
#    config_name_prefix=${ROOT_PATH}/$file_name
#    package_config=$(echo $file_name | awk -F "_" '{print$(NF-2)}')
#    language=$(echo $file_name | awk -F "_" '{print$(NF-3)}')
#    softVersion=$(echo $file_name | awk -F "_" '{print"V"$(NF-1)"_"$(NF)}' )
#    generate_package_config_init ${config_name_prefix} ${package_config} ${language} ${softVersion} ${file_name}.zip
#    # add debug ezapp_nostrip
#    cp ${ROOT_PATH}/build/ezapp_nostrip ${OUTPUT_PATH}
#    cd ${ROOT_PATH}
#    zip ${file_name}_ELF.zip ezapp_nostrip
#    rm ezapp_nostrip
#    tree ${ROOT_PATH}
#else
#    echo "build failed,not found generate file"
#fiexit 0# 遍历所有$1文件夹下所有文件拷贝到$2
function read_dir_cp_dst(){for file in `ls $1`doif [ -d $1"/"$file ]thenread_dir_cp_dst $1"/"$file $2elsefile_path=$1"/"$filecp $file_path $2fidone
}
# 使用平台配置的组件打包,$1平台组件目录 $2平台产品目录
function use_component_file(){temp_dir=$(pwd)/temp_compentif [ ! -d "${temp_dir}" ]; thenecho "${temp_dir} not found and mkdir "mkdir -p ${temp_dir}elseecho "${temp_dir} exist delete"rm -rf ${temp_dir}firead_dir_cp_dst $1  ${temp_dir}for file in `ls ${temp_dir}`doproduct_file=$(find $2 -name $file)if [ ${#product_file} -gt 0 ];thenecho "cp  ${temp_dir}/$file ${product_file}"cp  ${temp_dir}/$file ${product_file}if [ $? -ne 0 ] ; thenecho "replace ${file} and use platform file failed"exit 1elseecho "replace ${file} and use platform file success"fifidoneecho "clean tempdir rm -rf ${temp_dir}"rm -rf ${temp_dir}
}#平台传递必选参数:buildType为release或debug;OUTPUT_PATH为绝对路径;commonPath为组件存放路径;component为依赖组件json信息,可解析成modelList组件名列表
buildType="${1}"
OUTPUT_PATH="${2}"
build_product="${3}"
ROOT_PATH=$(cd "$(dirname "$0")"/../..;pwd)PRODUCT_WORK_PATH=${ROOT_PATH}/ci/${build_product}
# 检查目标产品路径是否存在,如果不存在直接退出。
if [ ! -d "${PRODUCT_WORK_PATH}" ]; thenecho ${PRODUCT_WORK_PATH}" not exist exit"exit 1
fi# 使用平台配置的组件打包
if [ ${buildType} == "release" ]; thenuse_component_file ../common ${PRODUCT_WORK_PATH}
fi
# 根据不同产品设置不同的编译
source ${PRODUCT_WORK_PATH}/build.sh
build_tool_prefix=${CROSS_TOOL_PREFIX}
if [[ ${#build_tool_prefix} == 0 ]]; thenecho "empty build toold prefix"exit 1
fi
echo "build ${build_product} cross tool prefix ${CROSS_TOOL_PREFIX}"#定义运行脚本路径及参数
if [ ${buildType} == "release" ]; thenBUILD_PATH=${PRODUCT_WORK_PATH}shift 3BUILD_CMD="./build.sh ${build_tool_prefix} $@"
elif [ ${buildType} == "debug" ] thenBUILD_PATH=${PRODUCT_WORK_PATH}shift 3BUILD_CMD="./buildDebug.sh ${build_tool_prefix} $@"
elseecho "[ERROR]:***************************unknow buildType ${buildType}******************************"usageexit 1
fi
echo "build ${BUILD_PATH} ${BUILD_CMD}"
#执行构建
build "${BUILD_PATH}" "${BUILD_CMD}"

打包脚本如下:


#!/bin/sh
export LANG=en_US.UTF-8
#计时
SECONDS=0# 项目名称
#project_name=`find . -name *.xcodeproj | awk -F "[/.]" '{print $(NF-1)}'`
project_name='xxx'
echo "工程名是: $project_name"# 当前时间(用于区分目录名打包时间)
now=$(date +"%Y-%m-%d_%H-%M-%S")
echo "打包时间是: $now"# scheme名称
scheme_name=${project_name}
echo "scheme名称是: $scheme_name"# 项目路径
# ~/NB-IOS/v1.0.0/ci/build
build_path=$(pwd)
ci_path=$(dirname "$(pwd)")
podfile_path=$(dirname "$ci_path")echo "build_path:$build_path"
echo "podfile_path:$podfile_path"version=$(xcodebuild -version | awk 'NR == 1 { print $2 }')
major_version=$(echo $version | cut -d'.' -f1)if [ "$major_version" == "14" ]; thenecho "Xcode version is 14"cd ${build_path}echo "进入${build_path}文件夹"echo '=============正在移除-ld_classic配置============='ruby ./remove.rb
elseecho "Xcode is not 14"
fi#导出目录
#export_path="./ci/build/${project_name}_${now}"
export_path="${build_path}/${project_name}"
echo "导出目录是: $export_path"#ipa包路径(用于检验是否导出成功)
export_ipa_path="${export_path}/${project_name}.ipa"
echo "ipa包路径是: $export_ipa_path"#编译build路径
archive_path="${export_path}/${project_name}.xcarchive"
echo "编译build路径是: $archive_path"# 打包配置plist文件路径【这个文件需要先创建】
plist_path="${build_path}/archive_release.plist"#打包方式
#build_type="project"
# workspace/xcodeproj 路径(根据项目是否使用cocoapod,确定打包的方式)
#if [ -e "${podfile_path}${project_name}-iOS.xcworkspace" ];then
workspace_path="${project_name}-iOS.xcworkspace"
build_type="workspace"
# 执行pod
cd ${podfile_path}
pod install#else
#workspace_path="${podfile_path}/${project_name}.xcodeproj"
echo "工程路径是: $workspace_path"
#build_type="project"
#fi
#echo ${workspace_path}# scheme名称
scheme_name=${project_name}# 配置打包样式:Release/ad-hoc/Debug
configuration='Release'
echo "打包样式是: $configuration"echo '=============正在清理工程============='
echo $configurationxcodebuild \
clean -${build_type} ${workspace_path} \
-scheme ${scheme_name} \
-configuration ${configuration} -quiet  || exitecho '清理完成-->>>--正在编译工程:'${workspace_path}
# build
if [ -d ${workspace_path} ];thenxcodebuild archive -${build_type} ${workspace_path} \-scheme ${scheme_name} \-configuration ${configuration} \-archivePath ${archive_path} -quiet || exit
elseecho 'workspace 不存在'
fi# 检查是否构建成功(build)
if [ -d ${archive_path} ] ; thenecho '=============项目 build 成功============='
elseecho '=============项目 build 失败============='exit 1
fi# exprot
echo '编译完成-->>>--开始ipa打包'
xcodebuild -exportArchive -archivePath ${archive_path} \
-configuration ${configuration} \
-exportPath ${export_path} \
-exportOptionsPlist ${plist_path} \
-quiet || exitif [ -e ${export_ipa_path} ]; then#删除编译包文件rm -rf $archive_pathecho '=============ipa包导出成功============='
elseecho '=============ipa包导出失败============'echo "${export_ipa_path}"exit 1
fi#打包完成,打开目录
#open ${export_path}# 输出总用时
echo "执行耗时: ${SECONDS}秒"exit 0

注意:

由于Xcode15采用了新的链接器(Linker),被称作 ld_prime 。新的连接器有诸多好处,尤其是对合并库的支持方面。然而,链接器的升级可能会出现不兼容老库的情况出现。遇到这种情况,可以通过恢复旧的连接器来解决这个问题。从 Other Linker Flags 添加 -ld_classic选择使用旧的链接器,而不是默认的新的 -ld_prime 链接器。同时由于服务器还是使用 xcode 14,所以目前只能在 release 环境下去掉 -ld_classic ,用来暂时支持自动打包,后续再研究其他兼容性方案。

后续:

为了解决上述 注意: 中遗留的问题,首先思考能否使用脚本的方式移除 xcode 的某些配置项,因为我们的打包流程全部使用的脚本来实现的,所以采用脚本也在可行性的考虑范围内。最后找到了用脚本的方式动态移除 -ld_classic 的配置项的解决方案(该脚本是 .rb 文件)。上述文章中的脚本也是添加完移除配置项的最新脚本
具体代码如下:


#!/usr/bin/env ruby
require 'xcodeproj'
taget_name='xxx'
current_folder_path = __dir__
ci_folder_path = File.dirname(__dir__)
podfile_folder_path = File.dirname(ci_folder_path)
#xcodeproj_path="$podfile_path/$project_name.xcodeproj"
xcodeproj_path="#{podfile_folder_path}/#{taget_name}.xcodeproj"
puts "xcodeproj_path:#{xcodeproj_path}"project = Xcodeproj::Project.open(xcodeproj_path)
target=project.targets.find { |t| t.name == "#{taget_name}" }target.build_configurations.each do |config|flags = config.build_settings['OTHER_LDFLAGS']flags.reject! { |flag| flag == '-ld_classic' }config.build_settings['OTHER_LDFLAGS'] = flags
endproject.save
puts "移除 -ld_classic 成功"

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/816071.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

leetcode46--全排列

题目 给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。 示例 1: 输入:nums [1,2,3] 输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]示例 2: 输入:…

Vue新手入门

1 Vue概述 官网:https://cn.vuejs.org/ 1、什么是Vue.js Vue.js 是目前最火的一个前端框架,React是最流行的一个前端框架(React除了开发网站,还可以开发手机App, Vue语法也是可以用于进行手机App开发的,需要借助于W…

C语言数组的初始化方法大全

在C语言中,数组初始化主要有以下几种方法: 1.完全初始化:在声明数组的同时给出数组所有元素的值。 int arr[5] {1, 2, 3, 4, 5}; // 初始化一个整型数组 2.部分初始化:只给数组的前面一部分元素赋值&am…

Bridge 桥接

意图 将抽象部分与其显示部分分离,使他们都可以独立地变化。 结构 其中: Abstraction定义抽象类的接口,维护一个指向Implementer类型对象的指针。RefinedAbstraction扩展由Abstraction定义的接口。Implementor定义实现类的接口&#xff0c…

React 19 的新增功能:Action Hooks

React 是前端开发领域最流行的框架之一。我喜欢 React 是因为它背后的团队和社区对它的热情。当社区提出新功能和改进的需求时,团队会倾听,React 的未来是令人兴奋和有趣的。 让我们来看一下 React 19 中令开发人员提升开发效率的新特性。对于每个钩子&…

关于项目打包

除了自己常用的那种方式,也可以直接在文件夹下执行命令。 如果当前项目聚合了其他子模块的话: 先清理,再打包,同时跳过测试 如果打包后,然后项执行某个模块,进入当前文件夹下直接java -jar 和jar包名执行就…

C++中的vector容器

一. 基本概念 1. 包含在头文件 #include <vector> 2. 功能: 模拟了一个动态数组 3. 底层实现 首先开辟一定大小的数组 随着元素的增加&#xff0c;如果空间不够之后 自动采取扩容机制 -> 自增长 扩容规则&#xff1a;以原空间大小的 2 倍重新开辟一块空间 将就空…

【SpinalHDL】Scala编程中的class及case class

本篇文章仅简单介绍在spinalhdl编程中遇到的比较常见的2中类定义方式&#xff1a;class及case class。对于不太了解JAVA或Scala编码又开始学习SpinalHDL的人进行入门介绍。 在 SpinalHDL 中&#xff0c;case class 和 class 都是用来定义数据结构或对象的关键字&#xff0c;它…

第五十二章 进程亲和性和状态感知模式(保留模式 1) - 启动状态感知模式

文章目录 第五十二章 进程亲和性和状态感知模式&#xff08;保留模式 1&#xff09; - 启动状态感知模式维护状态感知模式并响应错误终止状态感知模式 第五十二章 进程亲和性和状态感知模式&#xff08;保留模式 1&#xff09; - 启动状态感知模式 通过设置保留模式将会话标记…

k8s:kubectl 命令设置简写启用自动补全功能

k8s&#xff1a;kubectl 命令设置简写&启用自动补全功能 1、设置kubectl命令简写2、启用kubectl自动补全功能 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; Kubernetes&#xff08;K8s&#xff09;是一个强大的容器编排平台&#xff0…

恢复MySQL!是我的条件反射,PXB开源的力量...

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3; 哈喽&#xff01;大家好&#xff0c;我是【IT邦德】&#xff0c;江湖人称jeames007&#xff0c;10余年DBA及大数据工作经验 一位上进心十足的【大数据领域博主】&#xff01;&#x1f61c;&am…

[leetcode 链表] 反转链表 vs 链表相交

1. 反转链表 E :::details 给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[5,4,3,2,1] 示例 2&#xff1a; 输入&#xff1a;head [1,2] 输出&#xff1a;[2,1]…

【设计模式】SOLID设计原则

1、什么是SOLID设计原则 SOLID 是面向对象设计中的五个基本设计原则的首字母缩写&#xff0c;它们是&#xff1a; 单一职责原则&#xff08;Single Responsibility Principle&#xff0c;SRP&#xff09;&#xff1a; 类应该只有一个单一的职责&#xff0c;即一个类应该有且只…

js和ES的关系

ES和JS之间的关系是&#xff1a;ES&#xff08;ECMAScript&#xff09;是JS&#xff08;JavaScript&#xff09;的一个规范或者标准&#xff0c;而JS则是ES的实现。具体来说&#xff0c;JavaScript 是一种在浏览器中运行的脚本语言&#xff0c;用于实现网页的交互功能。而 ECMA…

力扣面试150 分发糖果 分步贪心

Problem: 135. 分发糖果 思路 &#x1f468;‍&#x1f3eb; 参考&#xff1a;代码随想录 一次是从左到右遍历&#xff0c;只比较右边孩子评分比左边大的情况。一次是从右到左遍历&#xff0c;只比较左边孩子评分比右边大的情况。 复杂度 时间复杂度: O ( n ) O(n) O(n) …

低成本,高效能:探索物联网新宠LoRa

LoRa是什么&#xff1f; LoRa是一种物联网无线传输技术&#xff0c;利用调制解调器实现低功耗远距离数据传输。其基本工作原理是通过基站发送数据到特定终端设备&#xff0c;实现双向数据传输。 LoRa无线传输技术是一种为低功耗和低成本设计的无线技术&#xff0c;用于实现远距…

【Linux】CentOS 7安装后没有图形界面

专栏文章索引&#xff1a;Linux 有问题可私聊&#xff1a;QQ&#xff1a;3375119339 目录 一、项目场景 二、问题描述 三、原因分析 四、解决方案 1.当前处于命令行界面&#xff0c;可以切换为图形界面 2.安装时没有安装图形界面&#xff0c;选择了Minimal Install 3.下…

鸿蒙端云一体化开发--开发云函数--适合小白体制

开发云函数 那什么是云函数&#xff1f;我们将来又怎么去使用这个云函数呢&#xff1f; 答&#xff1a;我们之前要编写一些服务端的业务逻辑代码&#xff0c;那现在&#xff0c;在这种端云一体化的开发模式下&#xff0c;我们是把服务端的业务逻辑代码&#xff0c;通过云函数来…

linux安装和使用-第一天

一. 安装linux系统 安装过程:略注意事项: 安装时一定一定一定不要选择有中文的目录包括镜像文件所在的目录,否则会发生各种问题,比如VMware Tools是灰色的.1. 安装ssh工具 (1) 安装命令 # 第一次安装系统需要更新一下apt的源,他维护了软件依赖关系,否则安装不了软件,每次安装…

网络安全教程及案例分析

一、网络安全教程 &#xff08;一&#xff09;网络安全基础知识 计算机基础知识&#xff1a;了解计算机的硬件、软件、操作系统和网络结构&#xff0c;有助于我们更好地理解网络安全的概念和技术。这些基础知识为我们提供了对计算机系统的全面认识&#xff0c;从而能够更准确…