Skip to content

Build

Build #420

Workflow file for this run

name: Build
on:
workflow_dispatch:
inputs:
build-variant:
description: "Build Variant"
required: true
default: "Beta"
type: choice
options:
- Beta
- Production
build-mode:
description: "Build Mode"
required: true
default: "Device"
type: choice
options:
- Device
- Simulator
build-version:
description: "Version Name Override - e.g. '2024.8.1'"
type: string
build-number:
description: "Version Number Override - e.g. '1021'"
type: string
xcode-version:
description: "Xcode Version Override - e.g. '15.2'"
type: string
compiler-flags:
description: "Compiler Flags - e.g. 'DEBUG_MENU FEATURE2'"
type: string
base_version_number:
description: "Base Version Number - Will be added to the calculated version number"
type: number
default: 1500
patch_version:
description: "Patch Version Override - e.g. '999'"
type: string
distribute:
description: "Distribute to TestFlight"
type: boolean
default: true
workflow_call:
inputs:
build-variant:
description: "Build Variant"
type: string
build-mode:
description: "Build Mode"
type: string
build-version:
description: "Version Name Override - e.g. '2024.8.1'"
type: string
build-number:
description: "Version Number Override - e.g. '1021'"
type: string
xcode-version:
description: "Xcode Version Override - e.g. '15.2'"
type: string
compiler-flags:
description: "Compiler Flags - e.g. 'DEBUG_MENU FEATURE2'"
type: string
base_version_number:
description: "Base Version Number - Will be added to the calculated version number"
type: number
default: 1500
patch_version:
description: "Patch Version Override - e.g. '999'"
type: string
distribute:
description: "Distribute to TestFlight"
type: boolean
upload_version_info:
description: "Upload version-info file - When false, caller may be handling it already"
type: boolean
env:
BUILD_VARIANT: ${{ inputs.build-variant || 'Beta' }}
BUILD_MODE: ${{ inputs.build-mode || 'Device' }}
XCODE_VERSION: ${{ inputs.xcode-version }}
jobs:
build:
name: Build
runs-on: macos-15
env:
MINT_PATH: .mint/lib
MINT_LINK_PATH: .mint/bin
steps:
- name: Log inputs to job summary
run: |
echo "<details><summary>Build Workflow Inputs</summary>" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo '```json' >> $GITHUB_STEP_SUMMARY
echo '${{ toJson(inputs) }}' >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
echo "</details>" >> $GITHUB_STEP_SUMMARY
- name: Check out repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0
filter: tree:0
- name: Calculate version
if: ${{ inputs.build-number == '' || inputs.build-version == '' }}
uses: bitwarden/ios/.github/actions/dispatch-and-download@main
id: dispatch-version
with:
token: ${{ secrets.GITHUB_TOKEN }}
owner: bitwarden
repo: ios
ref: main
workflow: _version.yml
workflow_inputs: '{"base_version_number": "${{ inputs.base_version_number }}", "version_name": "${{ inputs.build-version }}", "version_number": "${{ inputs.build-number }}", "patch_version": "${{ inputs.patch_version }}"}'
- name: Read version info
id: version_info
run: |
# test if dispatch-version was skipped. In that case, creates the same .json file expected by the Upload artifact step
if [ ! -f version-info/version_info.json ]; then
echo "::warning::version-version.json not found, was the previous step skipped? Creating a new file"
json='{
"version_number": "${{ inputs.build-number }}",
"version_name": "${{ inputs.build-version }}"
}'
# file will be used by the upload step
mkdir version-info
echo "$json" > version-info/version_info.json
else
echo "::notice::version-version.json found!"
fi
content=$(cat version-info/version_info.json)
echo "version_name=$(echo $content | jq -r .version_name)" >> $GITHUB_OUTPUT
echo "version_number=$(echo $content | jq -r .version_number)" >> $GITHUB_OUTPUT
- name: Upload version info artifact
if: ${{ inputs.upload_version_info }}
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
with:
name: version-info
path: version-info/version_info.json
- name: Read default Xcode version
run: |
echo "DEFAULT_XCODE_VERSION=$(cat .xcode-version | tr -d '\n')" >> "$GITHUB_ENV"
- name: Set Xcode version
uses: maxim-lobanov/setup-xcode@60606e260d2fc5762a71e64e74b2174e8ea3c8bd # v1.6.0
with:
xcode-version: ${{ env.XCODE_VERSION || env.DEFAULT_XCODE_VERSION }}
- name: Cache Mint packages
id: mint-cache
uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2
with:
path: .mint
key: ${{ runner.os }}-mint-${{ hashFiles('**/Mintfile') }}
restore-keys: |
${{ runner.os }}-mint-
- name: Log in to Azure
if: env.BUILD_MODE != 'Simulator'
uses: Azure/login@cb79c773a3cfa27f31f25eb3f677781210c9ce3d # v1.6.1
with:
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
- name: Retrieve secrets
if: env.BUILD_MODE != 'Simulator'
uses: bitwarden/gh-actions/get-keyvault-secrets@main
with:
keyvault: "bitwarden-ci"
secrets: "appcenter-ios-token"
- name: Retrieve production provisioning profiles
if: env.BUILD_VARIANT == 'Production'
env:
ACCOUNT_NAME: bitwardenci
CONTAINER_NAME: profiles
run: |
mkdir -p $HOME/secrets
profiles=(
"dist_autofill.mobileprovision"
"dist_bitwarden.mobileprovision"
"dist_extension.mobileprovision"
"dist_share_extension.mobileprovision"
"dist_bitwarden_watch_app.mobileprovision"
"dist_bitwarden_watch_app_extension.mobileprovision"
"dist_bitwarden_watch_widget_extension.mobileprovision"
)
for FILE in "${profiles[@]}"
do
az storage blob download --account-name $ACCOUNT_NAME --container-name $CONTAINER_NAME --name $FILE \
--file $HOME/secrets/$FILE --output none
done
- name: Retrieve beta provisioning profiles
if: env.BUILD_VARIANT == 'Beta'
env:
ACCOUNT_NAME: bitwardenci
CONTAINER_NAME: profiles
run: |
mkdir -p $HOME/secrets
profiles=(
"dist_beta_autofill.mobileprovision"
"dist_beta_bitwarden.mobileprovision"
"dist_beta_extension.mobileprovision"
"dist_beta_share_extension.mobileprovision"
"dist_beta_bitwarden_watch_app.mobileprovision"
"dist_beta_bitwarden_watch_app_extension.mobileprovision"
"dist_beta_bitwarden_watch_widget_extension.mobileprovision"
)
for FILE in "${profiles[@]}"
do
az storage blob download --account-name $ACCOUNT_NAME --container-name $CONTAINER_NAME --name $FILE \
--file $HOME/secrets/$FILE --output none
done
- name: Retrieve production Google Services secret
if: env.BUILD_VARIANT == 'Production'
env:
ACCOUNT_NAME: bitwardenci
CONTAINER_NAME: mobile
SOURCE_FILE: GoogleService-Info.plist
TARGET_FILE: GoogleService-Info.plist
run: |
mkdir -p $HOME/secrets
az storage blob download --account-name $ACCOUNT_NAME --container-name $CONTAINER_NAME --name $SOURCE_FILE \
--file Bitwarden/Application/Support/$TARGET_FILE --output none
- name: Retrieve watch production Google Services secret
if: env.BUILD_VARIANT == 'Production'
env:
ACCOUNT_NAME: bitwardenci
CONTAINER_NAME: mobile
SOURCE_FILE: GoogleService-Info.plist
TARGET_FILE: GoogleService-Info.plist
run: |
mkdir -p $HOME/secrets
az storage blob download --account-name $ACCOUNT_NAME --container-name $CONTAINER_NAME --name $SOURCE_FILE \
--file BitwardenWatchApp/$TARGET_FILE --output none
plutil -replace BUNDLE_ID -string com.8bit.bitwarden.watchkitapp BitwardenWatchApp/$TARGET_FILE
- name: Retrieve beta Google Services secret
if: env.BUILD_VARIANT == 'Beta'
env:
ACCOUNT_NAME: bitwardenci
CONTAINER_NAME: mobile
SOURCE_FILE: GoogleService-Info-ios-pm-beta.plist
TARGET_FILE: GoogleService-Info.plist
run: |
mkdir -p $HOME/secrets
az storage blob download --account-name $ACCOUNT_NAME --container-name $CONTAINER_NAME --name $SOURCE_FILE \
--file Bitwarden/Application/Support/$TARGET_FILE --output none
- name: Retrieve watch beta Google Services secret
if: env.BUILD_VARIANT == 'Beta'
env:
ACCOUNT_NAME: bitwardenci
CONTAINER_NAME: mobile
SOURCE_FILE: GoogleService-Info-ios-pm-beta.plist
TARGET_FILE: GoogleService-Info.plist
run: |
mkdir -p $HOME/secrets
az storage blob download --account-name $ACCOUNT_NAME --container-name $CONTAINER_NAME --name $SOURCE_FILE \
--file BitwardenWatchApp/$TARGET_FILE --output none
plutil -replace BUNDLE_ID -string com.8bit.bitwarden.beta.watchkitapp BitwardenWatchApp/$TARGET_FILE
- name: Retrieve certificates
if: env.BUILD_MODE != 'Simulator'
run: |
mkdir -p $HOME/certificates
az keyvault secret show --id https://bitwarden-ci.vault.azure.net/certificates/ios-distribution |
jq -r .value | base64 -d > $HOME/certificates/ios-distribution.p12
- name: Download Fastlane credentials
if: env.BUILD_MODE != 'Simulator'
env:
ACCOUNT_NAME: bitwardenci
CONTAINER_NAME: mobile
FILE: appstoreconnect-fastlane.json
run: |
mkdir -p $HOME/secrets
az storage blob download --account-name $ACCOUNT_NAME --container-name $CONTAINER_NAME --name $FILE \
--file $HOME/secrets/$FILE --output none
- name: Configure Keychain Access
if: env.BUILD_MODE != 'Simulator'
env:
KEYCHAIN_PASSWORD: ${{ secrets.IOS_KEYCHAIN_PASSWORD }}
run: |
security create-keychain -p $KEYCHAIN_PASSWORD build.keychain
security default-keychain -s build.keychain
security unlock-keychain -p $KEYCHAIN_PASSWORD build.keychain
security set-keychain-settings -lut 1200 build.keychain
security import $HOME/certificates/ios-distribution.p12 -k build.keychain -P "" -T /usr/bin/codesign \
-T /usr/bin/security
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k $KEYCHAIN_PASSWORD build.keychain
- name: Configure provisioning profiles
if: env.BUILD_MODE != 'Simulator'
run: |
./Scripts/configure_provisioning_profiles.sh ${{ env.BUILD_VARIANT }}
- name: Update beta export compliance key
if: env.BUILD_VARIANT == 'Beta'
run: |
plutil -replace ITSEncryptionExportComplianceCode -string 3dd3e32f-efa6-4d99-b410-28aa28b1cb77 Bitwarden/Application/Support/Info.plist
- name: Update beta Fastlane Appfile
if: env.BUILD_VARIANT == 'Beta'
run: |
echo 'app_identifier "com.8bit.bitwarden.beta"' > fastlane/Appfile
- name: Update APNS entitlements
if: env.BUILD_MODE != 'Simulator'
run: |
plutil -replace aps-environment -string production Bitwarden/Application/Support/Bitwarden.entitlements
- name: Configure Ruby
uses: ruby/setup-ruby@a2bbe5b1b236842c1cb7dd11e8e3b51e0a616acc # v1.202.0
with:
bundler-cache: true
- name: Update homebrew
run: |
brew update
- name: Install Fastlane
if: env.BUILD_MODE != 'Simulator'
run: |
brew install fastlane
- name: Install Mint
run: |
brew install mint
- name: Install Mint packages
if: steps.mint-cache.outputs.cache-hit != 'true'
run: |
mint bootstrap
- name: Select variant
run: |
./Scripts/select_variant.sh ${{ env.BUILD_VARIANT }} "${{ inputs.compiler-flags }}"
- name: Update build version and number
run: |
yq -i '.settings.MARKETING_VERSION = "${{ steps.version_info.outputs.version_name }}"' 'project.yml'
yq -i '.settings.CURRENT_PROJECT_VERSION = "${{ steps.version_info.outputs.version_number }}"' 'project.yml'
- name: Update CI build info
run: |
./Scripts/update_app_ci_build_info.sh ${{ github.run_id }} ${{ github.run_number }} ${{ github.run_attempt }} "${{ inputs.compiler-flags }}"
- name: Build iOS app
run: |
./Scripts/build.sh ${{ env.BUILD_MODE }}
- name: Prepare artifacts for upload to GitHub
run: |
if [ "$BUILD_MODE" == "Simulator" ]; then
app_path="build/DerivedData/Build/Products/Debug-iphonesimulator/Bitwarden.app"
watchapp_path="build/DerivedData/Build/Products/Debug-watchsimulator/BitwardenWatchApp.app"
#dsym_path="build/DerivedData/Build/Products/Debug-iphonesimulator/Bitwarden.app/dSYMs/*.dSYM"
else
app_path="build/Bitwarden/Bitwarden.ipa"
dsym_path="build/Bitwarden.xcarchive/dSYMs/*.dSYM"
fi
mkdir -p export
mkdir -p export/dSYMs
zip -r "export/$(basename "$app_path").zip" "$app_path"
cp $dsym_path export/dSYMs
zip -r "export/dSYMs.zip" "export/dSYMs"
if [ "$BUILD_MODE" == "Simulator" ]; then
zip -r "export/$(basename "$watchapp_path").zip" "$watchapp_path"
else
# Copy uncompressed .ipa file.Required for App Store Connect upload step
cp $app_path export/
fi
- name: Upload artifacts to GitHub
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
with:
name: Bitwarden iOS ${{ steps.version_info.outputs.version_name }} (${{ steps.version_info.outputs.version_number }}) ${{ env.BUILD_VARIANT }} ${{ env.XCODE_VERSION || env.DEFAULT_XCODE_VERSION }}
path: export
if-no-files-found: error
- name: Set up private auth key
if: env.BUILD_MODE != 'Simulator'
run: |
mkdir ~/private_keys
cat << EOF > ~/private_keys/AuthKey_J46C83CB96.p8
${{ secrets.APP_STORE_CONNECT_AUTH_KEY }}
EOF
- name: Validate app with App Store Connect
if: env.BUILD_MODE != 'Simulator'
run: |
xcrun altool --validate-app \
--type ios \
--file "export/Bitwarden.ipa" \
--apiKey "J46C83CB96" \
--apiIssuer "${{ secrets.APP_STORE_CONNECT_TEAM_ISSUER }}"
- name: Upload app to TestFlight with Fastlane
if: ${{ inputs.distribute && env.BUILD_MODE == 'Device' }}
run: |
CHANGELOG="$(git show -s --format=%s)
$GITHUB_REPOSITORY/$GITHUB_REF_NAME @ $GITHUB_SHA
Xcode ${{ env.XCODE_VERSION }}
Compiler Flags: ${{ inputs.compiler-flags }}
$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID"
fastlane upload_build \
api_key_path:"$HOME/secrets/appstoreconnect-fastlane.json" \
changelog:"$CHANGELOG" \
ipa_path:"export/Bitwarden.ipa"