diff --git a/.bitrise/bitrise.yml b/.bitrise/bitrise.yml
new file mode 100644
index 0000000000..f1d1abe1ad
--- /dev/null
+++ b/.bitrise/bitrise.yml
@@ -0,0 +1,79 @@
+---
+format_version: '13'
+default_step_lib_source: https://github.com/bitrise-io/bitrise-steplib.git
+project_type: ios
+include:
+ - path: .bitrise/pr_adhoc/workflow_pr_adhoc_builds.yml
+workflows:
+ _common_prepare_workspace:
+ steps:
+ - script@1:
+ inputs:
+ - content: |-
+ #!/usr/bin/env bash
+ # fail if any commands fails
+ set -e
+ # debug log
+ set -x
+
+ # write your script here
+ sudo systemsetup -settimezone America/Denver
+ title: Set System Time to America
+ - activate-ssh-key: {}
+ - git::https://github.com/instructure/steps-verify-pr.git@master:
+ title: Verify PR
+ - git-clone:
+ inputs:
+ - reset_repository: 'Yes'
+ - script:
+ inputs:
+ - content: |-
+ #!/usr/bin/env bash
+ sudo systemsetup -settimezone America/Denver
+ title: Update timezone to MST
+ - git::git@github.com:instructure/steps-canvas-ios-secrets.git@master:
+ title: Canvas iOS Secrets
+ - script@1:
+ title: Install XcodeGen
+ inputs:
+ - content: |-
+ #!/usr/bin/env bash
+ # fail if any commands fails
+ set -ex
+ # make pipelines' return status equal the last command to exit with a non-zero status, or zero if all commands exit successfully
+ set -o pipefail
+
+ cd $BITRISE_SOURCE_DIR
+ make provision-ci
+ - script@1:
+ title: Run make sync
+ inputs:
+ - content: |-
+ #!/usr/bin/env bash
+ # fail if any commands fails
+ set -ex
+ # make pipelines' return status equal the last command to exit with a non-zero status, or zero if all commands exit successfully
+ set -o pipefail
+
+ cd $BITRISE_SOURCE_DIR
+ make sync-ci
+ - restore-spm-cache@2: {}
+ summary: 'This step contains all the necessary steps to prepare the Xcode workspace
+ for building/testing.'
+ _common_save_spm_cache:
+ steps:
+ - save-spm-cache@1:
+ inputs:
+ - compression_level: 1
+ summary: This workflow's only task is to save resolved SPM dependencies to the
+ cache to be re-used later by other workflows (mainly the _common_prepare_workspace
+ one).
+meta:
+ bitrise.io:
+ machine_type_id: g2-m1.8core
+ stack: osx-xcode-16.2.x
+app:
+ envs:
+ - BITRISE_PROJECT_PATH: Canvas.xcworkspace
+ opts:
+ is_expand: false
diff --git a/.bitrise/pr_adhoc/create_pr_comment.sh b/.bitrise/pr_adhoc/create_pr_comment.sh
new file mode 100755
index 0000000000..f756f058b3
--- /dev/null
+++ b/.bitrise/pr_adhoc/create_pr_comment.sh
@@ -0,0 +1,63 @@
+#!/usr/bin/env bash
+#
+# This file is part of Canvas.
+# Copyright (C) 2024-present Instructure, Inc.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+
+# fail if any commands fails
+set -e
+# make pipelines' return status equal the last command to exit with a non-zero status, or zero if all commands exit successfully
+set -o pipefail
+# debug log
+# set -x
+
+# Reads files from the working directory containing QR code URLs
+# for each app and creates a PR comment from them that will be exposed
+# with envman in the $PR_BUILDS_COMMENT variable.
+# File format is expected in this format: Student_qr_url
+
+declare -a APP_NAMES=(
+ "Student"
+ "Teacher"
+ "Parent"
+ "Horizon"
+)
+
+COLUMNS=""
+
+for APP_NAME in "${APP_NAMES[@]}"; do
+ FILE_NAME="${APP_NAME}_qr_url"
+
+ if [[ -f "${FILE_NAME}" ]]; then
+ QR_URL=$(<"${FILE_NAME}")
+ echo "${APP_NAME}'s QR url is ${QR_URL}."
+ COLUMNS+="
"
+ COLUMNS+="${APP_NAME}"
+ COLUMNS+=" "
+ COLUMNS+=" | "
+ else
+ echo "File ${FILE_NAME} not found."
+ fi
+
+done
+
+PR_COMMENT="Builds
"
+PR_COMMENT+=""
+PR_COMMENT+="${COLUMNS}
"
+PR_COMMENT+="
"
+
+printf "\nGenerated HTML snippet:\n${PR_COMMENT}"
+envman add --key PR_BUILDS_COMMENT --value "${PR_COMMENT}"
diff --git a/.bitrise/pr_adhoc/discover_apps_to_build.sh b/.bitrise/pr_adhoc/discover_apps_to_build.sh
new file mode 100755
index 0000000000..455abd2b7a
--- /dev/null
+++ b/.bitrise/pr_adhoc/discover_apps_to_build.sh
@@ -0,0 +1,63 @@
+#!/usr/bin/env bash
+#
+# This file is part of Canvas.
+# Copyright (C) 2024-present Instructure, Inc.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+
+# fail if any commands fails
+set -e
+# make pipelines' return status equal the last command to exit with a non-zero status, or zero if all commands exit successfully
+set -o pipefail
+# debug log
+# set -x
+
+# This script inspects the pull request's description and if it contains an app's name
+# it sets the appropriate environment variable.
+#
+# Outputs if conditions are met for each app:
+# - $REQUIRE_PARENT=true
+# - $REQUIRE_TEACHER=true
+# - $REQUIRE_STUDENT=true
+# - $REQUIRE_HORIZON=true
+
+if [[ ! -z $BITRISE_PULL_REQUEST ]]; then
+ envman add --key PR_NUMBER --value $BITRISE_PULL_REQUEST
+else
+ envman add --key PR_NUMBER --value "NOT_PR"
+fi
+
+if [[ $BITRISE_GIT_MESSAGE == *"Student"* ]]; then
+ envman add --key REQUIRE_STUDENT --value "true"
+fi
+
+if [[ $BITRISE_GIT_MESSAGE == *"Teacher"* ]]; then
+ envman add --key REQUIRE_TEACHER --value "true"
+fi
+
+if [[ $BITRISE_GIT_MESSAGE == *"Parent"* ]]; then
+ envman add --key REQUIRE_PARENT --value "true"
+fi
+
+if [[ $BITRISE_GIT_MESSAGE == *"Horizon"* ]]; then
+ envman add --key REQUIRE_HORIZON --value "true"
+fi
+
+if [[ $BITRISE_GIT_MESSAGE == *"affects: All"* ]]; then
+ envman add --key REQUIRE_PARENT --value "true" &&
+ envman add --key REQUIRE_TEACHER --value "true" &&
+ envman add --key REQUIRE_STUDENT --value "true" &&
+ envman add --key REQUIRE_HORIZON --value "true"
+fi
diff --git a/.bitrise/pr_adhoc/workflow_pr_adhoc_build.yml b/.bitrise/pr_adhoc/workflow_pr_adhoc_build.yml
new file mode 100644
index 0000000000..385cb9c6a0
--- /dev/null
+++ b/.bitrise/pr_adhoc/workflow_pr_adhoc_build.yml
@@ -0,0 +1,30 @@
+---
+format_version: '13'
+workflows:
+ pr_adhoc_build:
+ summary: This workflow stamps the app icon and creates a QR code installable ad-hoc build used for pull request testing.
+ description: "This step expects the following environment variables: \n$INST_XCODE_SCHEME (e.g., Horizon)\n$INST_XCODE_APPICON_PATH (e.g., Horizon/Horizon/Resources/Assets.xcassets/AppIcon.appiconset)\n\nThe output will be a file with the name ${INST_XCODE_SCHEME}_qr_url that will contain the image url of the QR code pointing to the created application. The file is needed because we can't export environment variables in case the workflow is run via the Bitrise Run step."
+ steps:
+ - set-xcode-build-number@2:
+ inputs:
+ - scheme: $INST_XCODE_SCHEME
+ - bitrise-step-stamp-appicon-with-version-number@1:
+ inputs:
+ - stamp_version: PR
+ - stamp_build_number: $PR_NUMBER
+ - stamp_path_to_icons: $INST_XCODE_APPICON_PATH
+ - xcode-archive@5:
+ inputs:
+ - distribution_method: ad-hoc
+ - automatic_code_signing: api-key
+ - icloud_container_environment: Production
+ - scheme: $INST_XCODE_SCHEME
+ - deploy-to-bitrise-io@2:
+ inputs:
+ - notify_user_groups: none
+ - create-install-page-qr-code@1: {}
+ - script-runner@0:
+ inputs:
+ - file_path: ./.bitrise/pr_adhoc/write_qr_url_to_file.sh
+ - working_dir: $BITRISE_SOURCE_DIR/.bitrise/pr_adhoc
+ title: Write QR URL To File
diff --git a/.bitrise/pr_adhoc/workflow_pr_adhoc_builds.yml b/.bitrise/pr_adhoc/workflow_pr_adhoc_builds.yml
new file mode 100644
index 0000000000..5a98916fba
--- /dev/null
+++ b/.bitrise/pr_adhoc/workflow_pr_adhoc_builds.yml
@@ -0,0 +1,112 @@
+---
+format_version: '13'
+include:
+ - path: .bitrise/pr_adhoc/workflow_pr_adhoc_build.yml
+workflows:
+ pr_adhoc_builds:
+ status_report_name: Ad-Hoc Builds
+ after_run:
+ - _common_save_spm_cache
+ before_run:
+ - _common_prepare_workspace
+ steps:
+ - script-runner@0:
+ inputs:
+ - file_path: ./.bitrise/pr_adhoc/discover_apps_to_build.sh
+ title: Discover Which Apps To Build
+ - script@1:
+ inputs:
+ - content: |-
+ #!/usr/bin/env bash
+ # fail if any commands fails
+ set -e
+ # make pipelines' return status equal the last command to exit with a non-zero status, or zero if all commands exit successfully
+ set -o pipefail
+ # debug log
+ set -x
+
+ envman add --key INST_XCODE_SCHEME --value Student
+ envman add --key INST_XCODE_APPICON_PATH --value Student/Student/Assets.xcassets/AppIcon.appiconset
+ title: Setup Student Variables
+ - bitrise-run@0:
+ inputs:
+ - bitrise_config_path: ./.bitrise/bitrise.yml
+ - workflow_id: pr_adhoc_build
+ run_if: '{{enveq "REQUIRE_STUDENT" "true"}}'
+ title: Build Student
+ - script@1:
+ inputs:
+ - content: |-
+ #!/usr/bin/env bash
+ # fail if any commands fails
+ set -e
+ # make pipelines' return status equal the last command to exit with a non-zero status, or zero if all commands exit successfully
+ set -o pipefail
+ # debug log
+ set -x
+
+ envman add --key INST_XCODE_SCHEME --value Teacher
+ envman add --key INST_XCODE_APPICON_PATH --value Teacher/Teacher/Assets.xcassets/AppIcon.appiconset
+ title: Setup Teacher Variables
+ - bitrise-run@0:
+ inputs:
+ - bitrise_config_path: ./.bitrise/bitrise.yml
+ - workflow_id: pr_adhoc_build
+ run_if: '{{enveq "REQUIRE_TEACHER" "true"}}'
+ title: Build Teacher
+ - script@1:
+ inputs:
+ - content: |-
+ #!/usr/bin/env bash
+ # fail if any commands fails
+ set -e
+ # make pipelines' return status equal the last command to exit with a non-zero status, or zero if all commands exit successfully
+ set -o pipefail
+ # debug log
+ set -x
+
+ envman add --key INST_XCODE_SCHEME --value Parent
+ envman add --key INST_XCODE_APPICON_PATH --value Parent/Parent/Assets.xcassets/AppIcon.appiconset
+ title: Setup Parent Variables
+ - bitrise-run@0:
+ inputs:
+ - bitrise_config_path: ./.bitrise/bitrise.yml
+ - workflow_id: pr_adhoc_build
+ run_if: '{{enveq "REQUIRE_PARENT" "true"}}'
+ title: Build Parent
+ - script@1:
+ inputs:
+ - content: |-
+ #!/usr/bin/env bash
+ # fail if any commands fails
+ set -e
+ # make pipelines' return status equal the last command to exit with a non-zero status, or zero if all commands exit successfully
+ set -o pipefail
+ # debug log
+ set -x
+
+ envman add --key INST_XCODE_SCHEME --value Horizon
+ envman add --key INST_XCODE_APPICON_PATH --value Horizon/Horizon/Resources/Assets.xcassets/AppIcon.appiconset
+ title: Setup Horizon Variables
+ - bitrise-run@0:
+ inputs:
+ - bitrise_config_path: ./.bitrise/bitrise.yml
+ - workflow_id: pr_adhoc_build
+ run_if: '{{enveq "REQUIRE_HORIZON" "true"}}'
+ title: Build Horizon
+ - script-runner@0:
+ inputs:
+ - file_path: ./.bitrise/pr_adhoc/create_pr_comment.sh
+ - working_dir: $BITRISE_SOURCE_DIR/.bitrise/pr_adhoc
+ title: Create PR Comment
+ - comment-on-github-pull-request@0:
+ inputs:
+ - body: $PR_BUILDS_COMMENT
+ - personal_access_token: $GITHUB_API_TOKEN
+ - update_comment_tag: bitrise_adhoc_builds
+ is_always_run: false
+trigger_map:
+- pull_request_target_branch:
+ regex: .*
+ type: pull_request
+ workflow: pr_adhoc_builds
diff --git a/.bitrise/pr_adhoc/write_qr_url_to_file.sh b/.bitrise/pr_adhoc/write_qr_url_to_file.sh
new file mode 100755
index 0000000000..142b6cbf92
--- /dev/null
+++ b/.bitrise/pr_adhoc/write_qr_url_to_file.sh
@@ -0,0 +1,32 @@
+#!/usr/bin/env bash
+#
+# This file is part of Canvas.
+# Copyright (C) 2024-present Instructure, Inc.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+
+# fail if any commands fails
+set -e
+# make pipelines' return status equal the last command to exit with a non-zero status, or zero if all commands exit successfully
+set -o pipefail
+# debug log
+# set -x
+
+# Writes the contents of the $BITRISE_PUBLIC_INSTALL_PAGE_QR_CODE_IMAGE_URL variable
+# to a file named $INST_XCODE_SCHEME_qr_url to the working directory.
+# We do this because we can't export variables with envman if the workflow is triggered
+# via the "Bitrise Run" step.
+
+echo "${BITRISE_PUBLIC_INSTALL_PAGE_QR_CODE_IMAGE_URL}" > "${INST_XCODE_SCHEME}_qr_url"
diff --git a/Canvas.xcworkspace/contents.xcworkspacedata b/Canvas.xcworkspace/contents.xcworkspacedata
index 48d5898084..16d514e7f2 100644
--- a/Canvas.xcworkspace/contents.xcworkspacedata
+++ b/Canvas.xcworkspace/contents.xcworkspacedata
@@ -44,6 +44,9 @@
location = "group:TeacherE2E.xctestplan">
+
+