在项目中看到以前同事写的自动打包并上传蒲公英脚本,就萌发了用原生swift或者OC可不可以编写脚本的想法。查阅相关资料后发现是可行的。
1、Process是一个可以执行终端命令的类
我们给Process
扩展一个便捷方法执行终端命令
extension Process {
/// 执行命令
/// - Parameters:
/// - launchPath: 命令路径
/// - arguments: 命令参数
/// - currentDirectoryPath: 命令执行目录
/// - environment: 环境变量
/// - Returns: 返回执行结果
static func executable(launchPath:String,
arguments:[String],
currentDirectoryPath:String? = nil,
environment:[String:String]? = nil)->Pipe{
let process = Process()
process.launchPath = launchPath
process.arguments = arguments
if let environment = environment {
process.environment = environment
}
if let currentDirectoryPath = currentDirectoryPath {
process.currentDirectoryPath = currentDirectoryPath
}
let pipe = Pipe()
process.standardOutput = pipe
process.launch()
return pipe
}
}
2、xcodebuild命令
-
Clean
xcodebuild clean -workspace <workspaceName> -scheme <schemeName> -configuration <Debug|Release>
-
Archive
xcodebuild archive -archivePath <archivePath> -project <projectName> -workspace <workspaceName> -scheme <schemeName> -configuration <Debug|Release>
-
Export
xcodebuild -exportArchive -archivePath <xcarchivepath> -exportPath <destinationpath> -exportOptionsPlist <plistpath>
参考文章:xcodebuild命令介绍
3、使用 SPM 搭建开发或者直接使用xcode的创建一个命令行程序项目
$ mkdir SwiftCommandLineTool
$ cd SwiftCommandLineTool
$ swift package init --type executable
最后一行的 type executable
参数将告诉 SPM,我们想创建一个 CLI,而不是一个 Framework。
-
封装一个打包上传相关的工具
extension String{ func appPath(_ value:String) -> String { if self.hasSuffix("/") { return self + value } return self + "/" + value } } struct IpaTool { struct Output { var pipe:Pipe var readData:String init(pipe:Pipe) { self.pipe = pipe self.readData = String(data: pipe.fileHandleForReading.readDataToEndOfFile(), encoding: String.Encoding.utf8) ?? "" } } enum Configuration:String { case debug = "Debug" case release = "Release" } var workspace:String{ projectPath.appPath("\(projectName).xcworkspace") } ///scheme var scheme:String ///Debug|Release var configuration:Configuration ///编译产物路径 var xcarchive:String{ exportIpaPath.appPath("\(projectName).xcarchive") } ///配置文件路径 var exportOptionsPlist:String ///导出ipa包的路径 var exportIpaPath:String ///项目路径 let projectPath:String ///项目名称 let projectName:String ///存放打包目录 let packageDirectory:String ///蒲公英_api_key let pgyerKey:String /// /// - Parameters: /// - projectPath: 项目路径 /// - configuration: Debug|Release /// - exportOptionsPlist: 配置文件Plist的路径 /// - pgyerKey: 上传蒲公英的key /// - Throws: 抛出错误 init(projectPath:String, configuration:Configuration, exportOptionsPlist:String, pgyerKey:String) throws { self.projectPath = projectPath self.configuration = configuration self.exportOptionsPlist = exportOptionsPlist self.pgyerKey = pgyerKey let manager = FileManager.default var allFiles = try manager.contentsOfDirectory(atPath: projectPath) projectName = allFiles.first(where: { $0.hasSuffix(".xcodeproj") })?.components(separatedBy: ".").first ?? "" packageDirectory = NSHomeDirectory() .appPath("Desktop/\(projectName)_ipa") allFiles = try manager.contentsOfDirectory(atPath: projectPath.appPath("\(projectName).xcodeproj/xcshareddata/xcschemes") ) scheme = allFiles[0].components(separatedBy: ".").first ?? "" let formatter = DateFormatter() formatter.dateFormat = "yyyy-MM-dd HH:mm:ss" exportIpaPath = packageDirectory.appPath(formatter.string(from: Date())) } }
-
封装执行clean,archive,exportArchive
extension IpaTool{ /// 执行 xcodebuild clean func clean()->Output{ let arguments = ["clean", "-workspace", workspace, "-scheme", scheme, "-configuration", configuration.rawValue, "-quiet", ] return Output(pipe: Process.executable(launchPath: "/usr/bin/xcodebuild", arguments: arguments)) } /// 执行 xcodebuild archive func archive()->Output{ let arguments = ["archive", "-workspace", workspace, "-scheme", scheme, "-configuration", configuration.rawValue, "-archivePath", xcarchive, "-quiet", ] return Output(pipe: Process.executable(launchPath: "/usr/bin/xcodebuild", arguments: arguments)) } /// 执行 xcodebuild exportArchive func exportArchive()->Output{ let arguments = ["-exportArchive", "-archivePath", xcarchive, "-configuration", configuration.rawValue, "-exportPath", exportIpaPath, "-exportOptionsPlist", exportOptionsPlist, "-quiet", ] return Output(pipe: Process.executable(launchPath: "/usr/bin/xcodebuild", arguments: arguments)) } }
-
准备工作完成,我们现在来编写打包代码
do{ let ipaTool = try IpaTool(projectPath: "项目路径", configuration: .debug, exportOptionsPlist: "ExportOptions.plist文件路径", pgyerKey: "xxxxxx") print(ipaTool) print("执行clean") var output = ipaTool.clean() if output.readData.count > 0 { print("执行失败clean error = \(output.readData)") exit(-1) } print("执行archive") output = ipaTool.archive() if !FileManager.default.fileExists(atPath: ipaTool.xcarchive) { print("执行失败archive error = \(output.readData)") exit(-1) } print("执行exportArchive") output = ipaTool.exportArchive() if !FileManager.default.fileExists(atPath: ipaTool.exportIpaPath.appPath("\(ipaTool.scheme).ipa")) { print("执行失败exportArchive error =\(output.readData)") exit(-1) } print("导出ipa成功\(ipaTool.exportIpaPath)") }catch{ print(error) }
注意projectPath使用自己项目路径,exportOptionsPlist建议先手动打一次包来获取
-
运行结果
IpaTool(scheme: "xxx", configuration: SwiftCommandLineTool.IpaTool.Configuration.debug, exportOptionsPlist: "/Users/xxx/Desktop/xxx_ipa/2021-06-03 09:48:40/ExportOptions.plist", exportIpaPath: "/Users/xxx/Desktop/xxx_ipa/2021-06-07 13:59:21", projectPath: "/Users/xxx/xxx_ios", projectName: "xxx", packageDirectory: "/Users/xxx/Desktop/xxx_ipa", pgyerKey: "51895949ad44dcc3934f47c17aa0c0e5") 执行clean 执行archive 执行exportArchive 导出ipa成功/Users/xxx/Desktop/xxx_ipa/2021-06-07 13:59:21 Program ended with exit code: 0
4、把ipa包上传蒲公英
我这里上传文件使用Alamofire
,如果你们熟悉URLSession
用它也行
-
在Package.swift
let package = Package( name: "SwiftCommandLineTool", platforms: [.macOS("10.12")], dependencies: [ .package(name: "Alamofire", url: "https://github.com/Alamofire/Alamofire.git", from: "5.4.3") ], targets: [ .target( name: "SwiftCommandLineTool", dependencies: ["Alamofire"]), .testTarget( name: "SwiftCommandLineToolTests", dependencies: ["SwiftCommandLineTool","Alamofire"]), ] )
-
给IpaTool添加一个上传ipa的函数
//上传蒲公英 func update(){ let ipaPath = exportIpaPath.appPath("\(scheme).ipa") let upload = AF.upload(multipartFormData: { formdata in formdata.append(pgyerKey.data(using: .utf8)!, withName: "_api_key") formdata.append(URL(fileURLWithPath: ipaPath), withName: "file") }, to: URL(string: "https://www.pgyer.com/apiv2/app/upload")!) var isExit = true let queue = DispatchQueue(label: "queue") upload.uploadProgress(queue: queue) { progress in let p = Int((Double(progress.completedUnitCount) / Double(progress.totalUnitCount)) * 100) print("上传进度:\(p)%") } upload.responseData(queue:queue) { dataResponse in switch dataResponse.result { case .success(let data): let result = String(data: data, encoding: .utf8) ?? "" print("上传成功:\(result)") case .failure(let error): print("上传失败: \(error)") } isExit = false } //使用循环换保证命令行程序,不会死掉 while isExit { Thread.sleep(forTimeInterval: 1) } }
print("导出ipa成功\(ipaTool.exportIpaPath)") print("开始上传蒲公英") ipaTool.update()
上传文件的时候使用DispatchQueue.main命令行程序还是会死掉,所以加了一个while循环来保证程序不死。大家有其他方法告送我一下。
5、打代码打包成一个CLl工具(命令行程序)
我这里就不提供教程了,大家可以参考这篇文章:使用 Swift 编写 CLI 工具的入门教程
我不喜欢使用终端所以使用SwiftUI写了一个简单的macOS App
6、一般我们的项目中用了CocoaPods
我们打包的时候要执行一下pod相关的命令
// pod install
func podInstall()->Output{
var environment = [String:String]()
/* 添加环境变量LANG = en_US.UTF-8 否则这个错误 [33mWARNING: CocoaPods requires your terminal to be using UTF-8 encoding. Consider adding the following to ~/.profile: export LANG=en_US.UTF-8 */
environment["LANG"] = "en_US.UTF-8"
/* 添加环境变量PATH = /usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Library/Apple/usr/bin:/Users/xxx/.rvm/bin 终端运行 echo $PATH 获取 否则这个错误 [1mTraceback[m (most recent call last): 9: from /usr/local/bin/pod:23:in `<main>' 8: from /usr/local/bin/pod:23:in `load' 7: from /Library/Ruby/Gems/2.6.0/gems/cocoapods-1.10.1/bin/pod:55:in `<top (required)>' 6: from /Library/Ruby/Gems/2.6.0/gems/cocoapods-1.10.1/lib/cocoapods/command.rb:49:in `run' 5: from /Library/Ruby/Gems/2.6.0/gems/cocoapods-1.10.1/lib/cocoapods/command.rb:140:in `verify_minimum_git_version!' 4: from /Library/Ruby/Gems/2.6.0/gems/cocoapods-1.10.1/lib/cocoapods/command.rb:126:in `git_version' 3: from /Library/Ruby/Gems/2.6.0/gems/cocoapods-1.10.1/lib/cocoapods/executable.rb:143:in `capture_command' 2: from /Library/Ruby/Gems/2.6.0/gems/cocoapods-1.10.1/lib/cocoapods/executable.rb:117:in `which!' 1: from /Library/Ruby/Gems/2.6.0/gems/cocoapods-1.10.1/lib/cocoapods/executable.rb:117:in `tap' /Library/Ruby/Gems/2.6.0/gems/cocoapods-1.10.1/lib/cocoapods/executable.rb:118:in `block in which!': [1m[31m[!] Unable to locate the executable `git`[0m ([1;4mPod::Informative[m[1m)[m */
environment["PATH"] = "/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Library/Apple/usr/bin:/Users/xxx/.rvm/bin"
/* 添加环境变量CP_HOME_DIR = NSHomeDirectory().appending("/.cocoapods") 我的cocoapods安装在home目录所以使用这个, 你们可以在访达->前往文件夹...-> ~/.cocoapods,来获取路径 否则这个错误 Analyzing dependencies Cloning spec repo `cocoapods` from `https://github.com/CocoaPods/Specs.git` [!] Unable to add a source with url `https://github.com/CocoaPods/Specs.git` named `cocoapods`. You can try adding it manually in `/var/root/.cocoapods/repos` or via `pod repo add`. */
environment["CP_HOME_DIR"] = NSHomeDirectory().appending("/.cocoapods")
let pipe = Process.executable(launchPath: "/usr/local/bin/pod",
arguments: ["install"],
currentDirectoryPath: projectPath,
environment: environment)
return Output(pipe: pipe)
}
最后附上 项目地址
今天的文章用Swift写一个自动打包ipa,并上传蒲公英分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/18881.html