-
-
Save JoeM-RP/fda1a3519c8cb11906288e1b3efc4e2f to your computer and use it in GitHub Desktop.
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
# This is an expo pipeline configuration | |
# https://docs.expo.dev/build/building-on-ci/ | |
# ----- | |
# You can specify a custom docker image from Docker Hub as your build environment. | |
# | |
# You can specify a cache to speed up build times | |
# https://support.atlassian.com/bitbucket-cloud/docs/cache-dependencies/ | |
# | |
# Local build reference: | |
# https://docs.expo.dev/build-reference/local-builds/ | |
# | |
# Limitations | |
# Some of the options available for cloud builds are not available locally. Limitations you should be aware of: | |
# You can only build for a specific platform (option all is disabled). | |
# Customizing versions of software is not supported, fields node, yarn, fastlane, cocoapods, ndk, image in eas.json are ignored. | |
# Caching is not supported. | |
# EAS Secrets are not supported (set them in your environment locally instead). | |
# You are responsible for making sure that the environment have all necessary tools installed: | |
# - Node.js/yarn/npm | |
# - fastlane (iOS only) | |
# - CocoaPods (iOS only) | |
# - Android SDK and NDK | |
image: node:16.16.0 | |
clone: | |
depth: full # SonarCloud scanner needs the full history to assign issues properly | |
definitions: | |
caches: | |
node: ./node_modules | |
yarn: /usr/local/share/.cache/yarn | |
sonar: ~/.sonar/cache | |
# https://support.atlassian.com/bitbucket-cloud/docs/yaml-anchors/ | |
steps: | |
- parallel: | |
- step: &lint | |
name: Lint app | |
caches: | |
- node | |
- yarn | |
script: | |
- mv .npmrc_config .npmrc | |
- yarn | |
- yarn lint --quiet | |
- step: &test | |
name: Test and analyze on Jest | |
caches: | |
- node | |
- yarn | |
script: | |
- mv .npmrc_config .npmrc | |
- mv .test.env .env | |
- yarn | |
- yarn test:ci | |
artifacts: | |
- coverage/** | |
- step: &doctor | |
name: Expo doctor | |
script: | |
- yarn | |
- npx expo-doctor | |
- step: &test-sonarcloud | |
name: Test and analyze on SonarCloud | |
caches: | |
- node | |
- sonar | |
script: | |
- pipe: sonarsource/sonarcloud-scan:2.0.0 | |
variables: | |
SONAR_TOKEN: $SONAR_TOKEN | |
EXTRA_ARGS: '-Dsonar.javascript.lcov.reportPaths="$BITBUCKET_CLONE_DIR/coverage/lcov.info"' | |
- pipe: sonarsource/sonarcloud-quality-gate:0.1.6 | |
variables: | |
SONAR_TOKEN: $SONAR_TOKEN | |
- step: &build | |
name: Build app | |
caches: | |
- node | |
- yarn | |
script: | |
- mv .npmrc_config .npmrc | |
- yarn | |
- npx eas-cli build --platform all --non-interactive --profile preview | |
- step: &distribute | |
name: Validate & Distribute App | |
deployment: qa | |
caches: | |
- node | |
- yarn | |
script: | |
- apt-get update | |
- apt-get -y install jq | |
- mv .npmrc_config .npmrc | |
- yarn | |
# Set the build profile based on deployment config, which allows us to target the right buildProfile from Expo | |
- PROFILE=preview | |
- if [ ! $BITBUCKET_DEPLOYMENT_ENVIRONMENT = uat ]; then PROFILE=production; fi | |
- echo "Validating build output and distributing to $PROFILE channel" | |
# iOS | |
- IOS=$(npx eas-cli build:list --json --limit=1 --platform=ios --buildProfile $PROFILE --non-interactive --status finished) | |
- IBU=$(echo $IOS | jq -r '.[0].artifacts.buildUrl') | |
- IBV=$(echo $IOS | jq -r '.[0].appBuildVersion') | |
- IAV=$(echo $IOS | jq -r '.[0].appVersion') | |
- IRV=$(echo $IOS | jq -r '.[0].runtimeVersion') | |
# Check if the app version and runtime version match | |
- if [ ! $IAV = $IRV ]; then echo "iOS runtime vesion $IRV does not match app version $IAV. This probably means something has changed in-code that the pipeline does not currenlty handle"; exit 1; fi | |
# Android | |
- AND=$(npx eas-cli build:list --json --limit=1 --platform=android --buildProfile $PROFILE --non-interactive --status finished) | |
- ABU=$(echo $AND | jq -r '.[0].artifacts.buildUrl') | |
- ABV=$(echo $AND | jq -r '.[0].appBuildVersion') | |
- AAV=$(echo $AND | jq -r '.[0].appVersion') | |
- ARV=$(echo $AND | jq -r '.[0].runtimeVersion') | |
# Check if the app version and runtime version match | |
- if [ ! $AAV = $ARV ]; then echo "Android runtime vesion $ARV does not match app version $AAV. This probably means something has changed in-code that the pipeline does not currenlty handle"; exit 1; fi | |
# Check that iOS and Android build versions match | |
- if [ ! $IBV = $ABV ]; then echo "iOS Build Version $IBV does not match Android Build Version $ABV. While this has no technical implications, we generally try to keep these numbers unified for tracking purposes. Use `npx eas-cli build:version:set` to sync build versions, then re-run the pipeline"; exit 1; fi | |
- echo "iOS Build Version $IBV matches Android Build Version $ABV. Pass" | |
# Check that iOS and Android app versions match | |
- if [ ! $IAV = $AAV ]; then echo "iOS App Version $IAV does not match Android App Version $AAV. Use \`npx eas-cli build:version:set\` to sync app versions, then re-run the pipeline"; exit 1; fi | |
- echo "iOS App Version $IAV matches Android App Version $AAV. Pass" | |
- echo "Disribute app version $IAV build $IBV". | |
# iOS AppLive Release | |
- curl -u "$BROWSERSTACK_TOKEN" -X POST "https://api-cloud.browserstack.com/app-live/upload" -F "data={\"url\":\"$IBU\"}" | |
- echo "ios download url $IBU" | |
# Android AppLive Release | |
- curl -u "$BROWSERSTACK_TOKEN" -X POST "https://api-cloud.browserstack.com/app-live/upload" -F "data={\"url\":\"$ABU\"}" | |
- echo "android download url $ABU" | |
- step: &prepare | |
name: Prepare app for release | |
caches: | |
- node | |
- yarn | |
script: | |
- FNF=" not found. Ensure the file exists or is no longer needed and update this pipeline accordingly" | |
- mv .npmrc_config .npmrc | |
- yarn | |
- apt-get install -y sed # needed? | |
# When the build is triggered from a branch, the target name is the branch name. If triggered from a tag, we must | |
# use the tag name as the target name. If neither are present, we use the default target name. | |
# | |
# See: https://support.atlassian.com/bitbucket-cloud/docs/pipeline-start-conditions/#Tags | |
- TGT=${BITBUCKET_BRANCH:-$BITBUCKET_TAG} | |
- echo "Target name is $TGT" | |
# Set app version i.e 2024.0.1 based on the branch name | |
- REV=$(echo "$TGT" | grep -Eo '[0-9]{4}.[0-9]{1,2}.[0-9]{1,2}') | |
- echo "Set App Version to $REV" | |
# Confirm a file exists then replace a string in the file. If the file does not exist, exit with an error. | |
# The edits occurs in-place and are not committed back to the repository. | |
- if [ ! -e "app.json" ]; then echo "app.json $FNF"; exit 1; fi | |
- test -e app.json && sed -i "s/0000.0.0/$REV/g" app.json | |
- if [ ! -f ios/MyExpoApp/Info.plist ]; then echo "ios/MyExpoApp/Info.plist $FNF"; exit 1; fi | |
- sed -i "s/0000.0.0/$REV/g" ios/MyExpoApp/Info.plist | |
- if [ ! -f ios/MyExpoApp/Supporting/Expo.plist ]; then echo "ios/MyExpoApp/Supporting/Expo.plist $FNF"; exit 1; fi | |
- sed -i "s/0000.0.0/$REV/g" ios/MyExpoApp/Supporting/Expo.plist | |
- if [ ! -f android/app/build.gradle ]; then echo "android/app/build.gradle $FNF"; exit 1; fi | |
- sed -i "s/0000.0.0/$REV/g" android/app/build.gradle | |
- if [ ! -f android/app/src/main/res/values/strings.xml ]; then echo "android/app/src/main/res/values/strings.xml $FNF"; exit 1; fi | |
- sed -i "s/0000.0.0/$REV/g" android/app/src/main/res/values/strings.xml | |
# Remove dev bundle id (Deprecated) | |
# - if [ ! -f android/app/build.gradle ]; then echo "android/app/build.gradle $FNF"; exit 1; fi | |
# - sed -i ''s/applicationIdSuffix ".dev" \/\/ Remove this for store releases.//g" android/app/build.gradle | |
# Set Adobe App ID | |
- echo "Set Adobe App ID to $ADOBE_APP_ID_PROD" | |
# N.B - Adobe App ID contains a / so we supply sed with an alternate delimiter | |
- if [ ! -f android/app/src/main/java/com/MyExpoApp/mobile/MainApplication.java ]; then echo "android/app/src/main/java/com/MyExpoApp/mobile/MainApplication.java $FNF"; exit 1; fi | |
- sed -i "s~ADOBE_APP_ID_PLACEHOLDER~$ADOBE_APP_ID_PROD~g" android/app/src/main/java/com/MyExpoApp/mobile/MainApplication.java | |
- if [ ! -f ios/AdobeBridge.m ]; then echo "ios/AdobeBridge.m $FNF"; exit 1; fi | |
- sed -i "s~ADOBE_APP_ID_PLACEHOLDER~$ADOBE_APP_ID_PROD~g" ios/AdobeBridge.m | |
# Build the app | |
- echo "Building the app for all platforms:" | |
- npx eas-cli build --platform all --non-interactive | |
- step: &pingback | |
name: Pingback | |
script: | |
- apt-get update | |
- apt-get -y install jq | |
- mv .npmrc_config .npmrc | |
- yarn | |
# Get the id for current PR | |
- PRID=$BITBUCKET_PR_ID | |
- if [ ! $PRID ]; then echo "No PR ID found. Exiting"; exit 0; fi # exit without error | |
# Set the endpoint URL | |
# https://developer.atlassian.com/cloud/bitbucket/rest/api-group-pullrequests/ | |
- EPT="https://api.bitbucket.org/2.0/repositories/$BITBUCKET_WORKSPACE/$BITBUCKET_REPO_SLUG/pullrequests/$PRID/" | |
# Set the build profile based on deployment config, which allows us to target the right buildProfile from Expo | |
- PROFILE=preview | |
- if [ ! $BITBUCKET_DEPLOYMENT_ENVIRONMENT = uat ]; then PROFILE=production; fi | |
- echo "Searching for latest builds on $PROFILE channel" | |
# Fetch the latest completed build versions for iOS and Android. N.B - it is possible that a new, unrelated build may have | |
# finished since this pipeline was triggered, but before the pingback step & thus return the wrong download URL to append. | |
# This is a limitation of the current implementation | |
# iOS | |
- IOS=$(npx eas-cli build:list --json --limit=1 --platform=ios --buildProfile $PROFILE --non-interactive --status finished) | |
- IBU=$(echo $IOS | jq -r '.[0].artifacts.buildUrl') | |
- IBV=$(echo $IOS | jq -r '.[0].appBuildVersion') | |
# Android | |
- AND=$(npx eas-cli build:list --json --limit=1 --platform=android --buildProfile $PROFILE --non-interactive --status finished) | |
- ABU=$(echo $AND | jq -r '.[0].artifacts.buildUrl') | |
- ABV=$(echo $AND | jq -r '.[0].appBuildVersion') | |
# Append a warning to the PR if the build versions do not match. Technically, this should never happen since we check during the "distribute" phase | |
# but I've added this check here also in the event the "distribute" phase is skipped for some reason or if the build:list somehow returns different instances | |
- BV_WARN="" | |
- if [ ! $IBV = $ABV ]; then BV_WARN="> **WARN:** iOS Build Version $IBV does not match Android Build Version $ABV. While this has no technical implications, we generally try to keep these numbers unified for tracking purposes. Use \`npx eas-cli build:version:set\` to sync build versions, then re-run the pipeline."; fi | |
# Get PR content. Here, jq -r was also spitting out garbage for some reason, so we need to do some fancy string manipulation later | |
- OG_CONTENT=$(curl --location $EPT --header "Authorization:Bearer $PINGBACK_TOKEN" --header "Accept:application/json" --header "Content-Type:application/json" | jq '.summary.raw') | |
- echo $OG_CONTENT | |
# Fancy string manipulation. Remove pingback content, if it exists, from original PR description and any extra quotes which will befuddle cURL later | |
- DEV_CONTENT=$(echo $OG_CONTENT | sed 's/§.*//') | |
- DEV_CONTENT=$(echo $DEV_CONTENT | tr -d '"') | |
# Default content to add to PR description | |
- APPEND_CONTENT="§ **my-expo-app-mobile-pipeline:** ($(date '+%Y-%m-%d %r %Z')) \n\n >🍏 ($IBV): [$IBU]($IBU) \n\n >🤖 ($ABV): [$ABU]($ABU)" | |
# Concat the PR description, default pingback content, and optional warning (if applicable) | |
- NEW_CONTENT="$DEV_CONTENT\n\n\n\n$APPEND_CONTENT\n\n$BV_WARN" | |
- echo "$NEW_CONTENT" # check this for extra quotes here if the PUT fails later | |
# PUT request to update the PR description with the new content | |
- curl --request PUT --url $EPT --header "Authorization:Bearer $PINGBACK_TOKEN" --header "Accept:application/json" --header "Content-Type:application/json" --data "{\"description\":\"$NEW_CONTENT\"}" | |
pipelines: | |
branches: | |
'{main}': | |
- parallel: | |
- step: *lint | |
- step: *test | |
- step: *doctor | |
- step: *test-sonarcloud | |
- step: *build | |
pull-requests: | |
'*chore/*': | |
- parallel: | |
- step: *lint | |
- step: *test | |
- step: *doctor | |
- step: *test-sonarcloud | |
'release/*': | |
- parallel: | |
- step: *lint | |
- step: *test | |
- step: *doctor | |
- step: *test-sonarcloud | |
- step: *prepare | |
- step: | |
<<: *distribute | |
deployment: uat # for tagging/tracking purposes. These builds cannot be installed via BrowserStack, etc | |
- step: *pingback | |
- step: | |
name: Submit to App Stores | |
trigger: manual | |
deployment: production | |
caches: | |
- node | |
- yarn | |
script: | |
- mv .npmrc_config .npmrc | |
- yarn | |
- npx eas-cli submit --platform all --latest --non-interactive | |
'**': # triggers if no other specific pipeline was triggered | |
- parallel: | |
- step: *lint | |
- step: *test | |
- step: *doctor | |
- step: *test-sonarcloud | |
- step: *build | |
- step: *distribute | |
- step: *pingback | |
custom: # Pipelines that can only be triggered manually | |
updates-preview: | |
- parallel: | |
- step: *lint | |
- step: *test | |
- step: *doctor | |
- step: *test-sonarcloud | |
- step: | |
name: Update Patch - Preview | |
deployment: qa | |
caches: | |
- node | |
- yarn | |
script: | |
- mv .npmrc_config .npmrc | |
- yarn | |
- echo "Decrypt .env file from Bitbucket Secrets" | |
- (umask 077 ; echo $ENV | base64 --decode > $BITBUCKET_CLONE_DIR/.env) | |
- if [ ! -e "$BITBUCKET_CLONE_DIR/.env" ]; then echo "Missing env file"; exit 1; fi | |
- echo "Creating an update patch for channel PREVIEW" | |
- COMMIT_MESSAGE=`git log --format=%B -n 1 $BITBUCKET_COMMIT` | |
- echo $COMMIT_MESSAGE | |
- npx eas-cli update --channel preview --message "$COMMIT_MESSAGE" --non-interactive | |
updates-prod: | |
- parallel: | |
- step: *lint | |
- step: *test | |
- step: *doctor | |
- step: *test-sonarcloud | |
- step: | |
name: Update Patch - Prod | |
deployment: production | |
caches: | |
- node | |
- yarn | |
script: | |
- mv .npmrc_config .npmrc | |
- yarn | |
- echo "Decrypt .env file from Bitbucket Secrets" | |
- (umask 077 ; echo $ENV | base64 --decode > $BITBUCKET_CLONE_DIR/.env) | |
- if [ ! -e "$BITBUCKET_CLONE_DIR/.env" ]; then echo "Missing env file"; exit 1; fi | |
- echo "Creating an update patch for channel PRODUCTION" | |
- COMMIT_MESSAGE=`git log --format=%B -n 1 $BITBUCKET_COMMIT` | |
- echo $COMMIT_MESSAGE | |
- npx eas-cli update --channel production --message "$COMMIT_MESSAGE" --non-interactive |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment