Build and release #572
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: "Build and release" | |
on: | |
workflow_dispatch: # run manually only | |
jobs: | |
latest-release: | |
name: "Latest Release" | |
runs-on: macos-14 | |
steps: | |
- name: Check out code | |
uses: actions/checkout@v4 | |
- name: Set release flags based on branch | |
run: | | |
echo "X_SHORT_HASH=`git rev-parse --short HEAD`" >> $GITHUB_ENV | |
echo "X_BUILD_MONIKER=${{ github.ref_name == 'development' && 'preview' || startsWith(github.ref_name, '1.') && 'stable' || 'internal' }}" >> $GITHUB_ENV | |
grep -Eo 'version.*\.\d+' src/manifest/manifest-chrome.json > ver.tmp | |
sed -E 's/[^0-9]*([0-9.]+).*/X_VERSION=\1/' ver.tmp >> $GITHUB_ENV | |
# Safari can only use the first 3 digits; we'll trim to 2 if the last digit is 0 | |
sed -E 's/[^0-9]*([0-9]+\.[0-9]+(\.[1-9][0-9]*)?).*/X_SAFARI_VERSION=\1/' ver.tmp >> $GITHUB_ENV | |
# build number is the number of seconds since 2023-08-01 UTC (prefixed by a 3 for the version format) | |
# we have to add 300000000 to make it greater than build numbers generated in prior formats | |
echo "X_SAFARI_BUILD=$((`date -u +%s`+300000000-1690848000))" >> $GITHUB_ENV | |
- name: Setup Node.js | |
uses: actions/setup-node@v4 | |
with: | |
node-version: 20.x | |
cache: "npm" | |
- name: Install dependencies | |
run: npm ci | |
- name: Download WikiTree+ templates | |
# only run this once per release to ensure it is identical in all builds | |
run: npm run download-templates | |
- name: Build for Chrome | |
run: | | |
npm run clean | |
npm run build | |
cd dist | |
zip -r ../dist-chrome.zip * | |
cd .. | |
- name: Prepare Xcode environment | |
env: | |
X_SAFARI_NAME: ${{ env.X_BUILD_MONIKER == 'stable' && 'WikiTree Browser Extension' || 'WikiTree Browser Extension Preview' }} | |
X_SAFARI_PROJECT: xcode/${{ env.X_BUILD_MONIKER == 'stable' && 'WikiTree Browser Extension' || 'WikiTree Browser Extension Preview' }}/${{ env.X_BUILD_MONIKER == 'stable' && 'WikiTree Browser Extension' || 'WikiTree Browser Extension Preview' }}.xcodeproj | |
run: | | |
echo "X_SAFARI_NAME=${{ env.X_SAFARI_NAME }}" >> $GITHUB_ENV | |
echo "X_SAFARI_PROJECT=${{ env.X_SAFARI_PROJECT }}" >> $GITHUB_ENV | |
mv -f "${{ env.X_SAFARI_PROJECT }}/project.pbxproj" "${{ env.X_SAFARI_PROJECT }}/project.pbxproj.tmp" | |
sed -E 's/MARKETING_VERSION[ ]*=[ ]*[0-9.]+/MARKETING_VERSION = ${{ env.X_SAFARI_VERSION }}/Ig' "${{ env.X_SAFARI_PROJECT }}/project.pbxproj.tmp" > "${{ env.X_SAFARI_PROJECT }}/project.pbxproj" | |
mv -f "${{ env.X_SAFARI_PROJECT }}/project.pbxproj" "${{ env.X_SAFARI_PROJECT }}/project.pbxproj.tmp" | |
sed -E 's/CURRENT_PROJECT_VERSION[ ]*=[ ]*[0-9.]+/CURRENT_PROJECT_VERSION = ${{ env.X_SAFARI_BUILD }}/Ig' "${{ env.X_SAFARI_PROJECT }}/project.pbxproj.tmp" > "${{ env.X_SAFARI_PROJECT }}/project.pbxproj" | |
- name: Install the Apple certificates and provisioning profiles | |
if: vars.APPLE_SIGNING_ENABLED == 'true' | |
env: | |
P12_DISTRIBUTION: ${{ secrets.P12_DISTRIBUTION }} | |
P12_INSTALLATION: ${{ secrets.P12_INSTALLATION }} | |
P12_PASSWORD: ${{ secrets.P12_PASSWORD }} | |
PROVISIONING_APP_IOS: ${{ env.X_BUILD_MONIKER == 'stable' && secrets.PROVISIONING_STABLE_APP_IOS || secrets.PROVISIONING_PREVIEW_APP_IOS }} | |
PROVISIONING_APP_MACOS: ${{ env.X_BUILD_MONIKER == 'stable' && secrets.PROVISIONING_STABLE_APP_MACOS || secrets.PROVISIONING_PREVIEW_APP_MACOS }} | |
PROVISIONING_EXT_IOS: ${{ env.X_BUILD_MONIKER == 'stable' && secrets.PROVISIONING_STABLE_EXT_IOS || secrets.PROVISIONING_PREVIEW_EXT_IOS }} | |
PROVISIONING_EXT_MACOS: ${{ env.X_BUILD_MONIKER == 'stable' && secrets.PROVISIONING_STABLE_EXT_MACOS || secrets.PROVISIONING_PREVIEW_EXT_MACOS }} | |
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }} | |
run: | | |
# create variables for temporary files | |
DIST_CERT_PATH=$RUNNER_TEMP/dist_certificate.p12 | |
INST_CERT_PATH=$RUNNER_TEMP/inst_certificate.p12 | |
PP_APP_IOS_PATH=$RUNNER_TEMP/build_app_ios.mobileprovision | |
PP_APP_MACOS_PATH=$RUNNER_TEMP/build_app_macos.provisionprofile | |
PP_EXT_IOS_PATH=$RUNNER_TEMP/build_ext_ios.mobileprovision | |
PP_EXT_MACOS_PATH=$RUNNER_TEMP/build_ext_macos.provisionprofile | |
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db | |
# import certificates and provisioning profiles from secrets | |
echo -n "$P12_DISTRIBUTION" | base64 --decode -o $DIST_CERT_PATH | |
echo -n "$P12_INSTALLATION" | base64 --decode -o $INST_CERT_PATH | |
echo -n "$PROVISIONING_APP_IOS" | base64 --decode -o $PP_APP_IOS_PATH | |
echo -n "$PROVISIONING_APP_MACOS" | base64 --decode -o $PP_APP_MACOS_PATH | |
echo -n "$PROVISIONING_EXT_IOS" | base64 --decode -o $PP_EXT_IOS_PATH | |
echo -n "$PROVISIONING_EXT_MACOS" | base64 --decode -o $PP_EXT_MACOS_PATH | |
# create temporary keychain | |
security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH | |
security set-keychain-settings -lut 21600 $KEYCHAIN_PATH | |
security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH | |
# import certificates to keychain | |
security import $DIST_CERT_PATH -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH | |
security import $INST_CERT_PATH -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH | |
security list-keychain -d user -s $KEYCHAIN_PATH | |
# apply provisioning profiles | |
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles | |
cp $PP_APP_IOS_PATH ~/Library/MobileDevice/Provisioning\ Profiles | |
cp $PP_APP_MACOS_PATH ~/Library/MobileDevice/Provisioning\ Profiles | |
cp $PP_EXT_IOS_PATH ~/Library/MobileDevice/Provisioning\ Profiles | |
cp $PP_EXT_MACOS_PATH ~/Library/MobileDevice/Provisioning\ Profiles | |
# Safari Versions use dist already built from Chrome | |
- name: Build signed version for Safari (iOS) | |
if: vars.APPLE_SIGNING_ENABLED == 'true' | |
run: | | |
xcodebuild clean archive -quiet -project "${{ env.X_SAFARI_PROJECT }}" -scheme "${{ env.X_SAFARI_NAME }} (iOS)" -configuration Release -destination generic/platform=iOS -archivePath xcode/build-ios | |
xcodebuild -exportArchive -archivePath xcode/build-ios.xcarchive -exportPath xcode/build-ios.xcexport -exportOptionsPlist xcode/ExportOptions.${{ env.X_BUILD_MONIKER == 'stable' && 'stable' || 'preview' }}.ios.plist | |
cp "xcode/build-ios.xcexport/${{ env.X_SAFARI_NAME }}.ipa" dist-safari-ios.ipa | |
- name: Build signed version for Safari (macOS) | |
if: vars.APPLE_SIGNING_ENABLED == 'true' | |
run: | | |
xcodebuild clean archive -quiet -project "${{ env.X_SAFARI_PROJECT }}" -scheme "${{ env.X_SAFARI_NAME }} (macOS)" -configuration Release -destination generic/platform=macOS -archivePath xcode/build-macos | |
xcodebuild -exportArchive -archivePath xcode/build-macos.xcarchive -exportPath xcode/build-macos.xcexport -exportOptionsPlist xcode/ExportOptions.${{ env.X_BUILD_MONIKER == 'stable' && 'stable' || 'preview' }}.macos.plist | |
cp "xcode/build-macos.xcexport/${{ env.X_SAFARI_NAME }}.pkg" dist-safari-macos.pkg | |
# if signing is disabled or not configured, build the unsigned version for macOS only (iOS requires signing) | |
- name: Build unsigned version for Safari (macOS) | |
if: vars.APPLE_SIGNING_ENABLED != 'true' | |
run: | | |
mv -f "${{ env.X_SAFARI_PROJECT }}/project.pbxproj" "${{ env.X_SAFARI_PROJECT }}/project.pbxproj.tmp" | |
sed -E 's/((CODE_SIGN_IDENTITY|CODE_SIGN_STYLE|DEVELOPMENT_TEAM|PROVISIONING_PROFILE_SPECIFIER).*;)/\/* \1 *\//g' "${{ env.X_SAFARI_PROJECT }}/project.pbxproj.tmp" > "${{ env.X_SAFARI_PROJECT }}/project.pbxproj" | |
xcodebuild clean archive -quiet -project "${{ env.X_SAFARI_PROJECT }}" -scheme "${{ env.X_SAFARI_NAME }} (macOS)" -configuration Release -destination generic/platform=macOS -archivePath xcode/build-unsigned | |
cd xcode/build-unsigned.xcarchive/Products/Applications | |
zip -r ../../../../dist-safari-macos.zip "${{ env.X_SAFARI_NAME }}.app" | |
cd ../../../.. | |
- name: Build for Firefox | |
run: | | |
npm run clean | |
npm run build-firefox | |
cd dist | |
zip -r ../dist-firefox.zip * | |
cd .. | |
- name: Upload packages to Apple | |
if: vars.APPLE_SIGNING_ENABLED == 'true' && vars.APPLE_UPLOADING_ENABLED == 'true' | |
env: | |
API_ISSUER_ID: ${{ secrets.API_ISSUER_ID }} | |
API_KEY_ID: ${{ secrets.API_KEY_ID }} | |
API_KEY_P8: ${{ secrets.API_KEY_P8 }} | |
run: | | |
mkdir private_keys | |
P8_PATH=private_keys/AuthKey_$API_KEY_ID.p8 | |
echo -n "$API_KEY_P8" | base64 --decode -o $P8_PATH | |
xcrun altool --validate-app -f dist-safari-ios.ipa -t ios --apiKey $API_KEY_ID --apiIssuer $API_ISSUER_ID | |
xcrun altool --validate-app -f dist-safari-macos.pkg -t macos --apiKey $API_KEY_ID --apiIssuer $API_ISSUER_ID | |
# upload-app is deprecated for upload-package, so we need to update at some point | |
xcrun altool --upload-app -f dist-safari-ios.ipa -t ios --apiKey $API_KEY_ID --apiIssuer $API_ISSUER_ID | |
xcrun altool --upload-app -f dist-safari-macos.pkg -t macos --apiKey $API_KEY_ID --apiIssuer $API_ISSUER_ID | |
- name: Rename release assets | |
env: | |
X_DIST_PREFIX: ${{ format('wbe-{0}-{1}-{2}', (env.X_BUILD_MONIKER == 'stable' || env.X_BUILD_MONIKER == 'preview') && env.X_BUILD_MONIKER || format('{0}-{1}', env.X_BUILD_MONIKER, github.ref_name), env.X_VERSION, env.X_SHORT_HASH) }} | |
run: | | |
mv dist-chrome.zip "${{ env.X_DIST_PREFIX }}-chrome.zip" | |
mv dist-firefox.zip "${{ env.X_DIST_PREFIX }}-firefox.zip" | |
[ ! -f dist-safari-ios.ipa ] || mv dist-safari-ios.ipa "${{ env.X_DIST_PREFIX }}-safari-ios.ipa" | |
[ ! -f dist-safari-macos.pkg ] || mv dist-safari-macos.pkg "${{ env.X_DIST_PREFIX }}-safari-macos.pkg" | |
[ ! -f dist-safari-macos.zip ] || mv dist-safari-macos.zip "${{ env.X_DIST_PREFIX }}-safari-macos.zip" | |
- name: Update release | |
env: | |
GH_TOKEN: ${{ github.token }} | |
X_GH_RELEASE_TAG: "${{ (env.X_BUILD_MONIKER == 'stable' || env.X_BUILD_MONIKER == 'preview') && env.X_BUILD_MONIKER || format('{0}/{1}', env.X_BUILD_MONIKER, github.ref_name) }}" | |
X_GH_RELEASE_TITLE: ${{ format('--title "{0}-v{1}"', (env.X_BUILD_MONIKER == 'stable' || env.X_BUILD_MONIKER == 'preview') && env.X_BUILD_MONIKER || format('({0}) {1}', env.X_BUILD_MONIKER, github.ref_name), env.X_VERSION) }} | |
X_GH_RELEASE_TYPE: ${{ env.X_BUILD_MONIKER == 'stable' && '--latest' || '--prerelease' }} | |
X_GH_RELEASE_NOTES: ${{ (env.X_BUILD_MONIKER == 'stable' || env.X_BUILD_MONIKER == 'preview') && format('--notes-file ".github/RELEASE_TEMPLATE/{0}.md"', env.X_BUILD_MONIKER) || format('--notes "This {0} build is from the {1} branch. **It is not an official release for public distribution.**"', env.X_BUILD_MONIKER, github.ref_name) }} | |
run: | | |
if gh release delete ${{ env.X_GH_RELEASE_TAG }} --cleanup-tag --yes; then | |
echo previous release '${{ env.X_GH_RELEASE_TAG }}' deleted | |
fi | |
gh release create ${{ env.X_GH_RELEASE_TAG }} --target ${{ github.sha }} ${{ env.X_GH_RELEASE_TITLE }} ${{ env.X_GH_RELEASE_TYPE }} ${{ env.X_GH_RELEASE_NOTES }} wbe-*.zip | |
echo release '${{ env.X_GH_RELEASE_TAG }}' created | |
if gh release upload ${{ env.X_GH_RELEASE_TAG }} wbe-*.ipa --clobber; then | |
echo uploaded iOS assets | |
fi | |
if gh release upload ${{ env.X_GH_RELEASE_TAG }} wbe-*.pkg --clobber; then | |
echo uploaded macOS assets | |
fi |