EricJJ' Blog

OneDay App 开发上线记

2020-02-04

  1. 1. Archive, Export, Sign
  2. 2. Upload

https://oneday.ericjj.com/

OneDay Mac App 从年前开始开发,中间改了改需求, 把需求往后挪后, 就先上线了第一版。最初的设计稿:

中间从 UIKit 到 Cocoa 踩了不少坑,分享一些主要使用的东西:

Paddle: Paddle 主要解决个人开发者软件付费的问题。提供了 PayPal 付款方案。

脚本若干:

Archive, Export, Sign


mkdir -p build

project_list=`ls | grep .xcodeproj`
project_name=${project_list%%.*}
new_project_name="OneDay"
build_plist="./${project_name}/Info.plist"

echo "✅ archive ${app_version}"
xcodebuild archive \
  -workspace ${project_name}.xcworkspace \
  -scheme ${project_name} \
  -configuration Release \
  -archivePath ./build/archive.xcarchive

echo "✅ export archive"
xcodebuild -exportArchive \
  -archivePath ./build/archive.xcarchive \
  -exportPath ./build/ \
  -exportOptionsPlist exportPlist.plist

CFBundleShortVersionString=$(/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" $build_plist)
CFBundleVersion=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" $build_plist)
app_version="${CFBundleShortVersionString}.${CFBundleVersion}"
tar_filename="${new_project_name}${app_version}.tar.gz"

echo "✅ tar ${tar_filename}"
rm -rf build/${new_project_name}.app
python sign.py
mv  ${project_name}.app ${new_project_name}.app
tar zcvf ${tar_filename} ${new_project_name}.app

exportPlist.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>teamID</key>
    <string>XXX</string>
    <key>method</key>
    <string>developer-id</string>
    <key> compileBitcode</key>
    <false/>
</dict>
</plist>

sign.py 签名脚本

#!/usr/bin/python

import os
import commands

APP_PATH = "build/XXX.app"
DEVELOPER_ID = "Developer ID Application: XXX"

def signWithPath(path):
    signCommand = "codesign --force --sign \"%s\" \"%s\"" % (DEVELOPER_ID, path)
    retCode, result = commands.getstatusoutput(signCommand)
    if retCode != 0:
        print result
        print "code sign failed"
    return retCode

def validateWithPath(path):
    signCommand = "codesign --verify --deep --verbose=3 \"%s\"" % path
    retCode, result = commands.getstatusoutput(signCommand)
    if retCode == 0:
        print "Accepted!"
        return 0
    else:
        print result
        print "Rejected!"
        return -2

def sign():
    print "> signing frameworks & dylibs..."

    if not os.path.exists(APP_PATH):
        print "where's your app?!"
        return

    frameworkDir = os.path.join(APP_PATH, "Contents/")

    # sign dylibs
    for root, dirs, files in os.walk(frameworkDir):
        for f in files:
            if f.endswith(".dylib"):
                print "signing", f
                dylibPath = os.path.join(root, f)
                signWithPath(dylibPath)

    # sign frameworks
    for root, dirs, files in os.walk(frameworkDir):
        for d in dirs:
            if d.endswith(".framework"):
                print "signing", d
                frameworkPath = os.path.join(root, d, "Versions/A")
                signWithPath(frameworkPath)


    print "> singing app..."
    print "singing", APP_PATH
    signWithPath(APP_PATH)

    print "> validate code sign..."
    if validateWithPath(APP_PATH) == 0:
        print "Code sign completed!"
    else:
        print "Dohhh!"


if __name__ == '__main__':
    sign()

Upload

我的下载链接是挂在github仓库下,使用脚本上传

token=xxx
repo=xxx

project_list=`ls | grep .xcodeproj`
project_name=${project_list%%.*}
new_project_name="OneDay"
build_plist="./${project_name}/Info.plist"

CFBundleShortVersionString=$(/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" $build_plist)
CFBundleVersion=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" $build_plist)
app_version="${CFBundleShortVersionString}.${CFBundleVersion}"
tar_filename="${new_project_name}${app_version}.tar.gz"


ids=$(curl -s -H "Authorization: token $token"  \
     "https://api.github.com/repos/$repo/releases" | jq .[].id)
echo $ids

for i in $ids;do
    echo 'delete' $i
    curl -s -X DELETE -H "Authorization: token $token"  \
     "https://api.github.com/repos/$repo/releases/$i"
done

upload_url=$(curl -s -H "Authorization: token $token"  \
     -d '{"tag_name": "'${app_version}'", "name":"'${tar_filename}'","body":"release"}'  \
     "https://api.github.com/repos/$repo/releases" | jq -r '.upload_url')

upload_url="${upload_url%\{*}"

echo "uploading asset to release to url : $upload_url $tar_filename"
download_url=$(curl -s -H "Authorization: token $token"  \
        -H "Content-Type: application/zip" \
        --data-binary  @"build/$tar_filename" \
        "$upload_url?name=${tar_filename}&label=${tar_filename}"  | jq -r '.browser_download_url')

echo "$tar_filename download url : $download_url"
echo "update readme"
cd ~/code/OneDayMac.github.io
git pull
python3 update_readme.py $download_url
cat index.md
git commit -a -m "${app_version} archive"
git push
cd ~/code/oneday/OnedayMac

这样每次发布直接执行 ./build.sh && ./release.sh, 就会自动编译, 打包出 .app 文件, 先签名再压缩后传到 github release api, 然后更新网站。