From cec8fd884a68ebee125d4b98c27dff15d8c8ecb5 Mon Sep 17 00:00:00 2001 From: Chad Sykes Date: Sun, 3 Jan 2021 14:59:47 -0700 Subject: [PATCH 1/5] Add a script to incrementing the project build number in a yyyymmddnnn format where the nnn is an auto incrementing number (i.e. 20210103001) --- Scripts/increment_build_number.sh | 18 ++++++++++++++++++ Xcodes/Resources/Info.plist | 8 ++++---- 2 files changed, 22 insertions(+), 4 deletions(-) create mode 100755 Scripts/increment_build_number.sh diff --git a/Scripts/increment_build_number.sh b/Scripts/increment_build_number.sh new file mode 100755 index 0000000..cbf13d8 --- /dev/null +++ b/Scripts/increment_build_number.sh @@ -0,0 +1,18 @@ +#!/bin/sh +PROJECT_DIR=$(pwd)/Xcodes/Resources +INFOPLIST_FILE="Info.plist" +buildString=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "${PROJECT_DIR}/${INFOPLIST_FILE}") +buildDate=$(echo $buildString | cut -c 1-8) +buildNumber=$(echo $buildString | cut -c 9-11) +today=$(date +'%Y%m%d') + +if [[ $buildDate = $today ]] +then +buildNumber=$(($buildNumber + 1)) +else +buildNumber=1 +fi + +buildString=$(printf '%s%03u' $today $buildNumber) + +/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $buildString" "${PROJECT_DIR}/${INFOPLIST_FILE}" diff --git a/Xcodes/Resources/Info.plist b/Xcodes/Resources/Info.plist index 0d3bfc9..2f5badf 100644 --- a/Xcodes/Resources/Info.plist +++ b/Xcodes/Resources/Info.plist @@ -19,7 +19,9 @@ CFBundleShortVersionString 1.0.0 CFBundleVersion - 1 + 20210103001 + CODE_SIGNING_SUBJECT_ORGANIZATIONAL_UNIT + $(CODE_SIGNING_SUBJECT_ORGANIZATIONAL_UNIT) LSMinimumSystemVersion $(MACOSX_DEPLOYMENT_TARGET) NSHumanReadableCopyright @@ -33,9 +35,7 @@ SMPrivilegedExecutables com.robotsandpencils.XcodesApp.Helper - identifier "com.robotsandpencils.XcodesApp.Helper" and info [CFBundleShortVersionString] >= "1.0.0" and anchor apple generic and certificate leaf[subject.OU] = "$(CODE_SIGNING_SUBJECT_ORGANIZATIONAL_UNIT)" + identifier "com.robotsandpencils.XcodesApp.Helper" and info [CFBundleShortVersionString] >= "1.0.0" and anchor apple generic and certificate leaf[subject.OU] = "$(CODE_SIGNING_SUBJECT_ORGANIZATIONAL_UNIT)" - CODE_SIGNING_SUBJECT_ORGANIZATIONAL_UNIT - $(CODE_SIGNING_SUBJECT_ORGANIZATIONAL_UNIT) From 6b9b36d6eeb6580142b57c3b6d7d5c0dd1d73230 Mon Sep 17 00:00:00 2001 From: Chad Sykes Date: Sun, 3 Jan 2021 15:02:29 -0700 Subject: [PATCH 2/5] Add a packageRelease.sh script to help automate the generation of release assets --- .gitignore | 2 ++ Scripts/export_options.plist | 8 ++++++++ Scripts/package_release.sh | 24 ++++++++++++++++++++++++ 3 files changed, 34 insertions(+) create mode 100644 Scripts/export_options.plist create mode 100755 Scripts/package_release.sh diff --git a/.gitignore b/.gitignore index e27d315..18d371b 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,8 @@ DerivedData/ *.ipa *.dSYM.zip *.dSYM +Archive/* +Product/* ## Playgrounds timeline.xctimeline diff --git a/Scripts/export_options.plist b/Scripts/export_options.plist new file mode 100644 index 0000000..055f67c --- /dev/null +++ b/Scripts/export_options.plist @@ -0,0 +1,8 @@ + + + + + method + developer-id + + diff --git a/Scripts/package_release.sh b/Scripts/package_release.sh new file mode 100755 index 0000000..32b6432 --- /dev/null +++ b/Scripts/package_release.sh @@ -0,0 +1,24 @@ +#!/bin/bash +# +# Package release +# +# This will build and archive the app and then compress it in a .zip file +# You must already have all required code signing assets installed on your computer + +PROJECT_NAME=Xcodes +PROJECT_DIR=$(pwd)/$PROJECT_NAME/Resources +SCRIPTS_DIR=$(pwd)/Scripts +INFOPLIST_FILE="Info.plist" + +CFBundleVersion=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "${PROJECT_DIR}/${INFOPLIST_FILE}") +CFBundleShortVersionString=$(/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" "${PROJECT_DIR}/${INFOPLIST_FILE}") + +# Ensure a clean build +rm -rf Archive/* +rm -rf Product/* +xcodebuild clean -project $PROJECT_NAME.xcodeproj -configuration Release -alltargets + +# Archive the app and export for release distribution +xcodebuild archive -project $PROJECT_NAME.xcodeproj -scheme $PROJECT_NAME -archivePath Archive/$PROJECT_NAME.xcarchive +xcodebuild -archivePath Archive/$PROJECT_NAME.xcarchive -exportArchive -exportPath Product/$PROJECT_NAME.app -exportOptionsPlist "${SCRIPTS_DIR}/export_options.plist" +zip -r "Product/$PROJECT_NAME.v${CFBundleShortVersionString}.b${CFBundleVersion}.zip" Product/$PROJECT_NAME.app From 38cf21625bc3a69ef2875eae541cd1e7c1337ebc Mon Sep 17 00:00:00 2001 From: Brandon Evans Date: Mon, 18 Jan 2021 18:52:07 -0700 Subject: [PATCH 3/5] Add steps for releasing a new version to README --- README.md | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/README.md b/README.md index b38ea7b..66e528e 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,42 @@ Notable design decisions are recorded in [DECISIONS.md](./DECISIONS.md). The App [`xcode-install`](https://github.com/xcpretty/xcode-install) and [fastlane/spaceship](https://github.com/fastlane/fastlane/tree/master/spaceship) both deserve credit for figuring out the hard parts of what makes this possible. +## Releasing a new version + +Follow the steps below to build and release a new version of Xcodes.app. For any of the git steps, you can use your preferred tool, but please sign the tag. + +```sh +# Update the version number in Xcode and commit the change, if necessary + +# Increment the build number +scripts/increment_build_number.sh + +# Commit the change +git add Xcodes/Resources/Info.plist +git commit -asm "Increment build number" + +# Tag the latest commit +# Replace $VERSION and $BUILD below with the latest real values +git tag -asm "v$VERSION.b$BUILD" "v$VERSION.b$BUILD" + +# Push to origin +git push --follow-tags + +# Build the app +scripts/package_release.sh + +# Notarize the app +... + +# Go to https://github.com/RobotsAndPencils/XcodesApp/releases +# Edit the latest draft release +# Set its tag to the tag you just pushed +# Set its title to a string with the format "$VERSION ($BUILD)" +# Polish the draft release notes, if necessary +# Attach the zip that was created in the Product directory to the release +# Publish the release +``` + ## Contact From 8a0fa1da2ea6f49aaf128370be87600e139fab8b Mon Sep 17 00:00:00 2001 From: Brandon Evans Date: Mon, 18 Jan 2021 19:11:09 -0700 Subject: [PATCH 4/5] Use git tags to track build numbers --- Scripts/increment_build_number.sh | 30 ++++++++++++++++-------------- Xcodes/Resources/Info.plist | 2 +- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/Scripts/increment_build_number.sh b/Scripts/increment_build_number.sh index cbf13d8..f8ac44e 100755 --- a/Scripts/increment_build_number.sh +++ b/Scripts/increment_build_number.sh @@ -1,18 +1,20 @@ #!/bin/sh -PROJECT_DIR=$(pwd)/Xcodes/Resources -INFOPLIST_FILE="Info.plist" -buildString=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "${PROJECT_DIR}/${INFOPLIST_FILE}") -buildDate=$(echo $buildString | cut -c 1-8) -buildNumber=$(echo $buildString | cut -c 9-11) -today=$(date +'%Y%m%d') +# +# Increment build number +# +# This will get the latest build number from git tags, add 1, then set it in the Info.plist. +# Assumes that build numbers are monotonically increasing positive integers, across version numbers. +# Tags must be named v$version_numberb$build_number, e.g. v1.2.3b456 -if [[ $buildDate = $today ]] -then -buildNumber=$(($buildNumber + 1)) -else -buildNumber=1 -fi +infoplist_file="$(pwd)/Xcodes/Resources/Info.plist" -buildString=$(printf '%s%03u' $today $buildNumber) +# Get latest tag hash matching pattern +hash=$(git rev-list --tags="v" --max-count=1) +# Get latest tag at hash that matches the same pattern as a prefix in order to support commits with multiple tags +last_tag=$(git describe --tags --match "v*" "$hash") +# Get build number from last component of tag name +last_build_number=$(echo "$last_tag" | grep -o "b.*" | cut -c 2-) -/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $buildString" "${PROJECT_DIR}/${INFOPLIST_FILE}" +build_number=$(($last_build_number + 1)) + +/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $build_number" "${infoplist_file}" diff --git a/Xcodes/Resources/Info.plist b/Xcodes/Resources/Info.plist index 2f5badf..0a907aa 100644 --- a/Xcodes/Resources/Info.plist +++ b/Xcodes/Resources/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.0.0 CFBundleVersion - 20210103001 + 1 CODE_SIGNING_SUBJECT_ORGANIZATIONAL_UNIT $(CODE_SIGNING_SUBJECT_ORGANIZATIONAL_UNIT) LSMinimumSystemVersion From 6e0daa53250c14d2a7bf0500627116a9994f20aa Mon Sep 17 00:00:00 2001 From: Brandon Evans Date: Mon, 18 Jan 2021 19:38:37 -0700 Subject: [PATCH 5/5] Add notarization script --- README.md | 2 +- Scripts/notarize.sh | 69 ++++++++++++++++++++++++++++++++++++++ Scripts/package_release.sh | 12 +++---- 3 files changed, 76 insertions(+), 7 deletions(-) create mode 100755 Scripts/notarize.sh diff --git a/README.md b/README.md index 66e528e..581e8da 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ git push --follow-tags scripts/package_release.sh # Notarize the app -... +scripts/notarize.sh "test@example.com" "@keychain:altool" MyOrg Product/Xcodes.zip # Go to https://github.com/RobotsAndPencils/XcodesApp/releases # Edit the latest draft release diff --git a/Scripts/notarize.sh b/Scripts/notarize.sh new file mode 100755 index 0000000..89de6d4 --- /dev/null +++ b/Scripts/notarize.sh @@ -0,0 +1,69 @@ +#!/bin/sh +# +# Notarize +# +# Uploads to Apple's notarization service, polls until it completes, staples the ticket to the built app, then creates a new zip. +# +# Requires four arguments: +# - Apple ID username +# - Apple ID app-specific password (store this in your Keychain and use the @keychain:$NAME syntax to prevent your password from being added to your shell history) +# - App Store Connect provider name +# - Path to .app to upload +# +# Assumes that there's a .app beside the .zip with the same name so it can be stapled and re-zipped. +# +# E.g. notarize.sh "test@example.com" "@keychain:altool" MyOrg Xcodes.zip +# +# https://developer.apple.com/documentation/xcode/notarizing_macos_software_before_distribution/customizing_the_notarization_workflow +# Adapted from https://github.com/keybase/client/blob/46f5df0aa64ff19198ba7b044bbb7cd907c0be9f/packaging/desktop/package_darwin.sh + +username="$1" +password="$2" +asc_provider="$3" +file="$4" + +echo "Uploading to notarization service" + +uuid=$(xcrun altool \ + --notarize-app \ + --primary-bundle-id "com.robotsandpencils.XcodesApp.zip" \ + --username "$username" \ + --password "$password" \ + --asc-provider "$asc_provider" \ + --file "$file" 2>&1 | \ + grep 'RequestUUID' | \ + awk '{ print $3 }') + +echo "Successfully uploaded to notarization service, polling for result: $uuid" + +sleep 15 + while : + do + fullstatus=$(xcrun altool \ + --notarization-info "$uuid" \ + --username "$username" \ + --password "$password" \ + --asc-provider "$asc_provider" 2>&1) + status=$(echo "$fullstatus" | grep 'Status\:' | awk '{ print $2 }') + if [ "$status" = "success" ]; then + echo "Notarization success" + exit 0 + elif [ "$status" = "in" ]; then + echo "Notarization still in progress, sleeping for 15 seconds and trying again" + sleep 15 + else + echo "Notarization failed, full status below" + echo "$fullstatus" + exit 1 + fi + done + +# Remove .zip +rm $file + +# Staple ticket to .app +app_path="$(basename -s ".zip" "$file").app" +xcrun stapler staple "$app_path" + +# Zip the stapled app for distribution +zip -r "$file" "$app_path" diff --git a/Scripts/package_release.sh b/Scripts/package_release.sh index 32b6432..d4fbb7d 100755 --- a/Scripts/package_release.sh +++ b/Scripts/package_release.sh @@ -2,7 +2,7 @@ # # Package release # -# This will build and archive the app and then compress it in a .zip file +# This will build and archive the app and then compress it in a .zip file at Product/Xcodes.zip # You must already have all required code signing assets installed on your computer PROJECT_NAME=Xcodes @@ -10,9 +10,6 @@ PROJECT_DIR=$(pwd)/$PROJECT_NAME/Resources SCRIPTS_DIR=$(pwd)/Scripts INFOPLIST_FILE="Info.plist" -CFBundleVersion=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "${PROJECT_DIR}/${INFOPLIST_FILE}") -CFBundleShortVersionString=$(/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" "${PROJECT_DIR}/${INFOPLIST_FILE}") - # Ensure a clean build rm -rf Archive/* rm -rf Product/* @@ -20,5 +17,8 @@ xcodebuild clean -project $PROJECT_NAME.xcodeproj -configuration Release -alltar # Archive the app and export for release distribution xcodebuild archive -project $PROJECT_NAME.xcodeproj -scheme $PROJECT_NAME -archivePath Archive/$PROJECT_NAME.xcarchive -xcodebuild -archivePath Archive/$PROJECT_NAME.xcarchive -exportArchive -exportPath Product/$PROJECT_NAME.app -exportOptionsPlist "${SCRIPTS_DIR}/export_options.plist" -zip -r "Product/$PROJECT_NAME.v${CFBundleShortVersionString}.b${CFBundleVersion}.zip" Product/$PROJECT_NAME.app +xcodebuild -archivePath Archive/$PROJECT_NAME.xcarchive -exportArchive -exportPath Product/$PROJECT_NAME -exportOptionsPlist "${SCRIPTS_DIR}/export_options.plist" +cp -r "Product/$PROJECT_NAME/$PROJECT_NAME.app" "Product/$PROJECT_NAME.app" + +# Create a ZIP archive suitable for altool. +/usr/bin/ditto -c -k --keepParent "Product/$PROJECT_NAME.app" "Product/$PROJECT_NAME.zip"