导读

背景

  • 因为项目需要,未来除了主线产品外,还会有30几个马甲,所以为了减轻开发工作量,将所有打包项目都转移的Jenkins,单独部署一台专门打包,还有各种自动化的机器上.

  • 目前大概主要用到打包,提审,自动化蒲公英,企业签名….等等,主要是ios为主,还有部分Android的脚本配置,基本上都是一件自动化的

需要技能

  • 1.ios开发基础.

  • 2.shell,Python入门 (由于本人比较熟悉这2个所有就用来,当然你熟悉nodsjs也是可以的)

Jenkins必备插件

环境安装

  • brew安装及换源,brew安装方法请自行百度,并保持最新版

cd "$(brew --repo)"

git remote set-url origin https://mirrors.ustc.edu.cn/brew.git

# 替换homebrew-core.git:

cd "$(brew --repo)/Library/Taps/homebrew/homebrew-core"

git remote set-url origin https://mirrors.ustc.edu.cn/homebrew-core.git
  • jenkins采用brew方法安装,另外macOS自待python2的版本,这里就不配置了

brew install jenkins
  • jq,一个shell解析json的工具,我这里是解析蒲公英上传后的json使用

brew install jq 

插件安装

ios
  • 以下是必须的插件,还会自动配置一堆依赖

Email Extension Plugin //邮件配置

Role-based Authorization Strategy //权限管理

Git //代码拉取
android
  • 环境不是我部,所有不清楚,我这里只是提供了一个资源替换脚本而已

插件配置

邮件配置
  • jenkins邮件插件Email Extension Plugin的安装与配置 这篇就可以了说得很全

  • 另外补充一下,job邮件配置有几个注意点看下图.第一个可以指定其他特殊的邮箱,如qa组;第二个,可以设置附件,如蒲公英二维码,企签后的包…目录都是在workspace/项目名称的为根目录;具体见下

image

job参数配置
  • 建议能用可选参数尽量用可选

  • 文件参数上传后,必须指定生成路径及文件名.注意,如果需要zip上传一些资源,然后跑脚本更改配置,记得跑好脚本后删除原来资源,这样可以减小项目冲突,或某情况下,打包判断

image

git配置
  • 这个不说了,也没啥好说的.注意使用ssh链接的话要在本机先配公匙

ios脚本构建项目

  • 脚本打包主要涉及测试包,公测包,提审包…也就这三类…

  • 我们这里是使用脚本构建的,核心也就是xcodebuild.也没使用Jenkins,也没使用fastlane作为构建工具.都是用xcode自带的工具,签名也是采用自动化签名的.

  • 另外, debug/release构建没啥特别,提审需要引用xcode的一个工具链接到bin目录下.

debug/release 脚本

  • 脚本放在 *.xcworkspace同级目录 7个参数按需填写

  • 会在桌面生成对Ipa文件夹,里面会包含所有的打包


#./build-export.sh "debug" "day" "描述" "邮箱" "password" "ukey" "apikey"

#参数1 debug/release/publish

#参数2 day:2019-4-8/I0f05f3e33dac3cbc64da2a737145a96b12850137 

#参数3 ""/"蒲公英自定义描述"

#参数4 苹果上传账号 appleId

#参数5 苹果上传密码 apple password

#参数6 蒲公英ukey 默认 f46...83b

#参数7 蒲公英apikey 默认 b98...58f



#工程名 *.xcworkspace

project_name=Aaaa



#scheme名

scheme_name= Aaaa



#打包模式 debug/release/publish

pack_mode=${1:-"debug"}



# 1.type:day:2018-9-10 按日输出 2.change-id 最后一次上传日志

LastBuildCommitId=${2:-"day"}



#蒲公英-上传描述

PgyerUpdateDescription=${3:-""}



#提审需要信息

AppleId=${4:-}

ApplePsw=${5:-}



##蒲公英-触电内侧账号

PgyerUKey=${6:-"xxx"}

PgyerApiKey=${7:-"xxx"}



#plist文件所在路径

exportOptionsPlistPath=""



#版本类型中文名

development_name=""



#当前目录路径

sourcePath=$(pwd)



#build目录

buildPath="${sourcePath}/build"



#brew install jq 

#ln -s /Applications/Xcode.app/Contents/Applications/Application\ Loader.app/Contents/Frameworks/ITunesSoftwareService.framework/Support/altool /usr/local/bin/altool

