下载页面
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>游戏Demo</title>
</head>
<body>
<h1>1. iOS安装 <a href="https://xxxx/iOS/xxx_public.crt">证书</a><br/>(如果已经有https站点,这步跳过,如果之前尚未安装过此证书,请先安装证书)</h1>
<br/>
<h1>2. iOS安装 <a href="itms-services://?action=download-manifest&url=https://xxxx/iOS/manifest.plist">游戏包</a></h1>
<br/>
<h1>3. Android安装 <a href="https://xxxx/ipa/thirtysix.apk">apk安装包</a></h1>
</body>
</html>
重点 manifest.plist 格式 和制作
<?xml version="1.0" encoding="UTF-8"?>
<plist version="1.0"><dict>
<key>items</key>
<array>
<dict>
<key>assets</key>
<array>
<dict>
<key>kind</key>
<string>software-package</string>
<key>url</key>
<string><![CDATA[https://xxxx/iOS/ProductName.ipa]]></string>
</dict>
<dict>
<key>kind</key>
<string>display-image</string>
<key>needs-shine</key>
<integer>0</integer>
<key>url</key>
<string><![CDATA[https://xxxx/iOS/Icon-Store-1024.png]]></string>
</dict>
<dict>
<key>kind</key>
<string>full-size-image</string>
<key>needs-shine</key>
<true/>
<key>url</key>
<string><![CDATA[https://xxxx/iOS/Icon-Store-1024.png]]></string>
</dict>
</array>
<key>metadata</key>
<dict>
<key>bundle-identifier</key>
<string>com.xxx.xxx</string>
<key>bundle-version</key>
<string><![CDATA[1.204.5]]></string>
<key>kind</key>
<string>software</string>
<key>title</key>
<string><![CDATA[app名字]]></string>
</dict>
</dict>
</array>
</dict>
</plist>
把对应的 ipa 上传到文件服务器(注意需要时https支持下载)即可.
对了,下面给出 命令行 编译 ipa 需要的 plist 配置文件
ad-hoc.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>method</key>
<string>ad-hoc</string>
<key>teamID</key>
<string>xxxxxx</string>
<key>uploadBitcode</key>
<true/>
<key>uploadSymbols</key>
<true/>
<key>manifest</key>
<dict>
<key>appURL</key>
<string>https://xxxx/iOS/ProductName.ipa</string>
<key>displayImageURL</key>
<string>https://xxxx/iOS/Icon-Store-1024.png</string>
<key>fullSizeImageURL</key>
<string>https://xxxx/iOS/Icon-Store-1024.png</string>
<key>assetPackManifestURL</key>
<string>https://xxxx/iOS/manifest.plist</string>
</dict>
</dict>
</plist>
app-store.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>method</key>
<string>app-store</string>
<key>teamID</key>
<string>XXXX</string>
<key>uploadBitcode</key>
<true/>
<key>uploadSymbols</key>
<true/>
</dict>
</plist>
编译命令
'/usr/bin/xcodebuild -exportArchive -archivePath %s -exportOptionsPlist %s -exportPath %s -allowProvisioningUpdates -allowProvisioningDeviceRegistration' % (archivePath,exportOptionsPlist,exportPath)
完整的编译脚本 autobuild.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from optparse import OptionParser
import subprocess
import requests
import datetime
import os
import myemail
import re
curPath=os.path.split(os.path.realpath(__file__))[0]
messagetxt="本次上传包含:"
def cleanBuildDir(workspaceName,configuration,scheme):
buildCmd = '/usr/bin/xcodebuild -quiet clean -workspace %s -configuration %s -scheme %s' % (workspaceName,configuration,scheme)
if workspaceName is None :
buildCmd = '/usr/bin/xcodebuild -quiet clean -configuration %s -scheme %s' % (configuration,scheme)
print(buildCmd)
process = subprocess.Popen(buildCmd, shell = True)
process.wait()
def uploadIpaToFir(ipaPath ,firkey,changelog):
buildCmd = '/usr/local/bin/fir login %s && /usr/local/bin/fir publish %s -c %s' % (firkey,ipaPath,changelog)
print(buildCmd)
process = subprocess.Popen(buildCmd, shell = True)
process.wait()
def buildWorkspace(workspaceName, scheme, configuration, archivePath):
buildCmd = '/usr/bin/xcodebuild -quiet archive -workspace %s -scheme %s -configuration %s -archivePath %s' % (workspaceName,scheme,configuration,archivePath)
if workspaceName is None :
buildCmd = '/usr/bin/xcodebuild -quiet archive -scheme %s -configuration %s -archivePath %s' % (scheme,configuration,archivePath)
print(buildCmd)
process = subprocess.Popen(buildCmd, shell = True)
process.wait()
def exportipa(archivePath, exportPath, exportOptionsPlist):
buildCmd = '/usr/bin/xcodebuild -exportArchive -archivePath %s -exportOptionsPlist %s -exportPath %s -allowProvisioningUpdates -allowProvisioningDeviceRegistration' % (archivePath,exportOptionsPlist,exportPath)
print(buildCmd)
process = subprocess.Popen(buildCmd, shell=True)
'''out_value=process.communicate()
regex = re.compile("Errors=\{")
if regex.match("".join(out_value)) :
logfile = writelog(out_value)
print("build error please log:%s"%(logfile))
exit()'''
process.wait()
def uploadIpaToAppStore(ipaPath_appstore, appleID, applepassword):
altoolPath="xcrun altool"
#buildCmd = '%s --validate-app -f %s -u %s -p %s -t ios --output-format xml' % (altoolPath, ipaPath_appstore, appleID, applepassword)
#process = subprocess.Popen(buildCmd, shell = True)
#process.wait()
buildCmd = '%s --upload-app -f %s --apiIssuer %s --apiKey %s -t ios --output-format xml' % (altoolPath, ipaPath_appstore, appleID, applepassword)
print(buildCmd)
process = subprocess.Popen(buildCmd, shell = True)
process.wait()
def writelog(logstr):
timenow = datetime.datetime.now()
timestr = datetime.datetime.strftime(timenow,'%H-%M-%S')
datestr = datetime.datetime.strftime(timenow,'%Y-%m-%d')
dirstr = '%s_%s' % (datestr,timestr)
logfile = '%s/log/%s.txt'%(curPath,dirstr)
file_object=open(logfile, 'w+')
file_object.write(logstr)
file_object.close()
return logfile
def xcbuild(options):
global messagetxt
if options.scheme is not None:
if options.needCompile:
cleanBuildDir(options.workspace,options.configuration,options.scheme)
buildWorkspace(options.workspace, options.scheme, options.configuration, options.archivePath)
if options.backUpArchive is not None:
buildCmd = 'cp -a %s %s' % (options.archivePath,options.backUpArchive)
print(buildCmd)
process = subprocess.Popen(buildCmd, shell = True)
process.wait()
if options.export == 1:
exportipa(options.archivePath,options.exportPath_adhoc,options.exportOptionsPlist_adhoc)
messagetxt = messagetxt + "导出test的ipa"
elif options.export == 2:
exportipa(options.archivePath,options.exportPath_appstore,options.exportOptionsPlist_appstore)
messagetxt = messagetxt + "导出AppStore的ipa"
elif options.export == 3:
exportipa(options.archivePath,options.exportPath_adhoc,options.exportOptionsPlist_adhoc)
exportipa(options.archivePath,options.exportPath_appstore,options.exportOptionsPlist_appstore)
messagetxt = messagetxt + "导出test、AppStore的ipa"
if options.upload == 1:
print ("build and upload test")
uploadIpaToFir(options.ipaPath_adhoc, options.firkey,options.changelog)
messagetxt = messagetxt + "上传测试版本"
#myemail.send_mail(options.scheme, "%s:%s"%(messagetxt,options.note), ",http://hp3h1.hidog.net/shareapp/test.php")
elif options.upload == 2:
print ("build and upload appstore")
uploadIpaToAppStore(options.ipaPath_appstore,options.appleid, options.applepassword)
messagetxt = messagetxt + "上传AppStore发布版本"
#myemail.send_mail(options.scheme, "%s:%s"%(messagetxt,options.note), "")
elif options.upload == 3:
print ("build and upload appstore & test")
uploadIpaToFir(options.ipaPath_adhoc, options.firkey,options.changelog)
messagetxt = messagetxt + "上传测试版本"
uploadIpaToAppStore(options.ipaPath_appstore,options.appleid, options.applepassword)
messagetxt = messagetxt + "、AppStore发布版本,备注:%s,http://hp3h1.hidog.net/shareapp/test.php" % options.note
#myemail.send_mail(options.scheme, messagetxt, "")
def main():
parser = OptionParser()
parser.add_option("-s", "--scheme", type="string", help="Build the scheme specified by schemename. Required if building a workspace.", metavar="schemename")
parser.add_option("-e", "--export", type="int", help="specify export filename", metavar="output_filename")
parser.add_option("-u", "--upload", type="int", help="upload filename", metavar="upload_filename")
parser.add_option("-n", "--note", type="string", help="specify publish note", metavar="publish note")
parser.add_option("-a", "--archivePath", type="string", help="archivePath", metavar="archivePath")
parser.add_option("-t", "--team", type="int", help="whichTeam", metavar="whichTeam")
parser.add_option("-w", "--workspace", type="string", help="whichWorkspace", metavar="whichWorkspace")
parser.add_option("-c", "--changelog", type="string", help="changelog", metavar="changelog")
parser.add_option("-d", "--debug", type="string", help="debug", metavar="debug mode")
parser.add_option("-b", "--backUpArchive", type="string", help="backUpArchive", metavar="backUpArchive")
parser.add_option("-r", "--upUser", type="string", help="upUser", metavar="upUser")
parser.add_option("-p", "--upPass", type="string", help="upPass", metavar="upPass")
parser.add_option("-o", "--ipaPath", type="string", help="ipaPath", metavar="ipaPath")
(options, args) = parser.parse_args()
timenow = datetime.datetime.now()
datestr = datetime.datetime.strftime(timenow,'%Y-%m-%d')
timestr = datetime.datetime.strftime(timenow,'%H-%M-%S')
#datestr = '2019-10-15'
#timestr = '11-06-59'
dirstr = '%s/%s_%s' % (datestr,options.scheme,timestr)
#options.home = os.path.expanduser('~')
options.home = curPath
options.ipaPath_adhoc = '%s/../ipa_test/%s/%s.ipa' % (options.home,dirstr,options.scheme)
options.ipaPath_appstore = '%s/../ipa_release/%s/%s.ipa' % (options.home,dirstr,options.scheme)
options.exportPath_adhoc = '%s/../ipa_test/%s' % (options.home,dirstr)
options.exportPath_appstore = '%s/../ipa_release/%s' % (options.home,dirstr)
if options.ipaPath is not None:
options.ipaPath_adhoc = '%s/%s/%s.ipa' % (options.ipaPath,dirstr,options.scheme)
options.ipaPath_appstore = '%s/%s/%s.ipa' % (options.ipaPath,dirstr,options.scheme)
print("ipaPath "+options.ipaPath_appstore)
if options.team is None:
options.exportOptionsPlist_adhoc = '%s/packcfghz/ad-hoc.plist' % (options.home)
options.exportOptionsPlist_appstore = '%s/packcfghz/app-store.plist' % (options.home)
options.appleid=options.upUser
options.applepassword=options.upPass
if options.archivePath is None :
options.archivePath = "%s/Library/Developer/Xcode/Archives/%s/%s.xcarchive" % (os.path.expanduser('~'),datestr,dirstr)
options.needCompile=True
else:
options.needCompile=False
if options.debug is None or options.debug == "false" :
options.configuration = "Release"
else:
options.configuration = "Debug"
if options.changelog is None:
options.changelog=""
options.firkey = "xxxxxxxx"
if options.export is None:
options.export = 0
if options.upload is None:
options.upload = 0
if options.note is None:
options.note = ""
print ("options: %s, args: %s" % (options, args))
if options.scheme is not None:
xcbuild(options)
if __name__ == '__main__':
main()
使用示例,可以结合 Jenkins PipeLine 来使用
/usr/local/bin/python3 ~/Desktop/desk/BuildScript/autobuild.py \
-s Proj-mobile \
-d $debug \
-e $IOS_UPLOAD_TYPE \
-u $IOS_UPLOAD_TYPE \
-w Proj.xcworkspace \
-c "吧啦吧啦" \
-b ${updateServerPath}/apkipa/Proj-mobile.${APP_VERSION_CODE}.${APP_VERSION_NAME}-$buildTime.xcarchive \
-o $ipaPath
#下面是 bugly 上传符号 脚本
#上传符号文件 必须编译完立即上传,每次编译产生的 map 都会变化
$JAVAHOME1_8/bin/java -jar $ProjRoot/buglyqq-upload-symbol.jar -appid 5fe2f21f91 \
-appkey xxxx \
-bundleid xxxxx \
-version ${APP_VERSION_NAME}.${APP_VERSION_CODE} \
-platform IOS \
-inputSymbol ${updateServerPath}/apkipa/Proj-mobile.${APP_VERSION_CODE}.${APP_VERSION_NAME}-$buildTime.xcarchive/dSYMs/