-
Notifications
You must be signed in to change notification settings - Fork 1
51 ‐ About continuous integration and delivery
Today we host the project fully in this GitHub repository. However, because we do not want to leave outside certificates and provisioning profiles, we keep these sensitive files in our own GitLab CI runners and use a dedicated project to make the alpha, beta and production builds.
In the future, the demo app will be moved there so as to build it using both this public and open source repository (converted to a full Swift Package project) and internal private resources (like assets or brand elements).
You can setup in your side a GitLab CI runner which can trigger some Fastlane actions for example each night. However of course you will need distribution certificate (in .p12 format with private key) and the release provisioning profile in your runner keychain. Of course you will need also to fill secrets and environement variables. Do not expect use to share these secrets.
You can copy/paste this file to define your YAML for GitLab CI.
# Software Name: OUDS iOS
# SPDX-FileCopyrightText: Copyright (c) Orange SA
# SPDX-License-Identifier: MIT
# Variables defined by user who wants to start the pipeline
variables:
ALPHA_BRANCH_TO_BUILD:
value: ""
description: "Mandatory: The name of the branch to build as alpha version"
ALPHA_ISSUES_NUMBERS:
value: ""
description: "Mandatory: The number(s) of the issue(s) in GitHub which will be implemented in ALPHA_BRANCH_TO_BUILD and built (e.g.: '42' or seperated with commas '42, 666, 1337'). Will be used also for GitHub notifications."
GITHUB_REPOSITORY_NAME:
value: "ouds-ios"
description: "Mandatory: The name of the repository to use for builds (default: ouds-ios)"
GITHUB_ORGANIZATION_NAME:
value: "Orange-OpenSource"
description: "Mandatory: The name of the GitHub organization containing the repository to use for builds (default: Orange-OpenSource)"
BETA_BRANCH_TO_BUILD:
value: "develop"
description: "Mandatory: The name of the branch to build as beta version (default: develop)"
PRODUCTION_BRANCH_TO_BUILD:
value: "main"
description: "Mandatory: The name of the branch to build as production version (default: main)"
PATH_TO_IPA:
value: "./tmp/ouds-ios/Showcase/build/Showcase.ipa"
description: "Mandatory: The path to get the IPA for artifacts (default: ./tmp/ouds-ios/Showcase/build/Showcase.ipa)"
PATH_TO_ZIP:
value: "./tmp/ouds-ios/Showcase/build/oudsApp.zip"
description: "Mandatory: The path to get the ZIP for artifacts (default: ./tmp/ouds-ios/Showcase/build/oudsApp.zip)"
PATH_TO_APP_SOURCES:
value: "./Showcase"
description: "Mandatory: The path where the sources to build are (default: ./Showcase)"
# All stages for alpha, beta, production builds and releases
stages:
- prepare-alpha
- test-alpha
- build-alpha
- prepare-beta
- test-beta
- build-beta
- prepare-production
- build-production
# -------------------
# Alpha releases
# Dedicated branch to build on ask, triggered manualy, without Git tags
.common_alpha:
tags:
- xcode16 # Must match of course a registered and enabled runner with this label and Xcode 16 installed
.common_alpha_ios:
extends: .common_alpha
before_script:
# Job fails with allowed error code if IOS_APP_COMMIT_SHA environment variable does not exist.
# This IOS_APP_COMMIT_SHA variable is defined as environement variable in prepare-build-environment.sh
- if [[ -z "$IOS_APP_COMMIT_SHA" ]]; then exit 81680085; fi
- ./download_github_repository.sh $GITHUB_ORGANIZATION_NAME $GITHUB_REPOSITORY_NAME $IOS_APP_COMMIT_SHA
- cd tmp/$GITHUB_REPOSITORY_NAME
allow_failure:
exit_codes: 81680085
prepare_alpha_environment:
extends: .common_alpha
stage: prepare-alpha
script: ./prepare_build_environment.sh $GITHUB_ORGANIZATION_NAME $GITHUB_REPOSITORY_NAME $ALPHA_BRANCH_TO_BUILD
artifacts:
reports:
dotenv: .env
when: manual
test_alpha_ios:
extends: .common_alpha_ios
stage: test-alpha
needs: [prepare_alpha_environment]
script:
- bundle install
- cd "$PATH_TO_APP_SOURCES"
- bundle exec pod cache clean --all
- bundle exec pod install --repo-update
- bundle exec fastlane ios test
- bundle exec fastlane test_ui
build_alpha:
extends: .common_alpha_ios
stage: build-alpha
needs: [prepare_alpha_environment]
script:
- pwd
- bundle install
- cd "$PATH_TO_APP_SOURCES"
- bundle exec pod cache clean --all
- bundle exec pod install --repo-update
- bundle exec fastlane alpha commitHash:$IOS_APP_COMMIT_SHA issueNumber:"$IOS_ISSUE_NUMBER" # IOS_APP_COMMIT_SHA defined in prepare_alpha_environment phase script
artifacts:
expire_in: 1 week
paths:
- $PATH_TO_IPA
- $PATH_TO_ZIP
# -------------------
# Beta releases
# develop branch to build nightly with dedicated tags
.common_beta:
tags:
- xcode16 # Must match of course a registered and enabled runner with this label and Xcode 16 installed
rules:
- if: $CI_PIPELINE_SOURCE == "schedule" # Only scheduled pipeline needed
.common_beta_ios:
extends: .common_beta
before_script:
# Job fails with allowed error code if IOS_APP_COMMIT_SHA environment variable does not exist.
# This IOS_APP_COMMIT_SHA variable is defined as environement variable in prepare-build-environment.sh
- if [[ -z "$IOS_APP_COMMIT_SHA" ]]; then exit 81680085; fi
- ./download_github_repository.sh $GITHUB_ORGANIZATION_NAME $GITHUB_REPOSITORY_NAME $IOS_APP_COMMIT_SHA
- cd tmp/$GITHUB_REPOSITORY_NAME
allow_failure:
exit_codes: 81680085
prepare_beta_environment:
extends: .common_beta
stage: prepare-beta
script: ./prepare_build_environment.sh $GITHUB_ORGANIZATION_NAME $GITHUB_REPOSITORY_NAME $BETA_BRANCH_TO_BUILD
artifacts:
reports:
dotenv: .env
test_beta_ios:
extends: .common_beta_ios
stage: test-beta
needs: [prepare_beta_environment]
script:
- bundle install
- cd "$PATH_TO_APP_SOURCES"
- bundle exec pod cache clean --all
- bundle exec pod install --repo-update
- bundle exec fastlane ios test
- bundle exec fastlane test_ui
build_beta_ios:
extends: .common_beta_ios
stage: build-beta
needs: [prepare_beta_environment]
script:
- bundle install
- cd "$PATH_TO_APP_SOURCES"
- bundle exec pod cache clean --all
- bundle exec pod install --repo-update
- bundle exec fastlane beta commitHash:$IOS_APP_COMMIT_SHA
# Creates tags dedicated to the CI/CD builds and TestFlight uploads using some commit hash, e.g. the last commit hash.
# Will use first characters of the hash, but it might not be enough accurate because some commits may start with same value.
artifacts:
expire_in: 1 week
paths:
- $PATH_TO_IPA
- $PATH_TO_ZIP
# -------------------
# Production releases
# main branch to build on ask
.common_prod:
tags:
- xcode16 # Must match of course a registered and enabled runner with this label and Xcode 16 installed
.common_prod_ios:
extends: .common_prod
before_script:
# Job fails with allowed error code if IOS_APP_COMMIT_SHA environment variable does not exist.
# This IOS_APP_COMMIT_SHA variable is defined as environement variable in prepare-build-environment.sh
- if [[ -z "$IOS_APP_COMMIT_SHA" ]]; then exit 81680085; fi
- ./download_github_repository.sh $GITHUB_ORGANIZATION_NAME $GITHUB_REPOSITORY_NAME $IOS_APP_COMMIT_SHA
- cd tmp/$GITHUB_REPOSITORY_NAME
allow_failure:
exit_codes: 81680085
prepare_production_environment:
extends: .common_prod
stage: prepare-production
script: ./prepare_build_environment.sh $GITHUB_ORGANIZATION_NAME $GITHUB_REPOSITORY_NAME $PRODUCTION_BRANCH_TO_BUILD
artifacts:
reports:
dotenv: .env
when: manual
build_production:
extends: .common_prod_ios
stage: build-production
needs: [prepare_production_environment]
script:
- bundle install
- cd "$PATH_TO_APP_SOURCES"
- bundle exec pod cache clean --all
- bundle exec pod install
- bundle exec fastlane prod upload:true
artifacts:
expire_in: 1 week
paths:
- $PATH_TO_IPA
- $PATH_TO_ZIP
when: manual
We use a script to prepare the workspace
#!/usr/bin/env bash
# Software Name: OUDS iOS
# SPDX-FileCopyrightText: Copyright (c) Orange SA
# SPDX-License-Identifier: MIT
set -euxo pipefail
# Exit codes
# ----------
EXIT_STATUS_MISSING_PREREQUISITES=100
EXIT_STATUS_UNDEFINED_ENV_VARIABLES=101
EXIT_STATUS_ERROR_MISSING_TAG_OR_BRANCH=102
EXIT_STATUS_ERROR_NO_ORGANIZATION=200
EXIT_STATUS_ERROR_NO_PROJECT=201
EXIT_STATUS_GITHUB_REQUEST_FAILED=300
EXIT_STATUS_NO_COMMITS=301
# Functions
# ---------
DisplayUsage(){
echo " Usage: ./prepare_build_environement.sh orga_name repo_name tag_or_branch"
}
Assert(){
env_var_name=$1
env_var_value=$2
if [[ -z $env_var_value ]]; then
echo "❌ The environment variable '$env_var_name' is undefined"
exit $EXIT_STATUS_UNDEFINED_ENV_VARIABLES
else
echo "✅ The environment variable '$env_var_name' is defined"
fi
}
Check(){
env_var_name=$1
env_var_value=$2
if [[ -z $env_var_value ]]; then
echo "⚠️ The environment variable '$env_var_name' is undefined, are you aware of that?"
else
echo "✅ The environment variable '$env_var_name' is defined"
fi
}
# Requirements
# ------------
REQUIREMENTS=(curl jq)
for someCommand in ${REQUIREMENTS[@]}; do
command -v "$someCommand" > /dev/null 2>&1
if [[ $? -ne 0 ]]; then
>&2 echo "❌ Required '$someCommand' is not installed"
exit $EXIT_STATUS_MISSING_PREREQUISITES
fi
done
# Parameters
# ----------
GITHUB_ORGA_NAME=$1
if [[ -z $GITHUB_ORGA_NAME ]]; then
DisplayUsage
exit $EXIT_STATUS_ERROR_NO_ORGANIZATION
fi
GITHUB_REPO_NAME=$2
if [[ -z $GITHUB_REPO_NAME ]]; then
DisplayUsage
exit $EXIT_STATUS_ERROR_NO_PROJECT
fi
TAG_OR_BRANCH=$3 # e.g. "main" for production, "develop" for beta, other branch name for alpha
if [[ -z $TAG_OR_BRANCH ]]; then
DisplayUsage
exit $EXIT_STATUS_ERROR_MISSING_TAG_OR_BRANCH
fi
# Check main environment variables (defined in GitLab project settings)
# ---------------------------------------------------------------------
Assert "OUDS_APPLE_ISSUER_ID" "$OUDS_APPLE_ISSUER_ID"
Assert "OUDS_APPLE_KEY_CONTENT" "$OUDS_APPLE_KEY_CONTENT"
Assert "OUDS_DEVELOPER_BUNDLE_IDENTIFIER" "$OUDS_DEVELOPER_BUNDLE_IDENTIFIER"
Assert "OUDS_MATTERMOST_HOOK_URL" "$OUDS_MATTERMOST_HOOK_URL"
Assert "OUDS_MATTERMOST_HOOK_BOT_NAME" "$OUDS_MATTERMOST_HOOK_BOT_NAME"
Assert "OUDS_MATTERMOST_HOOK_BOT_ICON_URL" "$OUDS_MATTERMOST_HOOK_BOT_ICON_URL"
Assert "OUDS_FASTLANE_APPLE_ID" "$OUDS_FASTLANE_APPLE_ID"
Assert "OUDS_DEVELOPER_PORTAL_TEAM_ID" "$OUDS_DEVELOPER_PORTAL_TEAM_ID"
Assert "OUDS_APPLE_KEY_ID" "$OUDS_APPLE_KEY_ID"
Assert "GITHUB_ACCESS_TOKEN" "$GITHUB_ACCESS_TOKEN"
Check "IOS_ISSUE_NUMBER" "$IOS_ISSUE_NUMBER"
# Get last commit hash
# --------------------
> .env
echo "Preparing environment..."
echo "Tag or branch to pull sources from is: '$TAG_OR_BRANCH'"
headers=(-L -H "Accept: application/vnd.github+json" -H "Authorization: Bearer $GITHUB_ACCESS_TOKEN" -H "X-GitHub-Api-Version: 2022-11-28")
commits=$(curl "${headers[@]}" https://api.github.com/repos/$GITHUB_ORGA_NAME/$GITHUB_REPO_NAME/commits\?per_page\=100\&sha\=$TAG_OR_BRANCH)
release_commit_sha=$(echo $commits | jq -r 'try(first | .sha)')
if [[ -z $release_commit_sha ]]; then
echo "❌ Could not find any commit in qualif '$TAG_OR_BRANCH' on GitHub '$GITHUB_REPO_NAME' repository."
exit $EXIT_STATUS_NO_COMMITS
else
echo "✅ Release commit to use is '$release_commit_sha'"
fi
echo "IOS_APP_COMMIT_SHA=$release_commit_sha" >> .env # Store environment variables for GitLab jobs
IOS_APP_COMMIT_SHA="$release_commit_sha"
export IOS_APP_COMMIT_SHA
echo "✅ It seems all environment variables are defined, let's continue"
The fllowing script will download the source from GitHub with the workspace prepared by the previous script.
#!/usr/bin/env bash
# Software Name: OUDS iOS
# SPDX-FileCopyrightText: Copyright (c) Orange SA
# SPDX-License-Identifier: MIT
set -uxo pipefail
# Exit codes
# ----------
EXIT_STATUS_ERROR_NO_ORGANIZATION=1
EXIT_STATUS_ERROR_NO_PROJECT=2
EXIT_STATUS_ERROR_NO_SHA1=3
EXIT_STATUS_GITHUB_REQUEST_FAILED=4
# Utils
# ------
DisplayUsage(){
echo " Usage: ./download_github_repository.sh orga_name repo_name commit_sha1"
}
# Parameters
# ----------
GITHUB_ORGA_NAME=$1
if [[ -z $GITHUB_ORGA_NAME ]]; then
DisplayUsage
exit $EXIT_STATUS_ERROR_NO_ORGANIZATION
fi
GITHUB_REPO_NAME=$2
if [[ -z $GITHUB_REPO_NAME ]]; then
DisplayUsage
exit $EXIT_STATUS_ERROR_NO_PROJECT
fi
COMMIT_SHA=$3
if [[ -z $COMMIT_SHA ]]; then
DisplayUsage
exit $EXIT_STATUS_ERROR_NO_SHA1
fi
# Business logic
# --------------
echo "Downloading $GITHUB_ORGA_NAME/$GITHUB_REPO_NAME repository at $COMMIT_SHA"
TMP_DIR_PATH="tmp"
if [ -d $TMP_DIR_PATH ]; then
echo "Delete old temp directory"
rm -rf $TMP_DIR_PATH
fi
# No need to clone the Git repository which can be quite heavy.
# Using also SSH implies to have proxy settings allowing this protocol and to use private key
# but some developers of OUDS iOS are GitHub organization admins, thus their private key are much to powerfull
# and their use is too hazardous
echo "Create new temp directory"
mkdir "$TMP_DIR_PATH"
ZIP_FILE_PATH="$TMP_DIR_PATH/$GITHUB_REPO_NAME.zip"
HEADERS=(-L -H "Accept: application/vnd.github+json" -H "Authorization: Bearer $GITHUB_ACCESS_TOKEN" -H "X-GitHub-Api-Version: 2022-11-28")
echo "Download version..."
curlReturn=$(curl "${HEADERS[@]}" "https://api.github.com/repos/"$GITHUB_ORGA_NAME"/"$GITHUB_REPO_NAME"/zipball/"$COMMIT_SHA"" --output "$ZIP_FILE_PATH" 2>&1)
if [ $? -ne 0 ] ; then
echo "Error with GitHub request: '$curlReturn'"
exit $EXIT_STATUS_GITHUB_REQUEST_FAILED
fi
echo "Unzip version"
yes | unzip "$ZIP_FILE_PATH" -d $TMP_DIR_PATH
echo "Unzip completed ($?)"
# Rename for future steps
echo "Moving items..."
mv $TMP_DIR_PATH/"$GITHUB_ORGA_NAME"-"$GITHUB_REPO_NAME"-* "$TMP_DIR_PATH/$GITHUB_REPO_NAME"
echo "✅ It seems the sources have been downloaded and extracted successfully!"
Note that the GITHUB_ACCESS_TOKEN mus be a fine grained personal access token with permissions read and write for contents, read only for metadata, and read and write for commit statuses and issues. Click on this hyperlink to create such token, however you may need to contact your GitHub organization admins for validation or help. For Orange-OpenSource, use the usual help address you should know.
The alpha builds must be created using a manual trigger of our internal pipeline. It needs a branch to pull with sources to builds, and some issue(s) number(s). The last commit hash will be computed and used. A first step will prepare the build then a second step will build and upload the app.
The alpha build then will be uploaded automatically to TestFlight and available for a team defined in the fastlane/Appfile
(here alpha-team).
Both our Mattermost hook and the Fastlane lanes produce details about the build like version, issues and build number.
There are also in the app some extra fields defined in the app Info.plist through Fastlane and GitLab CI showing the app version, its build number, the build type (DEBUG for local builds, ALPHA for alpha release, BETA for beta release, PROD for production release) and the build details (issues numbers). The display name will be modified too.
The beta builds are created with a scheduled pipeline. This is quite the same logic as alpha builds, but with Git tags associated to the builds on develop branch (one for the build prefixed by ci/, one for TestFlight upload prefixed by Test_Flight) with commit hash as suffix.
The beta build is automatically uploaded to TestFlight for a dedicated team (here beta-team).
The Mattermostt hook is also used, the app display name and the build details are updated too.
The production builds are created with manual trigger of pipeline. It will be done on mai branch, in release configuration, and shipped to TestFlight.
Because we host our library and demo app on GitHub, we need to be sure the code compiles and the tests pass befoire merging for example. We want also to be sure DCO is applied (even if not mandatory today) and if no secrets are leaked.
We use a GitHub Actions so as to define a workflow with some actions to build demo application and test the library. It will help use to ensure code on pull requests or being merged compiles and has all tests green. This workflow is defined in this YAML, and makes build, unit tests and UI tests. Keep in mind we may have some troubles with UI tests.
We have also a gitleaks workflow making some scans on the code to loook fo secrets leaks, defined in this YAML.
We use also two GitHub apps making controls on pull requests and defining wether or not prerequisites are filled or not. There is on control to check if PR template are all defined , and one if DCO is applied.