setup_quickstart_spm.sh 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. #!/usr/bin/env bash
  2. # Copyright 2025 Google LLC
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. # Script to run in a CI `before_install` phase to setup a SPM-based
  16. # quickstart repo so that it can be used for integration testing.
  17. set -euo pipefail
  18. # Define testing mode constants.
  19. readonly NIGHTLY_RELEASE_TESTING="nightly_release_testing"
  20. readonly PRERELEASE_TESTING="prerelease_testing"
  21. # All script logic is contained in functions. The main function is called at
  22. # the end.
  23. # Global variables:
  24. # - readonly constants are defined at the top.
  25. # - scripts_dir and root_dir are set after constants.
  26. scripts_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
  27. root_dir="$(dirname "$scripts_dir")"
  28. print_usage() {
  29. cat <<EOF
  30. Usage: $(basename "$0") <sample_name> [testing_mode]
  31. This script sets up a quickstart sample for SPM integration testing.
  32. ARGUMENTS:
  33. <sample_name> The name of the quickstart sample directory
  34. (e.g., "authentication").
  35. [testing_mode] Optional. Specifies the testing mode. Can be one of:
  36. - "${NIGHTLY_RELEASE_TESTING}": Points SPM to the latest
  37. CocoaPods tag.
  38. - "${PRERELEASE_TESTING}": Points SPM to the tip of the main
  39. branch.
  40. - (default): Points SPM to the current commit for PR testing.
  41. ENVIRONMENT VARIABLES:
  42. QUICKSTART_REPO: Optional. Path to a local clone of the quickstart-ios repo.
  43. If not set, the script will clone it from GitHub.
  44. Example:
  45. QUICKSTART_REPO=/path/to/quickstart-ios $(basename "$0") authentication
  46. QUICKSTART_BRANCH: Optional. The branch to checkout in the quickstart repo.
  47. Defaults to the repo's default branch.
  48. Example:
  49. QUICKSTART_BRANCH=my-feature-branch $(basename "$0") authentication
  50. BYPASS_SECRET_CHECK: Optional. Set to "true" to bypass the CI secret check
  51. for local runs.
  52. Example:
  53. BYPASS_SECRET_CHECK=true $(basename "$0") authentication
  54. DEBUG: Optional. Set to "true" to enable shell trace mode (`set -x`).
  55. Example: DEBUG=true $(basename "$0") authentication
  56. EOF
  57. }
  58. # Clones or locates the quickstart repo.
  59. #
  60. # Globals:
  61. # - QUICKSTART_REPO (read-only)
  62. # Arguments:
  63. # - $1: The name of the sample.
  64. # Outputs:
  65. # - Echoes the absolute path to the quickstart directory.
  66. setup_quickstart_repo() {
  67. local sample_name="$1"
  68. local quickstart_dir
  69. # If QUICKSTART_REPO is set, use it. Otherwise, clone the repo.
  70. if [[ -n "${QUICKSTART_REPO:-}" ]]; then
  71. # If the user provided a path, it must be a valid directory.
  72. if [[ ! -d "${QUICKSTART_REPO}" ]]; then
  73. echo "Error: QUICKSTART_REPO is set to '${QUICKSTART_REPO}'," \
  74. "but this is not a valid directory." >&2
  75. exit 1
  76. fi
  77. echo "Using local quickstart repository at ${QUICKSTART_REPO}" >&2
  78. quickstart_dir="${QUICKSTART_REPO}"
  79. if ! (cd "${quickstart_dir}" && \
  80. git rev-parse --is-inside-work-tree >/dev/null 2>&1); then
  81. echo "Error: QUICKSTART_REPO ('${quickstart_dir}') is not a git" \
  82. "repository." >&2
  83. exit 1
  84. fi
  85. else
  86. # QUICKSTART_REPO is not set, so clone it.
  87. quickstart_dir="quickstart-ios"
  88. if [[ -d "${quickstart_dir}" ]]; then
  89. echo "Quickstart repository already exists at ${quickstart_dir}" >&2
  90. else
  91. echo "Cloning quickstart repository into '${quickstart_dir}' directory..." >&2
  92. # Do a partial, sparse clone to speed up CI. See
  93. # https://github.blog/2020-12-21-get-up-to-speed-with-partial-clone-and-shallow-clone/
  94. git clone --filter=blob:none --sparse \
  95. https://github.com/firebase/quickstart-ios.git "${quickstart_dir}"
  96. fi
  97. (
  98. cd "${quickstart_dir}"
  99. echo "Ensuring sparse checkout is set for ${sample_name}..." >&2
  100. # Checkout the sample and scripts directories.
  101. git sparse-checkout set "${sample_name}" scripts shared
  102. )
  103. fi
  104. # If a branch is specified, check it out.
  105. if [[ -n "${QUICKSTART_BRANCH:-}" ]]; then
  106. echo "Checking out quickstart branch: ${QUICKSTART_BRANCH}" >&2
  107. (
  108. cd "${quickstart_dir}"
  109. git fetch --quiet
  110. git checkout --quiet "${QUICKSTART_BRANCH}"
  111. )
  112. fi
  113. # Return the absolute path to the quickstart directory.
  114. (cd "$quickstart_dir" && pwd)
  115. }
  116. # Updates the SPM dependency in the Xcode project based on the testing mode.
  117. #
  118. # Globals:
  119. # - NIGHTLY_RELEASE_TESTING (read-only)
  120. # - PRERELEASE_TESTING (read-only)
  121. # - scripts_dir (read-only)
  122. # - root_dir (read-only)
  123. # Arguments:
  124. # - $1: The testing mode.
  125. # - $2: The absolute path to the .xcodeproj file.
  126. update_spm_dependency() {
  127. local release_testing_mode="$1"
  128. local absolute_project_file="$2"
  129. case "$release_testing_mode" in
  130. "${NIGHTLY_RELEASE_TESTING}")
  131. # For release testing, find the latest CocoaPods tag.
  132. local latest_tag
  133. latest_tag=$(git -C "$root_dir" tag -l "CocoaPods-*" --sort=-v:refname |
  134. awk '/^CocoaPods-[0-9]+\.[0-9]+\.[0-9]+$/{print; exit}')
  135. if [[ -z "$latest_tag" ]]; then
  136. echo "Error: Could not find the latest CocoaPods tag." >&2
  137. echo "This is often caused by a shallow git clone in a CI environment." >&2
  138. echo "If you are running in GitHub Actions, please ensure your checkout" >&2
  139. echo "step includes 'fetch-depth: 0' to fetch the full git history." >&2
  140. exit 1
  141. fi
  142. local tag_revision
  143. tag_revision=$(git -C "$root_dir" rev-list -n 1 "$latest_tag")
  144. echo "Setting SPM dependency to revision for tag ${latest_tag}:" \
  145. "${tag_revision}"
  146. "$scripts_dir/update_firebase_spm_dependency.sh" \
  147. "$absolute_project_file" --revision "$tag_revision"
  148. ;;
  149. "${PRERELEASE_TESTING}")
  150. # For prerelease testing, point to the tip of the main branch.
  151. echo "Setting SPM dependency to the tip of the main branch."
  152. "$scripts_dir/update_firebase_spm_dependency.sh" \
  153. "$absolute_project_file" --prerelease
  154. ;;
  155. *)
  156. # For PR testing, point to the current commit.
  157. local current_revision
  158. current_revision=$(git -C "$root_dir" rev-parse HEAD)
  159. echo "Setting SPM dependency to current revision: ${current_revision}"
  160. "$scripts_dir/update_firebase_spm_dependency.sh" \
  161. "$absolute_project_file" --revision "$current_revision"
  162. ;;
  163. esac
  164. }
  165. main() {
  166. # --- Argument Parsing ---
  167. if [[ -z "${1:-}" ]]; then
  168. print_usage
  169. exit 1
  170. fi
  171. local sample="$1"
  172. local release_testing="${2-}"
  173. # Validate release_testing argument.
  174. case "$release_testing" in
  175. "" | "${NIGHTLY_RELEASE_TESTING}" | "${PRERELEASE_TESTING}")
  176. # This is a valid value (or empty), so do nothing.
  177. ;;
  178. *)
  179. # This is an invalid value.
  180. echo "Error: Invalid testing_mode: '${release_testing}'" >&2
  181. print_usage
  182. exit 1
  183. ;;
  184. esac
  185. # --- Environment Setup and Validation ---
  186. # Enable trace mode if DEBUG is set to 'true'
  187. if [[ "${DEBUG:-false}" == "true" ]]; then
  188. set -x
  189. fi
  190. # Source function to check if CI secrets are available.
  191. source "$scripts_dir/check_secrets.sh"
  192. # Some quickstarts may not need a real GoogleService-Info.plist for their
  193. # tests. When QUICKSTART_REPO is set (for local runs) or BYPASS_SECRET_CHECK
  194. # is true, the secrets check is skipped.
  195. if [[ -z "${QUICKSTART_REPO:-}" ]] && \
  196. [[ "${BYPASS_SECRET_CHECK:-}" != "true" ]] && \
  197. ! check_secrets && \
  198. [[ "${sample}" != "installations" ]]; then
  199. echo "Skipping quickstart setup: CI secrets are not available."
  200. exit 0
  201. fi
  202. # --- Main Logic ---
  203. local quickstart_dir
  204. quickstart_dir=$(setup_quickstart_repo "$sample")
  205. local quickstart_project_dir="${quickstart_dir}/${sample}"
  206. if [[ ! -d "${quickstart_project_dir}" ]]; then
  207. echo "Error: Sample directory not found at '${quickstart_project_dir}'" >&2
  208. exit 1
  209. fi
  210. # Find the .xcodeproj file within the sample directory.
  211. # Fail if there isn't exactly one.
  212. # Enable nullglob to ensure the glob expands to an empty list if no files
  213. # are found.
  214. shopt -s nullglob
  215. local project_files=("${quickstart_project_dir}"/*.xcodeproj)
  216. # Restore default globbing behavior.
  217. shopt -u nullglob
  218. if [[ "${#project_files[@]}" -ne 1 ]]; then
  219. echo "Error: Expected 1 .xcodeproj file in" \
  220. "'${quickstart_project_dir}', but found ${#project_files[@]}." >&2
  221. exit 1
  222. fi
  223. local project_file="${project_files[0]}"
  224. update_spm_dependency "$release_testing" "$project_file"
  225. }
  226. # Run the main function with all provided arguments.
  227. main "$@"