#mkdir -p  /usr/local/itms/bin/

#ln -s /Applications/Xcode.app/Contents/Applications/Application\ Loader.app/Contents/itms/bin/iTMSTransporter /usr/local/itms/bin/iTMSTransporter



## 版本选择

development_mode=""

if [ "$pack_mode" = "release" ]; then

  development_name="公测版"

  development_mode="Release"

  exportOptionsPlistPath=./export-achot.plist

elif [ "$pack_mode" = "publish" ]; then

  development_name="提审版"

  exportOptionsPlistPath=./export-appstore.plist

  development_mode="Release"

else

  development_name="测试版"

  exportOptionsPlistPath=./export-debug.plist

  development_mode="Debug"

fi





#分支名称

branch_name=$(git symbolic-ref --short -q HEAD)



#Info.plist路径

plistPath=${project_name}/Info.plist





#直播助手版本号必须为全数字不然有坑

buildTime=$(date +%Y%m%d%H%M)



#appVersion

appVersion=$(/usr/libexec/Plistbuddy -c "Print:CFBundleShortVersionString" "${plistPath}")

#displayName

appDisplayName=$(/usr/libexec/Plistbuddy -c "Print:CFBundleDisplayName" "${plistPath}")



#导出.ipa文件所在文件夹路径

exportFilePath=~/Desktop/Ipa/${appDisplayName}-${appVersion}-${pack_mode}-${buildTime}

	

#导出.ipa路径想

exportIpaPath=${exportFilePath}/${project_name}.ipa







#计时

SECONDS=0



#ask-commit() {

#  #commit没有传值

#  if [ -z "$LastBuildCommit"]; then

#  	read -p "commit-id:" input

#    LAST_BUILD_COMMIT=$input

#  fi

#}





rm -rf ${buildPath} || true



get_log(){

  echo "*** 获取日志开始 ***"

  if [ "$LastBuildCommitId" = "" ]; then



    echo "编译版本: $development_name"

    echo "程序分支: ${branch_name}"

    echo "日志:"

    echo "$PgyerUpdateDescription"



  	return

  fi



  

  #获取log

  gitlog=''

  result=$(echo $LastBuildCommitId | grep "day")

  if [ "$result" != "" ]; then

    currentDate="$(date +%Y-%m-%d) 0:0:0"

    result=$(echo $LastBuildCommitId | grep "-")

    if [ "$result" != "" ]; then

      currentDate="$LastBuildCommitId 0:0:0"

    fi

    git_log_format="%s"

    gitlog=$(git log --pretty=format:"$git_log_format" --date=short --since="$currentDate")

  else

    git_log_format="%s"

    gitlog=$(git log --pretty="$git_log_format" $LastBuildCommitId..HEAD)

  fi



  echo "编译版本: $development_name"

 

  # 过滤出以 [ 开头的行

  gitlog=$(echo "$gitlog" | grep "^\[" )

  # 如果没有符合条件的更新日志

  if [ -z "${gitlog}" ]; then

    return

  fi



  # gitlog=$(echo "$gitlog" | grep "^\[" )

  # 翻转 LOG,因为 Git log 最开始是倒序的

  gitlog=$(echo "$gitlog" | sed '1!G;h;$!d')

  # 在末尾加一 br 标签,邮件中会换行显示

  #gitlog=$(echo "$flip_changelog" | sed 's/$/&<br>/g')

  # 添加行号

  changelog_with_line_number=$(echo "$gitlog" | awk '{printf NR"."" "}1')

  

  PgyerUpdateDescription=$changelog_with_line_number

  

  echo "程序分支: ${branch_name}"

  echo "日志:"

  echo "$PgyerUpdateDescription"



}



package_ipa(){

    /usr/libexec/Plistbuddy -c "Set CFBundleVersion $buildTime" "${plistPath}"

   

    echo "*** 当前信息:app:${appDisplayName} verison:${appVersion} bulid:${buildTime}  ***"

    

    echo "*** 正在 清理工程 ***"

    xcodebuild \

    clean -configuration ${development_mode} -quiet  || exit 

    echo "*** 清理完成 ***"

    

    

    echo "*** 正在 编译工程 For "${development_mode}

    xcodebuild \

    archive -workspace ${project_name}.xcworkspace \

    -scheme ${scheme_name} \

    -configuration ${development_mode} \

    -archivePath build/${project_name}.xcarchive -quiet  || exit

    echo "*** 编译完成 ***"

    

    



}

