Skip to content

Build and release

Build and release #572

Workflow file for this run

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