Files
ShadowsocksX-NG/ShadowsocksX-NG2/Taskfile.package.yml

200 lines
9.2 KiB
YAML

# https://taskfile.dev/
version: '3'
vars:
DIST_DIR: '{{.TASKFILE_DIR}}/dist'
DMG_REQUIREMENT: '=anchor apple generic and certificate leaf[field.1.2.840.113635.100.6.1.13] exists'
tasks:
package:
desc: Build, sign, notarize and publish an arm64 Release DMG
dir: '{{.TASKFILE_DIR}}'
requires:
vars: [NOTARY_PROFILE, MARKETING_VERSION, CURRENT_PROJECT_VERSION]
preconditions:
- sh: 'test -n {{.NOTARY_PROFILE | shellQuote}}'
msg: 'NOTARY_PROFILE must name credentials stored by notarytool store-credentials.'
- sh: '{{if regexMatch .RELEASE_VERSION_PATTERN .MARKETING_VERSION}}true{{else}}false{{end}}'
msg: 'MARKETING_VERSION must be a release version (for example, 2.1.0 or v2.0.0-alpha-1).'
- sh: '{{if regexMatch "^[0-9]+(\\.[0-9]+){0,2}$" .CURRENT_PROJECT_VERSION}}true{{else}}false{{end}}'
msg: 'CURRENT_PROJECT_VERSION must have one to three numeric components.'
- sh: 'test ! -e {{.RELEASE_PATH | shellQuote}} && test ! -L {{.RELEASE_PATH | shellQuote}}'
msg: 'This release already exists in dist; existing releases are never overwritten: {{.RELEASE_PATH}}'
vars:
RELEASE_PATH: '{{.DIST_DIR}}/ShadowsocksX-NG2-{{.MARKETING_VERSION}}-{{.CURRENT_PROJECT_VERSION}}-arm64.dmg'
RUN_DIR:
sh: |
mkdir -p {{.BUILD_DIR | shellQuote}}/packaging
mktemp -d {{.BUILD_DIR | shellQuote}}/packaging/run-XXXXXX
cmds:
- 'printf "Packaging workspace: %s\n" {{.RUN_DIR | shellQuote}}'
- for: [xcodebuild, xcodegen, hdiutil, codesign, ditto, plutil, shasum, spctl, link]
cmd: 'command -v {{.ITEM}} > /dev/null'
- xcrun --find stapler
- >-
xcrun notarytool history --keychain-profile {{.NOTARY_PROFILE | shellQuote}}
--output-format json > {{.RUN_DIR | shellQuote}}/notary-history.json
- Scripts/fetch-external-binaries.sh
- task: test:smoke
vars:
DERIVED_DATA_DIR: '{{.RUN_DIR}}/derivedData'
RESULT_BUNDLE_PATH: '{{.RUN_DIR}}/smoke.xcresult'
- task: build:release
vars:
DERIVED_DATA_DIR: '{{.RUN_DIR}}/derivedData'
- task: package:image
vars:
RUN_DIR: '{{.RUN_DIR}}'
APP_PATH: '{{.RUN_DIR}}/derivedData/Build/Products/Release/ShadowsocksX-NG2.app'
package:image:
internal: true
dir: '{{.TASKFILE_DIR}}'
requires:
vars: [MARKETING_VERSION]
vars:
APP_VERSION:
sh: plutil -extract CFBundleShortVersionString raw -expect string -o - {{.APP_PATH | shellQuote}}/Contents/Info.plist
APP_BUILD:
sh: plutil -extract CFBundleVersion raw -expect string -o - {{.APP_PATH | shellQuote}}/Contents/Info.plist
IMAGE_VERSION: '{{.MARKETING_VERSION}}'
IMAGE_NAME: 'ShadowsocksX-NG2-{{.IMAGE_VERSION}}-{{.APP_BUILD}}-arm64.dmg'
IMAGE_PATH: '{{.RUN_DIR}}/{{.IMAGE_NAME}}'
preconditions:
- sh: '{{if regexMatch "^[0-9]+\\.[0-9]+\\.[0-9]+$" .APP_VERSION}}true{{else}}false{{end}}'
msg: 'Built App has an invalid CFBundleShortVersionString.'
- sh: '{{if regexMatch "^[0-9]+(\\.[0-9]+){0,2}$" .APP_BUILD}}true{{else}}false{{end}}'
msg: 'Built App has an invalid CFBundleVersion.'
- sh: '{{if and (regexMatch .RELEASE_VERSION_PATTERN .IMAGE_VERSION) (eq (regexFind "[0-9]+\\.[0-9]+\\.[0-9]+" .IMAGE_VERSION) .APP_VERSION)}}true{{else}}false{{end}}'
msg: 'Release version must be valid and match the built App version.'
- sh: 'test ! -e {{printf "%s/%s" .DIST_DIR .IMAGE_NAME | shellQuote}} && test ! -L {{printf "%s/%s" .DIST_DIR .IMAGE_NAME | shellQuote}}'
msg: 'This release already exists in dist; existing releases are never overwritten.'
cmds:
- Scripts/packaging-gate.sh {{.APP_PATH | shellQuote}}
- mkdir {{.RUN_DIR | shellQuote}}/stage
- ditto {{.APP_PATH | shellQuote}} {{.RUN_DIR | shellQuote}}/stage/ShadowsocksX-NG2.app
- ln -s /Applications {{.RUN_DIR | shellQuote}}/stage/Applications
- >-
hdiutil create -volname ShadowsocksX-NG2 -format UDZO
-srcfolder {{.RUN_DIR | shellQuote}}/stage {{.IMAGE_PATH | shellQuote}}
- codesign --display {{printf "--extract-certificates=%s/signing-" .RUN_DIR | shellQuote}} {{.APP_PATH | shellQuote}}
- task: package:sign
vars:
RUN_DIR: '{{.RUN_DIR}}'
DMG_PATH: '{{.IMAGE_PATH}}'
- task: package:notarize
vars:
DMG_PATH: '{{.IMAGE_PATH}}'
package:sign:
internal: true
vars:
CERTIFICATE_HASH:
sh: shasum -a 1 {{.RUN_DIR | shellQuote}}/signing-0
IDENTITY: '{{first (splitList " " .CERTIFICATE_HASH)}}'
preconditions:
- sh: '{{if regexMatch "^[0-9a-fA-F]{40}$" .IDENTITY}}true{{else}}false{{end}}'
msg: 'Cannot identify the certificate that signed the App.'
cmds:
- >-
codesign --sign {{.IDENTITY | shellQuote}} --timestamp
--identifier com.qiuyuzhou.ShadowsocksX-NG2.dmg {{.DMG_PATH | shellQuote}}
package:notarize:
desc: Notarize a signed DMG, staple its ticket and publish after verification
dir: '{{.TASKFILE_DIR}}'
requires:
vars: [DMG_PATH, NOTARY_PROFILE]
preconditions:
- sh: 'test -n {{.NOTARY_PROFILE | shellQuote}}'
msg: 'NOTARY_PROFILE must name credentials stored by notarytool store-credentials.'
- sh: '{{if and (hasPrefix "/" .DMG_PATH) (hasSuffix ".dmg" .DMG_PATH)}}true{{else}}false{{end}}'
msg: 'DMG_PATH must be an absolute path ending in .dmg.'
- sh: 'test -f {{.DMG_PATH | shellQuote}}'
msg: 'DMG_PATH does not name a file.'
- sh: 'test ! -e {{printf "%s/%s" .DIST_DIR (base .DMG_PATH) | shellQuote}} && test ! -L {{printf "%s/%s" .DIST_DIR (base .DMG_PATH) | shellQuote}}'
msg: 'This release already exists in dist; existing releases are never overwritten.'
vars:
LOG_DIR:
sh: |
mkdir -p {{.BUILD_DIR | shellQuote}}/notarization
mktemp -d {{.BUILD_DIR | shellQuote}}/notarization/run-XXXXXX
cmds:
- 'printf "Notarization evidence: %s\n" {{.LOG_DIR | shellQuote}}'
- codesign --verify --strict -R {{.DMG_REQUIREMENT | shellQuote}} {{.DMG_PATH | shellQuote}}
- shasum -a 256 {{.DMG_PATH | shellQuote}} > {{.LOG_DIR | shellQuote}}/input.sha256
- >-
xcrun notarytool submit {{.DMG_PATH | shellQuote}}
--keychain-profile {{.NOTARY_PROFILE | shellQuote}} --output-format json
> {{.LOG_DIR | shellQuote}}/submission.json
2> {{.LOG_DIR | shellQuote}}/submission.stderr.log
- task: package:wait
vars:
LOG_DIR: '{{.LOG_DIR}}'
- xcrun stapler staple {{.DMG_PATH | shellQuote}}
- xcrun stapler validate {{.DMG_PATH | shellQuote}}
- codesign --verify --strict -R {{.DMG_REQUIREMENT | shellQuote}} {{.DMG_PATH | shellQuote}}
- spctl --assess --type open --context context:primary-signature --verbose {{.DMG_PATH | shellQuote}}
- task: package:publish
vars:
DMG_PATH: '{{.DMG_PATH}}'
package:wait:
internal: true
vars:
SUBMISSION_ID:
sh: plutil -extract id raw -expect string -o - {{.LOG_DIR | shellQuote}}/submission.json
preconditions:
- sh: '{{if regexMatch "^[0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}$" .SUBMISSION_ID}}true{{else}}false{{end}}'
msg: 'No valid notarization submission ID; inspect submission.json and submission.stderr.log.'
cmds:
- defer:
task: package:notary-log
vars:
SUBMISSION_ID: '{{.SUBMISSION_ID}}'
LOG_DIR: '{{.LOG_DIR}}'
- >-
xcrun notarytool wait {{.SUBMISSION_ID | shellQuote}} --timeout 30m
--keychain-profile {{.NOTARY_PROFILE | shellQuote}} --output-format json
> {{.LOG_DIR | shellQuote}}/result.json
2> {{.LOG_DIR | shellQuote}}/wait.stderr.log
- task: package:accepted
vars:
LOG_DIR: '{{.LOG_DIR}}'
package:accepted:
internal: true
vars:
NOTARY_STATUS:
sh: plutil -extract status raw -expect string -o - {{.LOG_DIR | shellQuote}}/result.json
preconditions:
- sh: '{{if eq .NOTARY_STATUS "Accepted"}}true{{else}}false{{end}}'
msg: 'Apple did not accept this submission; inspect result.json and notary-log.json.'
cmds:
- 'printf "Notarization status: %s\n" {{.NOTARY_STATUS | shellQuote}}'
package:notary-log:
internal: true
cmds:
- cmd: >-
xcrun notarytool log {{.SUBMISSION_ID | shellQuote}}
--keychain-profile {{.NOTARY_PROFILE | shellQuote}}
{{.LOG_DIR | shellQuote}}/notary-log.json
2> {{.LOG_DIR | shellQuote}}/notary-log.stderr.log
ignore_error: true
package:publish:
internal: true
vars:
PUBLISH_DIR:
sh: |
mkdir -p {{.DIST_DIR | shellQuote}}
mktemp -d {{.DIST_DIR | shellQuote}}/.package-XXXXXX
OUTPUT_PATH: '{{.DIST_DIR}}/{{base .DMG_PATH}}'
cmds:
- defer: 'rm -rf {{.PUBLISH_DIR | shellQuote}}'
- ditto {{.DMG_PATH | shellQuote}} {{.PUBLISH_DIR | shellQuote}}/release.dmg
# Linking within dist publishes a complete file atomically and refuses replacement.
- link {{.PUBLISH_DIR | shellQuote}}/release.dmg {{.OUTPUT_PATH | shellQuote}}
- 'printf "Release DMG: %s\n" {{.OUTPUT_PATH | shellQuote}}'