export_ipa(){

    echo "*** 正在 打包 ***"

    xcodebuild -exportArchive -archivePath build/${project_name}.xcarchive \

    -configuration ${development_mode} \

    -exportPath ${exportFilePath} \

    -exportOptionsPlist ${exportOptionsPlistPath} \

    -allowProvisioningUpdates \

    -quiet || exit

    echo "*** 复制打包文件 ***"

    cp -rf build/* ${exportFilePath}

}



del_build(){

  # 删除build包

    

    if [[ -d buildPath ]]; then

        rm -rf buildPath -r

    fi

}

upload_pgyer_ipa(){



    #json格式化工具安装

    #sudo chown -R username /usr/local

    #brew install jq 

    # PgyerUpdateDescriptionAll=${development_name}${branch_name}${PgyerUpdateDescription}

    PgyerUpdateDescriptionAll=$(echo "编译版本:${development_name} verison:${appVersion} bulid:${buildTime}" &&echo "程序分支:${branch_name}"  &&echo "日志:"  &&echo "${PgyerUpdateDescription}")

    # echo "$PgyerUpdateDescriptionAll"

    if [ -e $exportFilePath/$scheme_name.ipa ]; then

        echo "*** .ipa文件已导出 ***"

        #此处上传分发应用-开始

        echo "*** 开始上传.ipa文件 *** ${exportIpaPath}"

        if which jq 2>/dev/null; then

          jqResult=$(curl -F "file=@${exportIpaPath}" -F "updateDescription=${PgyerUpdateDescriptionAll}" -F "uKey=${PgyerUKey}"  -F "_api_key=${PgyerApiKey}" https://www.pgyer.com/apiv1/app/upload | jq ".")

          cd ${sourcePath}

          echo "${jqResult}" > pgyer_tmp_info.txt

          cd ${exportFilePath}

          echo "${jqResult}"

          echo "${PgyerUpdateDescriptionAll}" > readme.txt

          echo "${jqResult}" >> readme.txt

        else

          result=$(curl -F "file=@${exportIpaPath}" -F "updateDescription=${PgyerUpdateDescriptionAll}" -F "uKey=${PgyerUKey}"  -F "_api_key=${PgyerApiKey}" https://www.pgyer.com/apiv1/app/upload)

          cd ${exportFilePath}

          echo "${result}"

          echo "${PgyerUpdateDescriptionAll}" > readme.txt

          echo "${result}" >> readme.txt

          

        fi

        

        echo "*** .ipa文件上传成功 ***"

        #此处上传分发应用-结束

    else

        echo "*** 创建.ipa文件失败 ***"

    fi

    

}



upload_appstore(){

  echo "验证IPA中..."

  altool -v -f ${exportIpaPath} -u ${AppleId} -p ${ApplePsw} -t ios --output-format xml

  echo "开始上传苹果商店..."

  altool --upload-app -f ${exportIpaPath} -t ios -u ${AppleId} -p ${ApplePsw}

}



if [ "$pack_mode" = "release" ]; then

  get_log

  package_ipa

  export_ipa

  upload_pgyer_ipa

  del_build

elif [ "$pack_mode" = "publish" ]; then

  get_log

  package_ipa



  #生成公测包

  cd ${sourcePath}

  exportOptionsPlistPath=./export-achot.plist

  development_mode="Release"

  pack_mode="release"

  exportFilePath=~/Desktop/Ipa/${appDisplayName}-${appVersion}-${pack_mode}-${buildTime}

  exportIpaPath=${exportFilePath}/${project_name}.ipa

  export_ipa

  upload_pgyer_ipa



  #生成提审包

  cd ${sourcePath}

  exportOptionsPlistPath=./export-appstore.plist

  development_mode="Release"

  pack_mode="publish"

  exportFilePath=~/Desktop/Ipa/${appDisplayName}-${appVersion}-${pack_mode}-${buildTime}

  exportIpaPath=${exportFilePath}/${project_name}.ipa

  export_ipa

  upload_appstore

  del_build

else

  get_log

  package_ipa

  export_ipa

  upload_pgyer_ipa

  del_build

fi



echo "*** 打包完成 *** Total time: ${SECONDS}s"

app store

  • 发苹果商店也是用上面的脚本但是需要配多一个环节.从xcode.app里链接一个上传工具,其他也没特别了

ln -s /Applications/Xcode.app/Contents/Applications/Application\ Loader.app/Contents/Frameworks/ITunesSoftwareService.framework/Support/altool /usr/local/bin/altool



mkdir -p  /usr/local/itms/bin/



ln -s /Applications/Xcode.app/Contents/Applications/Application\ Loader.app/Contents/itms/bin/iTMSTransporter /usr/local/itms/bin/iTMSTransporter

发邮件

  • 这里是先用脚本打包好ipa,然后将蒲公英上传成功后返回的json写入一个本地文件,然后在用Python,转成功一个html的网页,当附件发送到邮箱

  • 这个Python脚本,如下


#!/usr/bin/python2.6

# -*- coding: UTF-8 -*-





import os, sys

import shutil

import time

import codecs

import argparse

import shutil

import zipfile

import json



reload(sys)





def parse_args():

    global script_path, proj_ios_path

    parser = argparse.ArgumentParser(description='融媒打包项目.\n')



    parser.add_argument('--json_file',

                        default="pgyer_tmp_info.txt",

                        dest='json_file',

                        help='蒲公英生成的json文件', type=str, required=False)



    args = parser.parse_args()

    return args





def main():

    startTime = time.time()



    app_args = parse_args()

    json_file_name = app_args.json_file

    workspacePath = os.getcwd() + os.sep

    json_file_path = workspacePath + json_file_name



    # 写入HTML界面中



    appQRCodeURL = ""

    appShortcutUrl = ""

    appUpdateDescription = ""

    appName = ""

    appVersion = ""

    appVersionNo = ""

    with open(json_file_path) as f:

        js = json.load(f, encoding="utf-8", strict=False)

        appQRCodeURL = js["data"]["appQRCodeURL"].encode("utf8")

        appShortcutUrl = "https://www.pgyer.com/" + js["data"]["appShortcutUrl"].encode("utf8")

        appUpdateDescription = js["data"]["appUpdateDescription"].encode("utf8")

        appName = js["data"]["appName"].encode("utf8")

        appVersion = js["data"]["appVersion"].encode("utf8")

        appVersionNo = js["data"]["appVersionNo"].encode("utf8")



    message = """

        <!DOCTYPE html>

        <html>

        <head>

        <meta charset="UTF-8">

        <title>蒲公英邮件</title>

        </head>

        <body leftmargin="8" marginwidth="0" topmargin="8" marginheight="4" offset="0" >

            <table width="95%%" cellpadding="0" cellspacing="0" style="font-size: 11pt; font-family: Tahoma, Arial, Helvetica, sans-serif;margin:auto" >

                <tr>

                    <td> <br/>

                    <b><font color="#0B610B">构建信息</font></b>

                    <hr size="2" width="100%%" align="center" /></td>

                </tr>

                <tr>

                    <td>

                        <ul>

                            <li>项目名称 :%s</li>

                            <li>版 本 号 :%s</li>

                            <li>构 建 号 :%s</li>

                            <li>链接地址 :<a href="%s">%s</a></li>

                            <li><img src="%s" alt=""/></li>

                        </ul>

                    </td>

                </tr>

                <tr>

                    <td><b><font color="#0B610B">构建日志:</font></b>

                    <hr size="2" width="100%%" align="center" /></td>

                </tr>

                <tr>

                    <td>

                        <textarea cols="80" rows="30" readonly="readonly" style="font-family: Courier New">%s</textarea>

                    </td>

                </tr>

            </table>

        </body>

        </html>

    """ % (appName, appVersion, appVersionNo, appShortcutUrl, appShortcutUrl, appQRCodeURL, appUpdateDescription)



    # 移除旧文件

    if (os.path.exists(json_file_path)):

        print("remove: " + json_file_path)

        os.remove(json_file_path)



    html_file_name = "pgyer_tmp_info.html"

    html_path = workspacePath + html_file_name

    if (os.path.exists(html_path)):

        print("remove: " + html_path)

        os.remove(html_path)



    f = open(html_file_name, 'w')

    f.write(message)

    f.close()

    endTime = time.time()

    print("总耗时: %0.2f" % (endTime - startTime))





if __name__ == "__main__":

    main()

ios企签构建

  • 配置这个请先看企业证书重签名发布APP(支持APNS),搞清概念在做下一步.

  • 红框就是必须用到的文件,会在桌面生成一个Ipa_enterprise文件夹,里面会有企签后的APP及,下载plist,这个可能需要手动调整一下plist,文件下的ipa

  • 发布证书及其p12

  • 签名脚本

  • 第三方签名工具ota-tools


#!/usr/bin/python2.6

# -*- coding: UTF-8 -*-





import os, sys

import time

import codecs

import argparse

import shutil

import zipfile

import socket



reload(sys)



workspace_path = os.getcwd() + os.sep

workspace_desktop_path = os.path.expanduser("~/Desktop/Ipa_enterprise/")





class Error(Exception):

    pass





class Task(object):

    def __init__(self):

        self.config = Config()



    # ios 导出打包配置文件



    def export_info_list(self, team_id, bundle_id):

        pass



    # 上传蒲公英

    def run_pgyer_upload(self, ipa_path, pgyer_ukey, pgyer_apikey, pgyer_desc="描述"):



        print("开始上传路径 :" + ipa_path)

        cmd_workspace = "cd " + workspace_path

        cmd_curl_text = "curl -F \"file=@%s\" -F \"updateDescription=%s\" -F \"uKey=%s\"  -F \"_api_key=%s\" \"https://www.pgyer.com/apiv1/app/upload\" | jq \".\"" \

                        % (ipa_path, pgyer_desc, pgyer_ukey, pgyer_apikey)

        cmd_curl = "echo $(%s) > pgyer_tmp_info.txt " % (cmd_curl_text)

        cmd = cmd_workspace + " && " + cmd_curl

        p = os.popen(cmd)

        p.close()



    # 企签证书

    def run_sign(self, bin_floder_path, ipa_path, mobileprovision_path, certificate_name, ipa_http_url):



        if len(ipa_http_url) == 0:

            ipa_http_url = "http://" + socket.gethostbyname(socket.getfqdn(socket.gethostname())) + ":8000/"



        cmd_workspace = "cd " + workspace_path

        cmd_sign = bin_floder_path + "ipa_sign" + " " + ipa_path + " " + mobileprovision_path + " " + "\"" + certificate_name + "\""

        cmd = cmd_workspace + " && " + cmd_sign



        # APP重签处理

        p = os.popen(cmd)

        text = p.read()

        p.close()

        print(text)



        lines = text.split("\n")

        line1_words = lines[1].replace('\'', '').split(" ")

        line2_words = lines[2].replace('\'', '').split(" ")

        line5_words = lines[5].replace('\'', '').split(" ")

        BundleDisplayName = line1_words[3]

        ShortVersionString = line1_words[6]

        BundleId = line2_words[4]

        BundleVersion = line2_words[7]



        export_file_name = (BundleId + "_" + ShortVersionString + "_" + BundleVersion).replace('.', '_') + ".ipa"

        export_info_name = export_file_name.replace('.ipa', '.plist')

        # export_folder_name = BundleDisplayName + "-" + ShortVersionString + "-" + "resigned" + "-" + BundleVersion

        export_folder_name = export_file_name.replace('.ipa', '')



        # export_tmp_ipa = line5_words[4]

        export_resigned_path = line5_words[4]  # bin_floder_path + export_file_name

        export_info_path = workspace_path + export_info_name

        # shutil.move(export_tmp_ipa, export_resigned_path)



        export_desktop_folder_path = workspace_desktop_path + export_folder_name + os.sep

        export_desktop_ipa_path = export_desktop_folder_path + export_file_name

        export_desktop_info_path = export_desktop_folder_path + export_info_name



        if os.path.exists(export_desktop_folder_path):

            shutil.rmtree(export_desktop_folder_path)

            os.mkdir(export_desktop_folder_path)

        else:

            os.mkdir(export_desktop_folder_path)



        # 复制ipa文件

        shutil.copyfile(export_resigned_path, export_desktop_ipa_path)



        # 生成下载info.plist

        ipa_http_serve_floder_url = ipa_http_url + export_folder_name + "/"

        cmd_ota = bin_floder_path + "ipa_ota" + " " + export_desktop_ipa_path + " " + ipa_http_serve_floder_url

        cmd = cmd_workspace + " && " + cmd_ota



        # APP重签处理

        p = os.popen(cmd)

        text = p.read()

        p.close()

        print(text)



        # 复制info.plist文件

        shutil.copyfile(export_info_path, export_desktop_info_path)

        os.rename(export_info_path, export_resigned_path.replace(".ipa", ".plist"))



        # http_cmd = "itms-services://?action=download-manifest&url=http://aaa.list.ppp/com_touchtv_midou_1.0.1_201904281818.plist"

        # http_cmd = "itms-services://?action=download-manifest&url=%s%s" % (ipa_http_serve_floder_url, export_info_name)



        pgyer_desc = BundleDisplayName + "(企签版)" + "\n" + "版本:" + ShortVersionString + "\n" + "构建号:" + BundleVersion

        print("==========================================\n")

        print("ipa 生成路径 :" + export_desktop_ipa_path)

        print(".plist 生成路径 :" + export_desktop_info_path)

        print("安装连接 :" + ipa_http_serve_floder_url)

        return export_resigned_path, export_desktop_ipa_path, export_info_path, export_desktop_info_path, pgyer_desc



    # 生成邮件附件

    def create_mail_file(self, app_args):

        pass





def parse_args():

    parser = argparse.ArgumentParser(description='企签项目.\n')



    parser.add_argument('--soucre_ipa_name',

                        default="t_source.ipa",

                        dest='soucre_ipa_name',

                        help='需要签名的ipa', type=str, required=False)



    parser.add_argument('--pgyer_ukey',

                        default="f46...83b",

                        dest='pgyer_ukey',

                        help='pgyer_ukey', type=str, required=False)



    parser.add_argument('--pgyer_apikey',

                        default="b98...58f",

                        dest='pgyer_apikey',

                        help='pgyer_apikey', type=str, required=False)



    parser.add_argument('--mobileprovision_path',

                        default="c_certificate/touchtvInhouse.mobileprovision",

                        dest='mobileprovision_path',

                        help='mobileprovision_path', type=str, required=False)



    parser.add_argument('--certificate_name',

                        # default="iPhone Distribution: Huge Enterprises Inc.",

                        default="Beijing Gehua CATV Network Co., Ltd.",

                        dest='certificate_name',

                        help='certificate_name', type=str, required=False)



    parser.add_argument('--ipa_http_url',

                        default="",

                        dest='ipa_http_url',

                        help='ipa_http_url ex(https://aaa.list.ppp/)', type=str, required=False)



    args = parser.parse_args()

    return args





def main():

    task = Task()

    startTime = time.time()



    # 参数

    app_args = parse_args()

    soucre_ipa_name = app_args.soucre_ipa_name

    pgyer_ukey = app_args.pgyer_ukey

    pgyer_apikey = app_args.pgyer_apikey

    mobileprovision_path = app_args.mobileprovision_path

    certificate_name = app_args.certificate_name

    ipa_http_url = app_args.ipa_http_url



    # 校验路径

    if os.path.exists(workspace_desktop_path) == False:

        os.mkdir(workspace_desktop_path)



    # 路径

    workspace_ota_path = workspace_path + "s_ota-tools" + os.sep



    # 签名

    source_ipa_path = workspace_path + soucre_ipa_name

    export_resigned_path, export_desktop_ipa_path, export_info_path, export_desktop_info_path, pgyer_desc = \

        task.run_sign(

            workspace_ota_path,

            source_ipa_path,

            workspace_path + mobileprovision_path,

            certificate_name,

            ipa_http_url)



    # 上传

    task.run_pgyer_upload(export_desktop_ipa_path, pgyer_ukey, pgyer_apikey, pgyer_desc)



    # 移除文件

    # if os.path.exists(export_resigned_path):

    #     os.remove(export_resigned_path)

    # if os.path.exists(export_info_path):

    #     os.remove(export_info_path)

    # if os.path.exists(source_ipa_path):

    #     os.remove(source_ipa_path)



    endTime = time.time()

    print("总耗时: %0.2f" % (endTime - startTime))





if __name__ == "__main__":

    main()

image

总结

  • 这篇涉及jenkin的job配置,其中涉及打包的一些配置,另外还涉及企签一些知识,另外,hexo_blog也是部署在另外一台jenkins上的,使用码云的git-hook,发送post自动打包,上传到七牛.