#!/bin/bash # script BuildLoopFollow.sh # ----------------------------------------------------------------------------- # This file is GENERATED. DO NOT EDIT directly. # If you want to modify this file, edit the corresponding file in the src/ # directory and then run the build script to regenerate this output file. # ----------------------------------------------------------------------------- ############################################################ # Required parameters for any build script that uses # inline build_functions ############################################################ BUILD_DIR=~/Downloads/BuildLoopFollow OVERRIDE_FILE=LoopFollowConfigOverride.xcconfig DEV_TEAM_SETTING_NAME="LF_DEVELOPMENT_TEAM" CLONE_SUB_MODULES="0" TESTFLIGHT_SCHEME="LoopFollow" # *** Start of inlined file: inline_functions/build_functions.sh *** # *** Start of inlined file: inline_functions/common.sh *** STARTING_DIR="${PWD}" ############################################################ # define some font styles and colors ############################################################ # remove special font NC='\033[0m' # add special font #INFO_FONT='\033[1;36m' INFO_FONT='\033[1m' SUCCESS_FONT='\033[1;32m' ERROR_FONT='\033[1;31m' function section_divider() { echo -e "" echo -e "--------------------------------" echo -e "" } function section_separator() { # Clears the screen without clearing the scrollback buffer, suppressing any error messages echo -e "\033[2J\033[H" 2>/dev/null section_divider } function return_when_ready() { echo -e "${INFO_FONT}Return when ready to continue${NC}" read -p "" dummy } # Skip if this script is called from another script, then this has already been displayed if [ "$0" != "_" ]; then # Inform the user about env variables set # Variables definition variables=( "SCRIPT_BRANCH: Indicates the lnl-scripts branch in use." "LOCAL_SCRIPT: Set to 1 to run scripts from the local directory." "FRESH_CLONE: Lets you use an existing clone (saves time)." "CLONE_STATUS: Can be set to 0 for success (default) or 1 for error." "SKIP_OPEN_SOURCE_WARNING: If set, skips the open source warning for build scripts." "CUSTOM_URL: Overrides the repo url." "CUSTOM_BRANCH: Overrides the branch used for git clone." "CUSTOM_MACOS_VER: Overrides the detected macOS version." "CUSTOM_XCODE_VER: Overrides the detected Xcode version." "DELETE_SELECTED_FOLDERS: Echoes folder names but does not delete them" "PATCH_BRANCH: Indicates the source branch for patches." "PATCH_REPO: Specifies the URL of the patch source repository." "LOCAL_PATCH_FOLDER: Defines a local directory for sourcing patches." "CUSTOMIZATION_DEBUG: Determines the verbosity of the customization debug output." ) # Flag to check if any variable is set any_variable_set=false # Iterate over each variable for var in "${variables[@]}"; do # Split the variable name and description IFS=":" read -r name description <<<"$var" # Check if the variable is set if [ -n "${!name}" ]; then # If this is the first variable set, print the initial message if ! $any_variable_set; then section_separator echo -e "For your information, you are running this script in customized mode" echo -e "You might be using a branch other than main, and using SCRIPT_BRANCH" echo -e "Developers might have additional environment variables set:" any_variable_set=true fi # Print the variable name, value, and description echo " - $name: ${!name}" echo " $description" fi done if $any_variable_set; then echo echo "To clear the values, close this terminal and start a new one." echo "Sleeping for 2 sec then continuing" sleep 2 fi fi function choose_option() { echo -e "Type a number from the list below and return to proceed." section_divider } function invalid_entry() { echo -e "\n${ERROR_FONT}Invalid option${NC}\n" } function do_continue() { : } function menu_select() { choose_option local options=("${@:1:$#/2}") local actions=("${@:$(($# + 1))/2+1}") while true; do select opt in "${options[@]}"; do for i in $(seq 0 $((${#options[@]} - 1))); do if [ "$opt" = "${options[$i]}" ]; then eval "${actions[$i]}" return fi done invalid_entry break done done } function exit_or_return_menu() { if [ "$0" != "_" ]; then # Called directly echo "Exit Script" else # Called from BuildSelectScript echo "Return to Menu" fi } function exit_script() { if [ "$0" != "_" ]; then # Called directly exit_message else # Called from BuildSelectScript exit 0 fi } function exit_message() { section_divider echo -e "${INFO_FONT}Exit from Script${NC}\n" echo -e " You may close the terminal" echo -e "or" echo -e " You can press the up arrow ⬆️ on the keyboard" echo -e " and return to repeat script from beginning" section_divider exit 0 } function erase_previous_line { if [ -n "$TERM" ]; then (tput cuu1 && tput el) 2>/dev/null || true fi } # *** End of inlined file: inline_functions/common.sh *** ############################################################ # Common functions used by multiple build scripts # - Explanation of variables, Default values ############################################################ # Variables set by BuildXXX script that calls this inline script # # Required: BUILD_DIR # it is where the download folder will be created # For example: BUILD_DIR=~/Downloads/Build{app_name} # # Required: OVERRIDE_FILE # name of the automatic signing file # For example: OVERRIDE_FILE=LoopConfigOverride.xcconfig # Required: DEV_TEAM_SETTING_NAME # keyword used in the automatic signing file # e.g., ${DEV_TEAM_SETTING_NAME} = Apple Developer TeamID # Loop and Loop associated apps use: "LOOP_DEVELOPMENT_TEAM" # Trio uses: "DEVELOPER_TEAM" # Default: some projects create or use the override file in the BUILD_DIR # Some use a file in the downloaded clone itself # in that case, set USE_OVERRIDE_IN_REPO to 1 in the src/Build script : ${USE_OVERRIDE_IN_REPO:="0"} # Default: some projects use submodules (and need --recurse-submodule) # Some, like LoopFollow, do not use submodules # in that case, set CLONE_SUB_MODULES to 0 in the src/Build script : ${CLONE_SUB_MODULES:="1"} # Accept build_warning before creating folders # *** Start of inlined file: inline_functions/build_warning.sh *** ############################################################ # warning used by all scripts that build an app ############################################################ function open_source_warning() { # Skip open source warning if opted out using env variable or this script is run from another script if [ "${SKIP_OPEN_SOURCE_WARNING}" = "1" ] || [ "$0" = "_" ]; then return; fi local documentation_link="${1:-}" section_separator echo -e "${INFO_FONT}*** IMPORTANT ***${NC}\n" echo -e "This project is:" echo -e "${INFO_FONT} Open Source software" echo -e " Not \"approved\" for therapy${NC}" echo -e "" echo -e " You take full responsibility when you build" echo -e " or run an open source app, and" echo -e " ${INFO_FONT}you do so at your own risk.${NC}" echo -e "" echo -e "To increase (decrease) font size" echo -e " Hold down the CMD key and hit + (-)" echo -e "\n${INFO_FONT}By typing 1 and ENTER, you indicate you understand" echo -e "\n--------------------------------\n${NC}" options=("Agree" "Cancel") select opt in "${options[@]}"; do case $opt in "Agree") break ;; "Cancel") echo -e "\n${INFO_FONT}User did not agree to terms of use.${NC}\n\n" exit_script ;; *) echo -e "\n${INFO_FONT}User did not agree to terms of use.${NC}\n\n" invalid_entry exit_script ;; esac done # Warning has been issued SKIP_OPEN_SOURCE_WARNING=1 echo -e "${NC}\n\n\n\n" } # *** End of inlined file: inline_functions/build_warning.sh *** # Messages prior to opening xcode # *** Start of inlined file: inline_functions/before_final_return_message.sh *** function before_final_return_message() { echo "" echo -e "${INFO_FONT}BEFORE you hit return:${NC}" echo " *** Unlock your phone and plug it into your computer" echo " Trust computer if asked" echo "" echo -e "${INFO_FONT}AFTER you hit return, Xcode will open automatically${NC}" echo " For new phone or new watch (never used with Xcode)," echo " review Developer Mode Information:" echo -e " https://loopkit.github.io/loopdocs/build/build-app/#prepare-your-phone-and-watch" echo "" echo " For phones that have Developer Mode enabled continue with these steps" echo " Upper middle of Xcode:" echo " Confirm your phone or simulator choice is selected" echo " Upper right of Xcode:" echo " Wait for packages to finish being copied or downloaded" echo " When you see indexing, you can start the build" echo " Click on Play button to build and run on the selected device" } function after_final_return_message() { section_divider echo "If you need to find this download in a terminal, copy and paste the next line:" echo "" echo "cd ${LOCAL_DIR}/${REPO_NAME}" } # *** End of inlined file: inline_functions/before_final_return_message.sh *** # clean provisioning profiles saved on disk # *** Start of inlined file: inline_functions/clean_profiles.sh *** ############################################################ # clean_profiles function # Action: deletes saved mobileprovisions from Mac # Information: # If Xcode is open, *.mobileprovisions are deleted and new ones generated # The path changed between Xcode 15 and Xcode 16, delete both folders ############################################################ clean_profiles() { xcode15_path=${HOME}/Library/MobileDevice/Provisioning\ Profiles xcode16_path=${HOME}/Library/Developer/Xcode/UserData/Provisioning\ Profiles echo -e "\n✅ Cleaning Profiles" echo -e " This ensures the next app you build with Xcode will last a year." if [[ -d "$xcode15_path" ]]; then rm -rf "$xcode15_path" fi if [[ -d "$xcode16_path" ]]; then rm -rf "$xcode16_path" fi echo -e "✅ Profiles are cleaned." } # *** End of inlined file: inline_functions/clean_profiles.sh *** ############################################################ # Common functions used by multiple build scripts # - Start of build_functions.sh common code ############################################################ SCRIPT_DIR="${BUILD_DIR}/Scripts" if [ ! -d "${BUILD_DIR}" ]; then mkdir "${BUILD_DIR}" fi ############################################################ # set up nominal values # these can be later overwritten by flags # for convenience when testing (or for advanced users) ############################################################ # FRESH_CLONE # Default value is 1, which means: # Download fresh clone every time script is run : ${FRESH_CLONE:="1"} # CLONE_STATUS used to test error messages # Default value is 0, which means no errors with clone : ${CLONE_STATUS:="0"} # Prepare date-time stamp for folder DOWNLOAD_DATE=$(date +'%y%m%d-%H%M') # This enables the selection of a custom branch via enviroment variable # It can also be passed in as argument $1 # If passed in, it overwrites the environment variable # When CUSTOM_BRANCH is set, the menu which asks which branch is skipped CUSTOM_BRANCH=${1:-$CUSTOM_BRANCH} ############################################################ # Define the rest of the functions (usage defined above): ############################################################ # *** Start of inlined file: inline_functions/building_verify_version.sh *** #This should be the latest iOS version #This is the highest version we expect users to have on their iPhones LATEST_IOS_VER="27.x" #This should be the lowest xcode version required to build to LATEST_IOS_VER LOWEST_XCODE_VER="26.4" #This should be the latest known xcode version #LOWEST_XCODE_VER and LATEST_XCODE_VER will probably be equal but we should have suport for a span of these LATEST_XCODE_VER="27.x" #This is the lowest version of macOS required to run LOWEST_XCODE_VER LOWEST_MACOS_VER="26.2" # The compare_versions function takes two version strings as input arguments, # sorts them in ascending order using the sort command with the -V flag (version sorting), # and returns the first version (i.e., the lowest one) using head -n1. # # Example: # compare_versions "1.2.3" "1.1.0" will return "1.1.0" function compare_versions() { printf '%s\n%s\n' "$1" "$2" | sort -V | head -n1 } # xcodebuild -checkFirstLaunchStatus exits nonzero when the Xcode license # has not been accepted or the Xcode components are not installed. # Either condition causes git and xcodebuild to fail, so it is verified # before the version check and the clone. function check_xcode_license() { if xcodebuild -checkFirstLaunchStatus >/dev/null 2>&1; then return fi echo "" echo -e "❌ ${ERROR_FONT}The Xcode license has not been accepted for this Xcode version," echo -e " or Xcode has not finished installing its components.${NC}" echo -e " This happens after Xcode is installed or updated." echo -e " The script cannot download or build until this is done." echo -e "Please choose an option below to proceed:\n" options=("Accept Xcode license and install components" "Skip" "$(exit_or_return_menu)") select opt in "${options[@]}" do case $opt in "Accept Xcode license and install components") echo -e "You might be prompted for your password." echo -e " Use the password for logging into your Mac." sudo xcodebuild -runFirstLaunch if xcodebuild -checkFirstLaunchStatus >/dev/null 2>&1; then echo -e "✅ ${SUCCESS_FONT}Xcode license accepted and components installed.${NC}" return_when_ready break else echo -e "❌ ${ERROR_FONT}Xcode is still not ready.${NC}" echo -e " Open Xcode once, accept the license and let it finish installing," echo -e " or run this command in a terminal and then run this script again:" echo -e " sudo xcodebuild -runFirstLaunch" exit_message fi ;; "Skip") break ;; "$(exit_or_return_menu)") exit_script ;; *) # Invalid option invalid_entry ;; esac done } function check_versions() { section_divider echo "Verifying Xcode and macOS versions..." if ! command -v xcodebuild >/dev/null; then echo " Xcode not found. Please install Xcode and try again." exit_or_return_menu fi check_xcode_license if [ -n "$CUSTOM_XCODE_VER" ]; then XCODE_VER="$CUSTOM_XCODE_VER" else XCODE_VER=$(xcodebuild -version | awk '/Xcode/{print $NF}') fi if [ -n "$CUSTOM_MACOS_VER" ]; then MACOS_VER="$CUSTOM_MACOS_VER" else MACOS_VER=$(sw_vers -productVersion) fi echo " Xcode found: Version $XCODE_VER" # A trailing .x in LATEST_XCODE_VER matches any version with that prefix, # so only the leading components of XCODE_VER are compared against it latest_xcode_base="${LATEST_XCODE_VER%.x}" xcode_ver_base="$XCODE_VER" if [ "$latest_xcode_base" != "$LATEST_XCODE_VER" ]; then xcode_ver_base=$(echo "$XCODE_VER" | cut -d. -f1-"$(echo "$latest_xcode_base" | awk -F. '{print NF}')") fi # Check if Xcode version is greater than the latest known version if [ "$(compare_versions "$xcode_ver_base" "$latest_xcode_base")" = "$latest_xcode_base" ] && [ "$xcode_ver_base" != "$latest_xcode_base" ]; then echo "" echo "You have a newer Xcode version ($XCODE_VER) than" echo " the latest released version known by this script ($LATEST_XCODE_VER)." echo "You can probably continue; but if you have problems, refer to" echo " https://developer.apple.com/support/xcode/" options=("Continue" "$(exit_or_return_menu)") actions=("return" "exit_script") menu_select "${options[@]}" "${actions[@]}" # Check if Xcode version is less than the lowest required version elif [ "$(compare_versions "$XCODE_VER" "$LOWEST_XCODE_VER")" = "$XCODE_VER" ] && [ "$XCODE_VER" != "$LOWEST_XCODE_VER" ]; then if [ "$(compare_versions "$MACOS_VER" "$LOWEST_MACOS_VER")" != "$LOWEST_MACOS_VER" ]; then echo "" echo "Your macOS version ($MACOS_VER) is lower than $LOWEST_MACOS_VER" echo " required to build for iOS $LATEST_IOS_VER." echo "Please update macOS to version $LOWEST_MACOS_VER or later." echo "" echo "If you can't update, follow the GitHub build option here:" echo " https://loopkit.github.io/loopdocs/gh-actions/gh-overview/" fi echo "" echo "You need to upgrade Xcode to version $LOWEST_XCODE_VER or later to build for iOS $LATEST_IOS_VER." echo "If your iOS is at a lower version, refer to the compatibility table in LoopDocs" echo " https://loopkit.github.io/loopdocs/build/xcode-version/#compatible-versions" options=("Continue with lower iOS version" "$(exit_or_return_menu)") actions=("return" "exit_script") menu_select "${options[@]}" "${actions[@]}" else echo "Your Xcode version can build up to iOS $LATEST_IOS_VER." fi } # *** End of inlined file: inline_functions/building_verify_version.sh *** # *** Start of inlined file: inline_functions/building_config_override.sh *** function check_config_override_existence_offer_to_configure() { section_separator # Automatic signing functionality: # 1) Use existing Override file # 2) Copy team from latest provisioning profile # 3) Enter team manually with option to skip # Options for USE_OVERRIDE_IN_REPO # 0 means copy file in repo up 2 levels and use that # 1 create the file in the repo and add development team # 2 create the file in the repo with extra line(s) and the team if [[ $USE_OVERRIDE_IN_REPO -ge 1 ]]; then OVERRIDE_FULLPATH="${LOCAL_DIR}/$REPO_NAME/${OVERRIDE_FILE}" else OVERRIDE_FULLPATH="${BUILD_DIR}/${OVERRIDE_FILE}" fi if [ -f ${OVERRIDE_FULLPATH} ] && \ grep -q "^$DEV_TEAM_SETTING_NAME" ${OVERRIDE_FULLPATH}; then # how_to_find_your_id report_persistent_config_override else PROFILES_DIR="$HOME/Library/MobileDevice/Provisioning Profiles" if [ -d "${PROFILES_DIR}" ]; then latest_file=$(find "${PROFILES_DIR}" -type f -name "*.mobileprovision" -print0 | xargs -0 ls -t | head -n1) if [ -n "$latest_file" ]; then # Decode the .mobileprovision file using the security command decoded_xml=$(security cms -D -i "$latest_file") # Extract the Team ID from the XML DEVELOPMENT_TEAM=$(echo "$decoded_xml" | awk -F'[<>]' '/TeamIdentifier<\/key>/ { getline; getline; print $3 }') fi fi if [ -n "$DEVELOPMENT_TEAM" ]; then echo -e "Using TeamIdentifier from the latest provisioning profile\n" set_development_team "$DEVELOPMENT_TEAM" report_persistent_config_override else echo -e "Choose 1 to Sign Automatically or " echo -e " 2 to Sign Manually (later in Xcode)" echo -e "\nIf you choose Sign Automatically, script guides you" echo -e " to create a permanent signing file" echo -e " containing your Apple Developer ID" choose_option options=("Sign Automatically" "Sign Manually" "$(exit_or_return_menu)") select opt in "${options[@]}" do case $opt in "Sign Automatically") create_persistent_config_override break ;; "Sign Manually") break ;; "$(exit_or_return_menu)") exit_script ;; *) # Invalid option invalid_entry ;; esac done fi fi } function report_persistent_config_override() { echo -e "Your Apple Developer ID was found automatically:" grep "^$DEV_TEAM_SETTING_NAME" ${OVERRIDE_FULLPATH} echo -e "\nIf that is correct your app will be automatically signed\n" options=("ID is OK" "Editing Instructions" "$(exit_or_return_menu)") select opt in "${options[@]}" do case $opt in "ID is OK") break ;; "Editing Instructions") section_divider echo -e " Part 1: How to find your Apple Developer ID" echo -e "" how_to_find_your_id echo -e "" echo -e " Part 2: Edit the automatic signing file before hitting return" echo -e " step 1: open finder, " echo -e " step 2: locate and double click on" echo -e " ${OVERRIDE_FULLPATH/$HOME/~}" echo -e " to open that file in Xcode" echo -e " step 3: find the line that starts with " echo -e " ${DEV_TEAM_SETTING_NAME}=" echo -e " and modify the value to be your " echo -e " Apple Developer ID" echo -e " step 4: save the file" echo -e "" echo -e " When ready to proceed, hit return" return_when_ready break ;; "$(exit_or_return_menu)") exit_script ;; *) # Invalid option invalid_entry ;; esac done } function how_to_find_your_id() { echo -e "Your Apple Developer ID is the 10-character Team ID" echo -e " found on the Membership page after logging into your account at:" echo -e " https://developer.apple.com/account/#!/membership\n" echo -e "It may be necessary to click on the Membership Details icon" } function create_persistent_config_override() { section_separator echo -e "The Apple Developer page will open when you hit return\n" how_to_find_your_id echo -e "That page will be opened for you." echo -e " Once you get your ID, you will enter it in this terminal window" return_when_ready # open "https://developer.apple.com/account/#!/membership" echo -e "\n *** \nClick in terminal window so you can" read -p "Enter the ID and return: " devID # section_separator if [ ${#devID} -ne 10 ]; then echo -e "Something was wrong with the entry" echo -e "You can manually sign each target in Xcode" else echo -e "Creating ${OVERRIDE_FULLPATH}" echo -e " with your Apple Developer ID\n" # For Loop, copy the file and add developer ID # For other apps, create file with developer ID set_development_team $devID report_persistent_config_override echo -e "\nXcode uses the permanent file to automatically sign your targets" fi } set_development_team() { team_id="$1" if [[ $USE_OVERRIDE_IN_REPO == "0" ]] && [[ -f "${LOCAL_DIR}/$REPO_NAME/${OVERRIDE_FILE}" ]]; then cp -p "${LOCAL_DIR}/$REPO_NAME/${OVERRIDE_FILE}" "${OVERRIDE_FULLPATH}" elif [[ $USE_OVERRIDE_IN_REPO == "1" ]] || \ [[ $USE_OVERRIDE_IN_REPO == "2" ]]; then echo "// Automatic Signing File" > ${OVERRIDE_FULLPATH} fi if [[ $USE_OVERRIDE_IN_REPO == "2" ]]; then for str in ${ADDED_LINE_FOR_OVERRIDE[@]}; do echo "$str" >> ${OVERRIDE_FULLPATH} done fi echo "$DEV_TEAM_SETTING_NAME = $team_id" >> ${OVERRIDE_FULLPATH} } # *** End of inlined file: inline_functions/building_config_override.sh *** function standard_build_train() { verify_xcode_path check_versions clone_repo automated_clone_download_error_check check_config_override_existence_offer_to_configure ensure_a_year } function ensure_a_year() { section_separator echo -e "${INFO_FONT}Ensure a year by deleting old provisioning profiles${NC}" echo -e " Unless you have a specific reason, choose option 1\n" options=("Ensure a Year" "Skip" "$(exit_or_return_menu)") select opt in "${options[@]}" do case $opt in "Ensure a Year") clean_profiles break ;; "Skip") break ;; "$(exit_or_return_menu)") exit_script ;; *) # Invalid option invalid_entry ;; esac done } function clone_repo() { section_divider if [ "$SUPPRESS_BRANCH" == "true" ]; then LOCAL_DIR="${BUILD_DIR}/${APP_NAME}-${DOWNLOAD_DATE}" else LOCAL_DIR="${BUILD_DIR}/${APP_NAME}_${BRANCH//\//-}-${DOWNLOAD_DATE}" fi if [ ${FRESH_CLONE} == 1 ]; then mkdir "${LOCAL_DIR}" else LOCAL_DIR="${STARTING_DIR}" fi cd "${LOCAL_DIR}" if [ ${FRESH_CLONE} == 1 ]; then if [ "$SUPPRESS_BRANCH" == "true" ]; then echo -e " -- Downloading ${APP_NAME} to your Downloads folder --" else echo -e " -- Downloading ${APP_NAME} ${BRANCH} to your Downloads folder --" fi echo -e " ${LOCAL_DIR}\n" echo -e "Issuing this command:" if [[ $CLONE_SUB_MODULES == "1" ]]; then echo -e " git clone --branch=${BRANCH} --recurse-submodules ${REPO}" git clone --branch=$BRANCH --recurse-submodules $REPO else echo -e " git clone --branch=${BRANCH} ${REPO}" git clone --branch=$BRANCH $REPO fi clone_exit_status=$? else clone_exit_status=${CLONE_STATUS} fi } function automated_clone_download_error_check() { # Check if the clone was successful if [ $clone_exit_status -eq 0 ]; then # Use this flag to modify exit_or_return_menu echo -e "✅ ${SUCCESS_FONT}Successful Download. Proceed to the next step...${NC}" return_when_ready else echo -e "❌ ${ERROR_FONT}An error occurred during download. Please investigate the issue.${NC}" exit_message fi } function verify_xcode_path() { section_divider echo -e "Verifying xcode-select path...\n" # Get the path set by xcode-select xcode_path=$(xcode-select -p) # Check if the path contains "Xcode.app" if [[ -x "$xcode_path/usr/bin/xcodebuild" ]]; then echo -e "✅ ${SUCCESS_FONT}xcode-select path correctly set: $xcode_path${NC}" echo -e "Continuing the script..." else echo -e "❌ ${ERROR_FONT}xcode-select is not pointing to the correct Xcode path." echo -e " It is set to: $xcode_path${NC}" echo -e "Please choose an option below to proceed:\n" options=("Correct xcode-select path" "Skip" "$(exit_or_return_menu)") select opt in "${options[@]}" do case $opt in "Correct xcode-select path") xcode_path=$(mdfind -name Xcode.app 2>/dev/null) if [ -z "$xcode_path" ]; then echo -e "❌ ${ERROR_FONT}Xcode.app not found.${NC}" echo -e "Please use this guide to set the xcode-select path: https://loopkit.github.io/loopdocs/build/updating/#missing-xcode-or-command-line-tools" exit_message else echo -e "Using this location: $xcode_path" DEVELOPER_DIR_PATH="$xcode_path/Contents/Developer" if [ ! -d "$DEVELOPER_DIR_PATH" ] then echo -e "❌ ${ERROR_FONT}Developer directory not found in Xcode.app. Please ensure you have the correct version of Xcode installed..${NC}" echo -e "Please use this guide to set the xcode-select path: https://loopkit.github.io/loopdocs/build/updating/#missing-xcode-or-command-line-tools" exit_message else echo -e "You might be prompted for your password." echo -e " Use the password for logging into your Mac." sudo xcode-select -s "$DEVELOPER_DIR_PATH" # Check if the path was corrected successfully xcode_path=$(xcode-select -p) if [[ "$xcode_path" == *Xcode.app* ]]; then echo -e "✅ ${SUCCESS_FONT}xcode-select path has been corrected.${NC}" return_when_ready break else echo -e "❌ ${ERROR_FONT}Failed to set xcode-select path correctly.${NC}" exit_message fi fi fi ;; "Skip") break ;; "$(exit_or_return_menu)") exit_script ;; *) # Invalid option invalid_entry ;; esac done fi } function branch_select() { local url=$1 local branch=$2 local repo_name=$(basename $url .git) local app_name=${3:-$(basename $url .git)} local suppress_branch=${3:+true} REPO=$url BRANCH=$branch REPO_NAME=$repo_name APP_NAME=$app_name SUPPRESS_BRANCH=$suppress_branch } ############################################################ # End of functions used by script # - end of build_functions.sh common code ############################################################ # *** End of inlined file: inline_functions/build_functions.sh *** # *** Start of inlined file: inline_functions/loopfollow_functions.sh *** ############################################################ # Special functions used by LoopFollow build script ############################################################ function display_name_suggestion() { # Display this message only once per call to BuildLoopFollow.sh echo "" if [ "${SKIP_DISPLAY_NAME_INFORMATION}" = "1" ]; then return; fi echo "The display name replaces the follow app name on the phone" echo " and can be displayed on the home screen of the follow app" echo "" echo " To assist in finding the renamed app in iOS Settings," echo " you might want to use LF as a prefix." echo " For example: LF George" echo "" SKIP_DISPLAY_NAME_INFORMATION=1 } function loop_follow_display_name_config_override() { cd "$REPO_NAME" # Define the base file names local current_file="LoopFollowDisplayNameConfig.xcconfig" local base_target="LoopFollowDisplayNameConfig" local default_display_name="LoopFollow" local target_file local base_target_name # Check the value of REPO_NAME and set the target_file accordingly case $REPO_NAME in "LoopFollow_Second") base_target_name="${base_target}_Second" default_display_name="${default_display_name}_Second" ;; "LoopFollow_Third") base_target_name="${base_target}_Third" default_display_name="${default_display_name}_Third" ;; *) base_target_name="${base_target}" ;; esac target_file="${BUILD_DIR}/${base_target_name}.xcconfig" section_divider # Check if the target (display_name) file exists if [ -f "$target_file" ]; then # If it exists, remove the file downloaded from repo # It will be replaced rm -f "$current_file" # Report current display name echo -e "${INFO_FONT}Display name file exists: ${NC}" else # Ask user for their display_name preference echo -e "${INFO_FONT}Display name set to default value of:${NC}" echo -e "${INFO_FONT} ${default_display_name}${NC}" echo -e "" options=("Use Default" "Modify Display Name") select opt in "${options[@]}" # If user quits out of script, target not created, available for next attempt # Move the current file to the target location do mv "$current_file" "$target_file" case $opt in "Use Default") break ;; "Modify Display Name") display_name_suggestion read -p "Enter desired display name to show on Follow app: " looperID sed -i '' "s|display_name = ${default_display_name}|display_name = ${looperID}|" "$target_file" break ;; esac done fi echo "// The original file has been moved to:" > "$current_file" echo "// $target_file" >> "$current_file" echo "// Please edit the display name there." >> "$current_file" echo -e "" # Update Config.xcconfig to point to appropriate display_name file local config_file="Config.xcconfig" local include_line="#include? \"$target_file\"" # Replace the include line with the new target file sed -i '' "s|\"\.\./\.\./${base_target}|\"\.\./\.\./${base_target_name}|" "$config_file" # report the display name and provide editing instructions tail -1 "$target_file" echo "" echo -e "To modify the display_name, edit this file before you continue:" echo -e "${target_file}" display_name_suggestion return_when_ready cd .. } ############################################################ # End of functions used by LoopFollow build script ############################################################ # *** End of inlined file: inline_functions/loopfollow_functions.sh *** # *** Start of inlined file: inline_functions/testflight_functions.sh *** ############################################################ # Functions to archive the app and upload it to TestFlight ############################################################ # Required: TESTFLIGHT_SCHEME # Xcode scheme that is archived, set by the BuildXXX script # For example: TESTFLIGHT_SCHEME="LoopWorkspace" URL_TESTFLIGHT_GROUP="https://loopkit.github.io/loopdocs/browser/tf-users/#configure-testflight-group-for-the-app" URL_TESTFLIGHT_INSTALL="https://loopkit.github.io/loopdocs/browser/phone-install/" function select_xcode_or_testflight() { section_separator echo -e "${INFO_FONT}Choose how to install the app${NC}" echo -e "" echo -e " Open Xcode:" echo -e " Build in Xcode and install on a phone connected to this computer" echo -e "" echo -e " Upload to TestFlight:" echo -e " The script builds the app and uploads it to TestFlight" echo -e " You install the app on the phone with the TestFlight app" echo -e " Requires a paid Apple Developer account" echo -e " TestFlight builds expire after 90 days" echo -e "" options=("Open Xcode" "Upload to TestFlight" "$(exit_or_return_menu)") actions=("TESTFLIGHT_UPLOAD=0" "TESTFLIGHT_UPLOAD=1" "exit_script") menu_select "${options[@]}" "${actions[@]}" } function testflight_team_id() { if [ -f "${OVERRIDE_FULLPATH}" ]; then grep "^$DEV_TEAM_SETTING_NAME" "${OVERRIDE_FULLPATH}" | tail -1 | awk -F= '{gsub(/[ \t]/, "", $2); print $2}' fi } function create_testflight_export_options() { local plist_file=$1 local team_id=$(testflight_team_id) cat > "$plist_file" < method app-store-connect destination upload signingStyle automatic testFlightInternalTestingOnly manageAppVersionAndBuildNumber EOF if [ -n "$team_id" ]; then echo " teamID" >> "$plist_file" echo " ${team_id}" >> "$plist_file" fi echo "" >> "$plist_file" echo "" >> "$plist_file" } function testflight_next_steps() { echo -e " Apple processes the build before it shows up in TestFlight" echo -e " This usually takes 5 to 30 minutes" echo -e "" echo -e " First time only: set up an internal TestFlight group for the app" echo -e " and add yourself and anyone else who will install the app" echo -e " The steps are the same for every app:" echo -e " ${URL_TESTFLIGHT_GROUP}" echo -e "" echo -e " Install the app on the phone with the TestFlight app:" echo -e " ${URL_TESTFLIGHT_INSTALL}" } function testflight_missing_app_instructions() { echo -e "${INFO_FONT}The app is not yet registered in App Store Connect${NC}" echo -e " This is expected the first time you upload this app" echo -e " The archive opens in the Xcode Organizer when you hit return" echo -e "" echo -e " In the Organizer:" echo -e " 1. Select the newest archive and click Distribute App" echo -e " 2. Choose TestFlight Internal Only and click Distribute" echo -e " 3. Xcode offers to create an app record for you" echo -e " The Name must be unique in the whole App Store" echo -e " Add something to make it unique, for example: ${APP_NAME}-$(testflight_team_id)" echo -e " 4. Keep the other values and click Next until the upload starts" echo -e "" echo -e " Future uploads from this script will not need this step" } function upload_to_testflight() { local workspace=$(ls -d *.xcworkspace 2>/dev/null | head -1) local testflight_dir="${LOCAL_DIR}/TestFlight" local archive_path="${testflight_dir}/${REPO_NAME}.xcarchive" local export_options="${testflight_dir}/ExportOptions.plist" local archive_log="${testflight_dir}/archive.log" local upload_log="${testflight_dir}/upload.log" section_separator if [ -z "$workspace" ]; then echo -e "❌ ${ERROR_FONT}No Xcode workspace found in $(pwd)${NC}" exit_message fi mkdir -p "${testflight_dir}" create_testflight_export_options "${export_options}" echo -e "${INFO_FONT}Building the app for TestFlight${NC}" echo -e " This usually takes 10 to 30 minutes" echo -e " The full build log is saved to:" echo -e " ${archive_log}" echo -e "" xcodebuild archive \ -workspace "${workspace}" \ -scheme "${TESTFLIGHT_SCHEME}" \ -configuration Release \ -destination "generic/platform=iOS" \ -archivePath "${archive_path}" \ -allowProvisioningUpdates \ > "${archive_log}" 2>&1 if [ $? -ne 0 ]; then echo -e "❌ ${ERROR_FONT}The build failed${NC}" echo -e "" grep -E "error:" "${archive_log}" | sort -u | head -20 echo -e "" echo -e " The full build log is saved to:" echo -e " ${archive_log}" exit_message fi echo -e "✅ ${SUCCESS_FONT}Build succeeded${NC}" echo -e "" echo -e "${INFO_FONT}Uploading the app to App Store Connect${NC}" echo -e "" xcodebuild -exportArchive \ -archivePath "${archive_path}" \ -exportOptionsPlist "${export_options}" \ -exportPath "${testflight_dir}/export" \ -allowProvisioningUpdates \ > "${upload_log}" 2>&1 if [ $? -eq 0 ]; then echo -e "✅ ${SUCCESS_FONT}Upload succeeded${NC}" echo -e "" testflight_next_steps return fi local distribution_logs=$(grep -o 'Created bundle at path "[^"]*"' "${upload_log}" | tail -1 | sed 's/.*"\(.*\)"/\1/') if [ -n "$distribution_logs" ] && \ grep -qs "missingApp" "${distribution_logs}"/IDEDistribution*.log; then testflight_missing_app_instructions return_when_ready open "${archive_path}" echo -e "" echo -e "When the upload in the Organizer has finished:" echo -e "" testflight_next_steps return fi echo -e "❌ ${ERROR_FONT}The upload failed${NC}" echo -e "" grep -E "error:" "${upload_log}" | sort -u | head -20 echo -e "" echo -e " Common causes:" echo -e " - An agreement is waiting to be accepted in App Store Connect" echo -e " https://appstoreconnect.apple.com/business" echo -e " - Your Apple ID is not signed in to Xcode (Xcode Settings, Accounts)" echo -e "" echo -e " The full upload log is saved to:" echo -e " ${upload_log}" echo -e "" echo -e " You can also upload the archive from the Xcode Organizer," echo -e " which often shows a more detailed error message" echo -e " Select the newest archive, click Distribute App" echo -e " and choose TestFlight Internal Only" echo -e "" echo -e "The archive opens in the Xcode Organizer when you hit return" return_when_ready open "${archive_path}" } ############################################################ # End of TestFlight functions ############################################################ # *** End of inlined file: inline_functions/testflight_functions.sh *** ############################################################ # The rest of this is specific to the particular script ############################################################ open_source_warning ############################################################ # Welcome & Branch Selection ############################################################ URL_THIS_SCRIPT="https://github.com/loopandlearn/LoopFollow.git" function choose_main_branch() { branch_select ${URL_THIS_SCRIPT} main } function choose_second() { branch_select "https://github.com/loopandlearn/LoopFollow_Second.git" main } function choose_third() { branch_select "https://github.com/loopandlearn/LoopFollow_Third.git" main } function choose_dev_branch() { branch_select ${URL_THIS_SCRIPT} dev } if [ -z "$CUSTOM_BRANCH" ]; then section_separator echo -e "${INFO_FONT}You are running the script to build Loop Follow${NC}" echo -e " You need Xcode and Xcode command line tools installed" echo -e "" echo -e "${INFO_FONT}STOP - Please read this updated information${NC}" echo -e "" echo -e "You can build main or dev branch of Loop Follow with this script" echo -e "For main branch, you can choose multiple instances" echo -e " - useful if you follow more than one looper" echo -e "" echo -e "${INFO_FONT}You can choose a custom app/display name${NC}" echo -e "" echo -e "These choices build the main branch" echo -e " - 'main branch': Use this to build the primary version of Loop Follow" echo -e " - 'Second LoopFollow': Use this for a second looper" echo -e " - 'Third LoopFollow': Use this for a third looper" echo -e "" echo -e " - 'dev branch': Choose only when a feature is being tested in dev" echo -e "" echo -e "Documentation: https://www.loopandlearn.org/loop-follow/" echo -e "" return_when_ready section_divider options=("main branch" "Second LoopFollow app" "Third LoopFollow app" "dev branch" "$(exit_or_return_menu)") actions=("choose_main_branch" "choose_second" "choose_third" "choose_dev_branch" "exit_script") menu_select "${options[@]}" "${actions[@]}" else branch_select ${URL_THIS_SCRIPT} $CUSTOM_BRANCH fi ############################################################ # Standard Build train ############################################################ standard_build_train loop_follow_display_name_config_override ############################################################ # Open Xcode or upload to TestFlight ############################################################ select_xcode_or_testflight cd $REPO_NAME if [ "$TESTFLIGHT_UPLOAD" = "1" ]; then upload_to_testflight else section_divider before_final_return_message echo -e "" return_when_ready xed . fi after_final_return_message exit_script # *** End of inlined file: src/BuildLoopFollow.sh ***