FastLane and GitHub Actions for Mac-free install, for semi-automated TestFlight distributions (#419)

* Copy FastLane and GitHub Actions for Mac-free install

Copied from FreeAPS-X, not yet adapted to Xdrip4iOS. Originally based on FastLane and GitHub Actions from LoopKit/LoopWorkspace

* Adapt Fastfile and workflow file, rename workflow file to build_xdrip.yml

* Adapt instructions to Xdrip4iOS

* Fastfile: fix scheme (xdrip)

* xDrip.xcconfig: MAIN_APP_BUNDLE_IDENTIFIER = com.$(DEVELOPMENT_TEAM).xdripswift

* Add CURRENT_PROJECT_VERSION = $(CURRENT_PROJECT_VERSION) to xDrip and xdrip4iOS Widget targets

* testflight.md instructions: Add NFC Tag Reading to xdripswift App ID

-and some adjustments to identifier names

* Validate repository secrets

Copied from https://github.com/LoopKit/LoopWorkspace/pull/36

Validate repository secrets (#36)
* Validate repository secrets

Adds support for validation of repository secrets.

* Validate $FASTLANE_KEY as unencrypted PKCS#8

* Number workflows to guide sequential exection

* Add commented-out template for automated builds on push and on schedule (every two months)

Users must uncomment (remove "#") from the start of the lines indicated in the workflow to enable automated builds.

* testflight.md: Add Validate Secrets

instructions, and add numbered workflows.

* testflight.md: Add info about TestFlight on a child’s phone

* testflight.md: add info about steps to re-use for "Browser Builds" of other apps

* Add NFC tag reading capability to TestFlight app from Fastfile

Remove step for manual setup of NFC tag reading capability from testflight.md

* Always upload artifacts

* build_xdrip.yml: remove "secrets" job

No need to validate secrets for every build. This can be done by running Validate secrets manually.

- remove "with: submodules: recursive" from Checkout action

* build_xdrip.yml: Add branch name to run-name

* testflight:md: update instructions

* Build on macos-13 and Xcode 14.3

macos-13 is still in beta as GitHub Actions runner, but works well.

Available Xcode versions are listed here, Xcode 14.2 is currently the default:
https://github.com/actions/runner-images/blob/main/images/macos/macos-13-Readme.md#xcode

* Remove $(SRCROOT)/ from  INFOPLIST_FILE due to build error with Fastlane


INFOPLIST_FILE = "$(SRCROOT)/xdrip/Supporting Files/Info.plist"

Fastlane error:
-Cannot set build number with plist path containing $(SRCROOT)
-Please remove $(SRCROOT) in your Xcode target build settings
More info: https://github.com/fastlane/fastlane/issues/329

* build_xdrip.yml: correct app name (xDrip4iOS) in workflow name

* Sync changes from upstream, and keep repository "alive" 

The keep alive action allow scheduled runs beyond the 60 day repo inactivity limit for scheduled GitHub Actions

* build_xdrip: SYNC_UPSTREAM: 'true' / 'false'
This commit is contained in:
bjornoleh 2023-06-02 11:13:42 +02:00 committed by GitHub
parent dbac20572f
commit a13f11aa7a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 866 additions and 3 deletions

36
.github/workflows/add_identifiers.yml vendored Normal file
View File

@ -0,0 +1,36 @@
name: 2. Add Identifiers
run-name: Add Identifiers
on:
workflow_dispatch:
jobs:
secrets:
uses: ./.github/workflows/validate_secrets.yml
secrets: inherit
identifiers:
needs: secrets
runs-on: macos-13
steps:
# Uncomment to manually select latest Xcode if needed
#- name: Select Latest Xcode
# run: "sudo xcode-select --switch /Applications/Xcode_13.0.app/Contents/Developer"
# Checks-out the repo
- name: Checkout Repo
uses: actions/checkout@v3
# Patch Fastlane Match to not print tables
- name: Patch Match Tables
run: find /usr/local/lib/ruby/gems -name table_printer.rb | xargs sed -i "" "/puts(Terminal::Table.new(params))/d"
# Create or update identifiers for app
- name: Fastlane Provision
run: fastlane identifiers
env:
TEAMID: ${{ secrets.TEAMID }}
GH_PAT: ${{ secrets.GH_PAT }}
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
FASTLANE_KEY_ID: ${{ secrets.FASTLANE_KEY_ID }}
FASTLANE_ISSUER_ID: ${{ secrets.FASTLANE_ISSUER_ID }}
FASTLANE_KEY: ${{ secrets.FASTLANE_KEY }}

115
.github/workflows/build_xdrip.yml vendored Normal file
View File

@ -0,0 +1,115 @@
name: 4. Build xDrip4iOS
run-name: Build xDrip4iOS (${{ github.ref_name }})
on:
workflow_dispatch:
## Remove the "#" sign from the beginning of the line below to get automated builds on push (code changes in your repository)
#push:
schedule:
- cron: '0 04 * * *' # Checks for updates at 04:00 UTC every day
- cron: '0 04 1 * *' # Builds the app on the 1th every month
env:
UPSTREAM_REPO: JohanDegraeve/xdripswift
UPSTREAM_BRANCH: ${{ github.ref_name }} # branch on upstream repository to sync from (relpace with specific branch name if needed)
TARGET_BRANCH: ${{ github.ref_name }} # target branch on fork to be kept in sync, and target branch on upstream to be kept alive (relpace with specific branch name if needed)
SYNC_UPSTREAM: 'true' # set to 'false' or 'true' to disable / enable syncing of fork with upstream repository
jobs:
check_latest_from_upstream:
runs-on: ubuntu-latest
name: Check upstream
outputs:
NEW_COMMITS: ${{ steps.sync.outputs.has_new_commits }}
steps:
- name: Checkout target repo
uses: actions/checkout@v3
with:
# optional: set the branch to checkout,
# sync action checks out your 'target_sync_branch' anyway
#submodules: recursive
ref: ${{ env.TARGET_BRANCH }}
# REQUIRED step
# Step 2: run the sync action
- name: Sync upstream changes
if: ${{ env.SYNC_UPSTREAM == 'true' }} && github.repository_owner != 'JohanDegraeve' # do not run the upstream sync action on the upstream repository
id: sync
uses: aormsby/Fork-Sync-With-Upstream-action@v3.4
with:
target_sync_branch: ${{ env.TARGET_BRANCH }}
shallow_since: 6 months ago
target_repo_token: ${{ secrets.GH_PAT }}
upstream_sync_branch: ${{ env.UPSTREAM_BRANCH }}
upstream_sync_repo: ${{ env.UPSTREAM_REPO }}
# Step 3: Display a sample message based on the sync output var 'has_new_commits'
- name: New commits found
if: steps.sync.outputs.has_new_commits == 'true'
run: echo "New commits were found to sync."
- name: No new commits
if: steps.sync.outputs.has_new_commits == 'false'
run: echo echo "There were no new commits."
- name: Show value of 'has_new_commits'
run: |
echo ${{ steps.sync.outputs.has_new_commits }}
echo "NEW_COMMITS=${{ steps.sync.outputs.has_new_commits }}" >> $GITHUB_OUTPUT
# Keep repository "alive": add empty commits to TARGET_BRANCH after "time_elapsed" days of inactivity to avoid inactivation of scheduled workflows
- name: Keep alive
if: github.ref == 'refs/heads/${{ env.TARGET_BRANCH }}'
uses: gautamkrishnar/keepalive-workflow@v1 # using the workflow with default settings
with:
time_elapsed: 50 # Time elapsed from the previous commit to trigger a new automated commit (in days)
build:
needs: check_latest_from_upstream
runs-on: macos-13
if: ${{ github.event_name == 'workflow_dispatch' || github.event.schedule == '0 04 1 * *' || needs.check_latest_from_upstream.outputs.NEW_COMMITS == 'true' }} # runs if started manually, or if scheduled on the first each month, or if new commits were found
steps:
- name: Select Xcode version
run: "sudo xcode-select --switch /Applications/Xcode_14.3.app/Contents/Developer"
# Checks-out the repo
- name: Checkout Repo
uses: actions/checkout@v3
# Patch Fastlane Match to not print tables
- name: Patch Match Tables
run: find /usr/local/lib/ruby/gems -name table_printer.rb | xargs sed -i "" "/puts(Terminal::Table.new(params))/d"
# Build signed Xdrip4iOS IPA file
- name: Fastlane Build & Archive
run: fastlane build_xdrip4ios
env:
TEAMID: ${{ secrets.TEAMID }}
GH_PAT: ${{ secrets.GH_PAT }}
FASTLANE_KEY_ID: ${{ secrets.FASTLANE_KEY_ID }}
FASTLANE_ISSUER_ID: ${{ secrets.FASTLANE_ISSUER_ID }}
FASTLANE_KEY: ${{ secrets.FASTLANE_KEY }}
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
# Upload to TestFlight
- name: Fastlane upload to TestFlight
run: fastlane release
env:
TEAMID: ${{ secrets.TEAMID }}
GH_PAT: ${{ secrets.GH_PAT }}
FASTLANE_KEY_ID: ${{ secrets.FASTLANE_KEY_ID }}
FASTLANE_ISSUER_ID: ${{ secrets.FASTLANE_ISSUER_ID }}
FASTLANE_KEY: ${{ secrets.FASTLANE_KEY }}
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
# Upload Build artifacts
- name: Upload build log, IPA and Symbol artifacts
if: always()
uses: actions/upload-artifact@v3
with:
name: build-artifacts
path: |
artifacts
buildlog

36
.github/workflows/create_certs.yml vendored Normal file
View File

@ -0,0 +1,36 @@
name: 3. Create Certificates
run-name: Create Certificates
on:
workflow_dispatch:
jobs:
secrets:
uses: ./.github/workflows/validate_secrets.yml
secrets: inherit
certificates:
needs: secrets
runs-on: macos-13
steps:
# Uncomment to manually select latest Xcode if needed
#- name: Select Latest Xcode
# run: "sudo xcode-select --switch /Applications/Xcode_13.0.app/Contents/Developer"
# Checks-out the repo
- name: Checkout Repo
uses: actions/checkout@v3
# Patch Fastlane Match to not print tables
- name: Patch Match Tables
run: find /usr/local/lib/ruby/gems -name table_printer.rb | xargs sed -i "" "/puts(Terminal::Table.new(params))/d"
# Create or update certificates for app
- name: Create Certificates
run: fastlane certs
env:
TEAMID: ${{ secrets.TEAMID }}
GH_PAT: ${{ secrets.GH_PAT }}
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
FASTLANE_KEY_ID: ${{ secrets.FASTLANE_KEY_ID }}
FASTLANE_ISSUER_ID: ${{ secrets.FASTLANE_ISSUER_ID }}
FASTLANE_KEY: ${{ secrets.FASTLANE_KEY }}

70
.github/workflows/validate_secrets.yml vendored Normal file
View File

@ -0,0 +1,70 @@
name: 1. Validate Secrets
run-name: Validate Secrets
on: [workflow_call, workflow_dispatch]
jobs:
validate:
runs-on: macos-13
steps:
# Checks-out the repo
- name: Checkout Repo
uses: actions/checkout@v3
# Validates the repo secrets
- name: Validate Secrets
run: |
# Validate Secrets
echo Validating Repository Secrets...
# Validate TEAMID
if [ -z "$TEAMID" ]; then
failed=true
echo "::error::TEAMID secret is unset or empty. Set it and try again."
elif [ ${#TEAMID} -ne 10 ]; then
failed=true
echo "::error::TEAMID secret is set but has wrong length. Verify that it is set correctly and try again."
fi
# Validate GH_PAT
if [ -z "$GH_PAT" ]; then
failed=true
echo "::error::GH_PAT secret is unset or empty. Set it and try again."
elif [ "$(gh api -H "Accept: application/vnd.github+json" /repos/${{ github.repository_owner }}/Match-Secrets | jq --raw-output '.permissions.push')" != "true" ]; then
failed=true
echo "::error::GH_PAT secret is set but invalid or lacking appropriate privileges on the ${{ github.repository_owner }}/Match-Secrets repository. Verify that it is set correctly and try again."
fi
# Validate FASTLANE_ISSUER_ID, FASTLANE_KEY_ID, and FASTLANE_KEY
if [ -z "$FASTLANE_ISSUER_ID" ] || [ -z "$FASTLANE_KEY_ID" ] || [ -z "$FASTLANE_KEY" ]; then
failed=true
[ -z "$FASTLANE_ISSUER_ID" ] && echo "::error::The FASTLANE_ISSUER_ID secret is unset or empty. Set it and try again."
[ -z "$FASTLANE_KEY_ID" ] && echo "::error::The FASTLANE_KEY_ID secret is unset or empty. Set it and try again."
[ -z "$FASTLANE_KEY" ] && echo "::error::The FASTLANE_KEY secret is unset or empty. Set it and try again."
elif ! echo "$FASTLANE_KEY" | openssl pkcs8 -nocrypt >/dev/null; then
failed=true
echo "::error::The FASTLANE_KEY secret is set but invalid. Verify that it is set correctly and try again."
elif ! fastlane validate_secrets; then
failed=true
echo "::error::Unable to create a valid authorization token for the App Store Connect API.\
Verify that the FASTLANE_ISSUER_ID, FASTLANE_KEY_ID, and FASTLANE_KEY secrets are set correctly and try again."
fi
# Validate MATCH_PASSWORD
if [ -z "$MATCH_PASSWORD" ]; then
failed=true
echo "::error::The MATCH_PASSWORD secret is unset or empty. Set it and try again."
fi
# Exit unsuccessfully if secret validation failed.
if [ $failed ]; then
exit 2
fi
shell: bash
env:
TEAMID: ${{ secrets.TEAMID }}
GH_PAT: ${{ secrets.GH_PAT }}
FASTLANE_ISSUER_ID: ${{ secrets.FASTLANE_ISSUER_ID }}
FASTLANE_KEY_ID: ${{ secrets.FASTLANE_KEY_ID }}
FASTLANE_KEY: ${{ secrets.FASTLANE_KEY }}
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
GH_TOKEN: ${{ secrets.GH_PAT }}

3
Gemfile Normal file
View File

@ -0,0 +1,3 @@
source "https://rubygems.org"
gem "fastlane"

215
Gemfile.lock Normal file
View File

@ -0,0 +1,215 @@
GEM
remote: https://rubygems.org/
specs:
CFPropertyList (3.0.4)
rexml
addressable (2.8.0)
public_suffix (>= 2.0.2, < 5.0)
artifactory (3.0.15)
atomos (0.1.3)
aws-eventstream (1.2.0)
aws-partitions (1.516.0)
aws-sdk-core (3.121.2)
aws-eventstream (~> 1, >= 1.0.2)
aws-partitions (~> 1, >= 1.239.0)
aws-sigv4 (~> 1.1)
jmespath (~> 1.0)
aws-sdk-kms (1.50.0)
aws-sdk-core (~> 3, >= 3.121.2)
aws-sigv4 (~> 1.1)
aws-sdk-s3 (1.104.0)
aws-sdk-core (~> 3, >= 3.121.2)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.4)
aws-sigv4 (1.4.0)
aws-eventstream (~> 1, >= 1.0.2)
babosa (1.0.4)
claide (1.0.3)
colored (1.2)
colored2 (3.1.2)
commander (4.6.0)
highline (~> 2.0.0)
declarative (0.0.20)
digest-crc (0.6.4)
rake (>= 12.0.0, < 14.0.0)
domain_name (0.5.20190701)
unf (>= 0.0.5, < 1.0.0)
dotenv (2.7.6)
emoji_regex (3.2.3)
excon (0.87.0)
faraday (1.8.0)
faraday-em_http (~> 1.0)
faraday-em_synchrony (~> 1.0)
faraday-excon (~> 1.1)
faraday-httpclient (~> 1.0.1)
faraday-net_http (~> 1.0)
faraday-net_http_persistent (~> 1.1)
faraday-patron (~> 1.0)
faraday-rack (~> 1.0)
multipart-post (>= 1.2, < 3)
ruby2_keywords (>= 0.0.4)
faraday-cookie_jar (0.0.7)
faraday (>= 0.8.0)
http-cookie (~> 1.0.0)
faraday-em_http (1.0.0)
faraday-em_synchrony (1.0.0)
faraday-excon (1.1.0)
faraday-httpclient (1.0.1)
faraday-net_http (1.0.1)
faraday-net_http_persistent (1.2.0)
faraday-patron (1.0.0)
faraday-rack (1.0.0)
faraday_middleware (1.2.0)
faraday (~> 1.0)
fastimage (2.2.5)
fastlane (2.196.0)
CFPropertyList (>= 2.3, < 4.0.0)
addressable (>= 2.8, < 3.0.0)
artifactory (~> 3.0)
aws-sdk-s3 (~> 1.0)
babosa (>= 1.0.3, < 2.0.0)
bundler (>= 1.12.0, < 3.0.0)
colored
commander (~> 4.6)
dotenv (>= 2.1.1, < 3.0.0)
emoji_regex (>= 0.1, < 4.0)
excon (>= 0.71.0, < 1.0.0)
faraday (~> 1.0)
faraday-cookie_jar (~> 0.0.6)
faraday_middleware (~> 1.0)
fastimage (>= 2.1.0, < 3.0.0)
gh_inspector (>= 1.1.2, < 2.0.0)
google-apis-androidpublisher_v3 (~> 0.3)
google-apis-playcustomapp_v1 (~> 0.1)
google-cloud-storage (~> 1.31)
highline (~> 2.0)
json (< 3.0.0)
jwt (>= 2.1.0, < 3)
mini_magick (>= 4.9.4, < 5.0.0)
multipart-post (~> 2.0.0)
naturally (~> 2.2)
optparse (~> 0.1.1)
plist (>= 3.1.0, < 4.0.0)
rubyzip (>= 2.0.0, < 3.0.0)
security (= 0.1.3)
simctl (~> 1.6.3)
terminal-notifier (>= 2.0.0, < 3.0.0)
terminal-table (>= 1.4.5, < 2.0.0)
tty-screen (>= 0.6.3, < 1.0.0)
tty-spinner (>= 0.8.0, < 1.0.0)
word_wrap (~> 1.0.0)
xcodeproj (>= 1.13.0, < 2.0.0)
xcpretty (~> 0.3.0)
xcpretty-travis-formatter (>= 0.0.3)
gh_inspector (1.1.3)
google-apis-androidpublisher_v3 (0.12.0)
google-apis-core (>= 0.4, < 2.a)
google-apis-core (0.4.1)
addressable (~> 2.5, >= 2.5.1)
googleauth (>= 0.16.2, < 2.a)
httpclient (>= 2.8.1, < 3.a)
mini_mime (~> 1.0)
representable (~> 3.0)
retriable (>= 2.0, < 4.a)
rexml
webrick
google-apis-iamcredentials_v1 (0.7.0)
google-apis-core (>= 0.4, < 2.a)
google-apis-playcustomapp_v1 (0.5.0)
google-apis-core (>= 0.4, < 2.a)
google-apis-storage_v1 (0.8.0)
google-apis-core (>= 0.4, < 2.a)
google-cloud-core (1.6.0)
google-cloud-env (~> 1.0)
google-cloud-errors (~> 1.0)
google-cloud-env (1.5.0)
faraday (>= 0.17.3, < 2.0)
google-cloud-errors (1.2.0)
google-cloud-storage (1.34.1)
addressable (~> 2.5)
digest-crc (~> 0.4)
google-apis-iamcredentials_v1 (~> 0.1)
google-apis-storage_v1 (~> 0.1)
google-cloud-core (~> 1.6)
googleauth (>= 0.16.2, < 2.a)
mini_mime (~> 1.0)
googleauth (1.0.0)
faraday (>= 0.17.3, < 2.0)
jwt (>= 1.4, < 3.0)
memoist (~> 0.16)
multi_json (~> 1.11)
os (>= 0.9, < 2.0)
signet (>= 0.16, < 2.a)
highline (2.0.3)
http-cookie (1.0.4)
domain_name (~> 0.5)
httpclient (2.8.3)
jmespath (1.4.0)
json (2.6.0)
jwt (2.3.0)
memoist (0.16.2)
mini_magick (4.11.0)
mini_mime (1.1.2)
multi_json (1.15.0)
multipart-post (2.0.0)
nanaimo (0.3.0)
naturally (2.2.1)
optparse (0.1.1)
os (1.1.1)
plist (3.6.0)
public_suffix (4.0.6)
rake (13.0.6)
representable (3.1.1)
declarative (< 0.1.0)
trailblazer-option (>= 0.1.1, < 0.2.0)
uber (< 0.2.0)
retriable (3.1.2)
rexml (3.2.5)
rouge (2.0.7)
ruby2_keywords (0.0.5)
rubyzip (2.3.2)
security (0.1.3)
signet (0.16.0)
addressable (~> 2.8)
faraday (>= 0.17.3, < 2.0)
jwt (>= 1.5, < 3.0)
multi_json (~> 1.10)
simctl (1.6.8)
CFPropertyList
naturally
terminal-notifier (2.0.0)
terminal-table (1.8.0)
unicode-display_width (~> 1.1, >= 1.1.1)
trailblazer-option (0.1.1)
tty-cursor (0.7.1)
tty-screen (0.8.1)
tty-spinner (0.9.3)
tty-cursor (~> 0.7)
uber (0.1.0)
unf (0.1.4)
unf_ext
unf_ext (0.0.8)
unicode-display_width (1.8.0)
webrick (1.7.0)
word_wrap (1.0.0)
xcodeproj (1.21.0)
CFPropertyList (>= 2.3.3, < 4.0)
atomos (~> 0.1.3)
claide (>= 1.0.2, < 2.0)
colored2 (~> 3.1)
nanaimo (~> 0.3.0)
rexml (~> 3.2.4)
xcpretty (0.3.0)
rouge (~> 2.0.7)
xcpretty-travis-formatter (1.0.1)
xcpretty (~> 0.2, >= 0.0.7)
PLATFORMS
arm64-darwin-21
x86_64-darwin-19
DEPENDENCIES
fastlane
BUNDLED WITH
2.3.26

239
fastlane/Fastfile Normal file
View File

@ -0,0 +1,239 @@
# This file contains the fastlane.tools configuration
# You can find the documentation at https://docs.fastlane.tools
#
# For a list of all available actions, check out
#
# https://docs.fastlane.tools/actions
#
# For a list of all available plugins, check out
#
# https://docs.fastlane.tools/plugins/available-plugins
#
default_platform(:ios)
TEAMID = ENV["TEAMID"]
GH_PAT = ENV["GH_PAT"]
GITHUB_WORKSPACE = ENV["GITHUB_WORKSPACE"]
GITHUB_REPOSITORY_OWNER = ENV["GITHUB_REPOSITORY_OWNER"]
FASTLANE_KEY_ID = ENV["FASTLANE_KEY_ID"]
FASTLANE_ISSUER_ID = ENV["FASTLANE_ISSUER_ID"]
FASTLANE_KEY = ENV["FASTLANE_KEY"]
DEVICE_NAME = ENV["DEVICE_NAME"]
DEVICE_ID = ENV["DEVICE_ID"]
ENV["FASTLANE_XCODEBUILD_SETTINGS_TIMEOUT"] = "120"
platform :ios do
desc "Build Xdrip4iOS"
lane :build_xdrip4ios do
setup_ci if ENV['CI']
update_project_team(
path: "#{GITHUB_WORKSPACE}/xdrip.xcodeproj",
teamid: "#{TEAMID}"
)
api_key = app_store_connect_api_key(
key_id: "#{FASTLANE_KEY_ID}",
issuer_id: "#{FASTLANE_ISSUER_ID}",
key_content: "#{FASTLANE_KEY}"
)
previous_build_number = latest_testflight_build_number(
app_identifier: "com.#{TEAMID}.xdripswift",
api_key: api_key,
)
current_build_number = previous_build_number + 1
increment_build_number(
xcodeproj: "#{GITHUB_WORKSPACE}/xdrip.xcodeproj",
build_number: current_build_number
)
match(
type: "appstore",
git_basic_authorization: Base64.strict_encode64("#{GITHUB_REPOSITORY_OWNER}:#{GH_PAT}"),
app_identifier: [
"com.#{TEAMID}.xdripswift",
"com.#{TEAMID}.xdripswift.xDrip4iOS-Widget",
"com.#{TEAMID}.xdripswift.watchkitapp",
"com.#{TEAMID}.xdripswift.watchkitapp.watchkitextension"
]
)
previous_build_number = latest_testflight_build_number(
app_identifier: "com.#{TEAMID}.xdripswift",
api_key: api_key,
)
current_build_number = previous_build_number + 1
increment_build_number(
xcodeproj: "#{GITHUB_WORKSPACE}/xdrip.xcodeproj",
build_number: current_build_number
)
mapping = Actions.lane_context[
SharedValues::MATCH_PROVISIONING_PROFILE_MAPPING
]
update_code_signing_settings(
path: "#{GITHUB_WORKSPACE}/xdrip.xcodeproj",
profile_name: mapping["com.#{TEAMID}.xdripswift"],
code_sign_identity: "iPhone Distribution",
targets: ["xdrip"]
)
update_code_signing_settings(
path: "#{GITHUB_WORKSPACE}/xdrip.xcodeproj",
profile_name: mapping["com.#{TEAMID}.xdripswift.xDrip4iOS-Widget"],
code_sign_identity: "iPhone Distribution",
targets: ["xDrip4iOS Widget"]
)
update_code_signing_settings(
path: "#{GITHUB_WORKSPACE}/xdrip.xcodeproj",
profile_name: mapping["com.#{TEAMID}.xdripswift.watchkitapp"],
code_sign_identity: "iPhone Distribution",
targets: ["Watch App"]
)
update_code_signing_settings(
path: "#{GITHUB_WORKSPACE}/xdrip.xcodeproj",
profile_name: mapping["com.#{TEAMID}.xdripswift.watchkitapp.watchkitextension"],
code_sign_identity: "iPhone Distribution",
targets: ["Watch App WatchKit Extension"]
)
gym(
export_method: "app-store",
scheme: "xdrip",
output_name: "Xdrip4iOS.ipa",
configuration: "Release",
destination: 'generic/platform=iOS',
buildlog_path: 'buildlog'
)
copy_artifacts(
target_path: "artifacts",
artifacts: ["*.mobileprovision", "*.ipa", "*.dSYM.zip"]
)
end
desc "Push to TestFlight"
lane :release do
api_key = app_store_connect_api_key(
key_id: "#{FASTLANE_KEY_ID}",
issuer_id: "#{FASTLANE_ISSUER_ID}",
key_content: "#{FASTLANE_KEY}"
)
upload_to_testflight(
api_key: api_key,
skip_submission: false,
ipa: "Xdrip4iOS.ipa",
skip_waiting_for_build_processing: true,
)
end
desc "Provision Identifiers and Certificates"
lane :identifiers do
setup_ci if ENV['CI']
ENV["MATCH_READONLY"] = false.to_s
app_store_connect_api_key(
key_id: "#{FASTLANE_KEY_ID}",
issuer_id: "#{FASTLANE_ISSUER_ID}",
key_content: "#{FASTLANE_KEY}"
)
def configure_bundle_id(name, identifier, capabilities)
bundle_id = Spaceship::ConnectAPI::BundleId.find(identifier) || Spaceship::ConnectAPI::BundleId.create(name: name, identifier: identifier)
capabilities.each { |capability|
bundle_id.create_capability(capability)
}
end
configure_bundle_id("xdrip", "com.#{TEAMID}.xdripswift", [
Spaceship::ConnectAPI::BundleIdCapability::Type::APP_GROUPS,
Spaceship::ConnectAPI::BundleIdCapability::Type::HEALTHKIT,
Spaceship::ConnectAPI::BundleIdCapability::Type::NFC_TAG_READING
])
configure_bundle_id("xDrip4iOS Widget", "com.#{TEAMID}.xdripswift.xDrip4iOS-Widget", [
Spaceship::ConnectAPI::BundleIdCapability::Type::APP_GROUPS
])
configure_bundle_id("Watch App", "com.#{TEAMID}.xdripswift.watchkitapp", [
Spaceship::ConnectAPI::BundleIdCapability::Type::APP_GROUPS
])
configure_bundle_id("Watch App WatchKit Extension", "com.#{TEAMID}.xdripswift.watchkitapp.watchkitextension", [
Spaceship::ConnectAPI::BundleIdCapability::Type::APP_GROUPS,
])
end
desc "Provision Certificates"
lane :certs do
setup_ci if ENV['CI']
ENV["MATCH_READONLY"] = false.to_s
app_store_connect_api_key(
key_id: "#{FASTLANE_KEY_ID}",
issuer_id: "#{FASTLANE_ISSUER_ID}",
key_content: "#{FASTLANE_KEY}"
)
match(
type: "appstore",
force: true,
git_basic_authorization: Base64.strict_encode64("#{GITHUB_REPOSITORY_OWNER}:#{GH_PAT}"),
app_identifier: [
"com.#{TEAMID}.xdripswift",
"com.#{TEAMID}.xdripswift.xDrip4iOS-Widget",
"com.#{TEAMID}.xdripswift.watchkitapp.watchkitextension",
"com.#{TEAMID}.xdripswift.watchkitapp",
]
)
end
desc "Validate Secrets"
lane :validate_secrets do
setup_ci if ENV['CI']
ENV["MATCH_READONLY"] = true.to_s
app_store_connect_api_key(
key_id: "#{FASTLANE_KEY_ID}",
issuer_id: "#{FASTLANE_ISSUER_ID}",
key_content: "#{FASTLANE_KEY}"
)
def find_bundle_id(identifier)
bundle_id = Spaceship::ConnectAPI::BundleId.find(identifier)
end
find_bundle_id("com.#{TEAMID}.loopkit.Loop")
end
desc "Nuke Certs"
lane :nuke_certs do
setup_ci if ENV['CI']
ENV["MATCH_READONLY"] = false.to_s
app_store_connect_api_key(
key_id: "#{FASTLANE_KEY_ID}",
issuer_id: "#{FASTLANE_ISSUER_ID}",
key_content: "#{FASTLANE_KEY}"
)
match_nuke(
type: "appstore",
team_id: "#{TEAMID}",
skip_confirmation: true,
git_basic_authorization: Base64.strict_encode64("#{GITHUB_REPOSITORY_OWNER}:#{GH_PAT}")
)
end
end

10
fastlane/Matchfile Normal file
View File

@ -0,0 +1,10 @@
GITHUB_REPOSITORY_OWNER ||= ENV["GITHUB_REPOSITORY_OWNER"]
git_url("https://github.com/#{GITHUB_REPOSITORY_OWNER}/Match-Secrets.git")
storage_mode("git")
type("appstore")
# The docs are available on https://docs.fastlane.tools/actions/match

135
fastlane/testflight.md Normal file
View File

@ -0,0 +1,135 @@
# Using Github Actions + FastLane to deploy to TestFlight
These instructions allow you to build xDrip4iOS without having access to a Mac.
* You can install xDrip4iOS on phones via TestFlight that are not connected to your computer
* You can send builds and updates to those you care for
* You can install xDrip4iOS on your phone using only the TestFlight app if a phone was lost or the app is accidentally deleted
* You do not need to worry about specific Xcode/Mac versions for a given iOS
The setup steps are somewhat involved, but nearly all are one time steps. Subsequent builds are trivial. Your app must be updated once every 90 days, but it's a simple click to make a new build and can be done from anywhere. The 90-day update is a TestFlight requirement, which can be automated.
This method for building without a Mac was ported from Loop. If you have used this method for Loop or one of the other DIY apps (Loop Caregiver, Loop Follow or iAPS), some of the steps can be re-used and the full set of instructions does not need to be repeated. This will be mentioned in relevant sections below.
There are more detailed instructions in LoopDocs for using GitHub for Browser Builds of Loop, including troubleshooting and build errors. Please refer to:
* [LoopDocs: GitHub Overview](https://loopkit.github.io/loopdocs/gh-actions/gh-overview/)
* [LoopDocs: GitHub Errors](https://loopkit.github.io/loopdocs/gh-actions/gh-errors/)
Note that installing with TestFlight, (in the US), requires the Apple ID account holder to be 13 years or older. For younger users, an adult must log into Media & Purchase on the child's phone to install Loop. More details on this can be found in [LoopDocs](https://loopkit.github.io/loopdocs/gh-actions/gh-deploy/#install-testflight-loop-for-child).
## Prerequisites
* A [github account](https://github.com/signup). The free level comes with plenty of storage and free compute time to build Xdrip4iOS, multiple times a day, if you wanted to.
* A paid [Apple Developer account](https://developer.apple.com). You may be able to use the free version, but that has not been tested.
* Some time. Set aside a couple of hours to perform the setup.
* Use the same GitHub account for all "Browser Builds" of the various DIY apps.
## Generate App Store Connect API Key
This step is common for all "Browser Builds", and should be done ony once. Please save the API key somewhere safe, so it can be re-used for other builds, or if needing to start from scratch.
1. Sign in to the [Apple developer portal page](https://developer.apple.com/account/resources/certificates/list).
1. Copy the team id from the upper right of the screen. Record this as your `TEAMID`.
1. Go to the [App Store Connect](https://appstoreconnect.apple.com/access/api) interface, click the "Keys" tab, and create a new key with "Admin" access. Give it a name like "FastLane API Key".
1. Record the key id; this will be used for `FASTLANE_KEY_ID`.
1. Record the issuer id; this will be used for `FASTLANE_ISSUER_ID`.
1. Download the API key itself, and open it in a text editor. The contents of this file will be used for `FASTLANE_KEY`. Copy the full text, including the "-----BEGIN PRIVATE KEY-----" and "-----END PRIVATE KEY-----" lines.
## Setup Github Match-Secrets repository
This is also a common step for all "browser builds", do this step only once
1. Create a [new empty repository](https://github.com/new) titled `Match-Secrets`. It should be private.
## Setup Github xdripswift repository
1. Fork https://github.com/JohanDegraeve/xdripswift into your account. If you already have a fork of Xdrip4iOS in GitHub, you can't make another one. You can continue to work with your existing fork, or delete that from GitHub and then and fork https://github.com/JohanDegraeve/xdripswift.
If you have previously built Loop or another app using the "browser build" method, you can can re-use your previous personal access token (`GH_PAT`) and skip ahead to `step 2`.
1. Create a [new personal access token](https://github.com/settings/tokens/new):
* Enter a name for your token. Something like "FastLane Access Token".
* 30 days is fine, or you can select longer if you'd like.
* Select the `repo` permission scope.
* Click "Generate token".
* Copy the token and record it. It will be used below as `GH_PAT`.
1. In the forked Xdrip4iOS repo, go to Settings -> Secrets -> Actions.
1. For each of the following secrets, tap on "New repository secret", then add the name of the secret, along with the value you recorded for it:
* `TEAMID`
* `FASTLANE_KEY_ID`
* `FASTLANE_ISSUER_ID`
* `FASTLANE_KEY`
* `GH_PAT`
* `MATCH_PASSWORD` - just make up a password for this
## Validate repository secrets
1. Click on the "Actions" tab of your Xdrip4iOS repository.
1. Select "1. Validate Secrets".
1. Click "Run Workflow", and tap the green button.
1. Wait, and within a minute or two you should see a green checkmark indicating the workflow succeeded.
1. The workflow will check if the required secrets are added and that they are correctly formatted. If errors are detected, please check the run log for details.
## Add Identifiers for Xdrip4iOS App
1. Click on the "Actions" tab of your Xdrip4iOS repository.
1. Select "2. Add Identifiers".
1. Click "Run Workflow", and tap the green button.
1. Wait, and within a minute or two you should see a green checkmark indicating the workflow succeeded.
## Create App Group
If you have already built Xdrip4iOS via Xcode using this Apple ID, you can skip on to [Create Xdrip4iOS App in App Store Connect](#create-Xdrip4iOS-app-in-app-store-connect).
_Please note that in default builds of Xdrip4iOS, the app group is actually identical to the one used with Loop, so please enter these details exactly as described below. This is to ease the setup of apps such as Loop or FreeAPS X. It may require some caution if transfering between FreAPS X and Loop._
1. Go to [Register an App Group](https://developer.apple.com/account/resources/identifiers/applicationGroup/add/) on the apple developer site.
1. For Description, use "Loop App Group".
1. For Identifier, enter "group.com.TEAMID.loopkit.LoopGroup", subsituting your team id for `TEAMID`.
1. Click "Continue" and then "Register".
## Add App Group to Bundle Identifiers
1. Go to [Certificates, Identifiers & Profiles](https://developer.apple.com/account/resources/identifiers/list) on the apple developer site.
1. For each of the following identifier names:
* xdripswift
* xdripswift xDrip4iOS-Widget
* Watch App
* Watch App WatchKit Extension
1. Click on the identifier's name.
1. On the "App Groups" capabilies, click on the "Configure" button.
1. Select the "Loop App Group" _(yes, "Loop App Group" is correct)_
1. Click "Continue".
1. Click "Save".
1. Click "Confirm".
1. Remember to do this for each of the identifiers above.
## Create Xdrip4iOS App in App Store Connect
If you have created a Xdrip4iOS app in App Store Connect before, you can skip this section as well.
1. Go to the [apps list](https://appstoreconnect.apple.com/apps) on App Store Connect and click the blue "plus" icon to create a New App.
* Select "iOS".
* Select a name: this will have to be unique, so you may have to try a few different names here, but it will not be the name you see on your phone, so it's not that important.
* Select your primary language.
* Choose the bundle ID that matches `com.TEAMID.xdripswift`, with TEAMID matching your team id.
* SKU can be anything; e.g. "123".
* Select "Full Access".
1. Click Create
You do not need to fill out the next form. That is for submitting to the app store.
## Create Building Certficates
1. Go back to the "Actions" tab of your Xdrip4iOS repository in github.
1. Select "3. Create Certificates".
1. Click "Run Workflow", and tap the green button.
1. Wait, and within a minute or two you should see a green checkmark indicating the workflow succeeded.
## Build Xdrip4iOS!
1. Click on the "Actions" tab of your Xdrip4iOS repository.
1. Select "4. Build Xdrip4iOS". _Are you working on a previuos fork of Xdrip4iOS and not seeing any GitHub workflows in the Actions tab? You may have to change the default branch so that it contains the .github/workflows files, or merge these changes into your default branch (typically `master`)._
1. Click "Run Workflow", select your branch, and tap the green button.
1. You have some time now. Go enjoy a coffee. The build should take about 15 minutes.
1. Your app should eventually appear on [App Store Connect](https://appstoreconnect.apple.com/apps).
1. For each phone/person you would like to support Xdrip4iOS on:
* Add them in [Users and Access](https://appstoreconnect.apple.com/access/users) on App Store Connect.
* Add them to your TestFlight Internal Testing group.

View File

@ -4700,6 +4700,7 @@
CODE_SIGN_ENTITLEMENTS = "$(XDRIP_WIDGET_ENTITLEMENTS_DEBUG)";
CODE_SIGN_IDENTITY = "$(XDRIP_CODE_SIGN_IDENTITY_DEBUG)";
CODE_SIGN_STYLE = "$(XDRIP_CODE_SIGN_STYLE)";
CURRENT_PROJECT_VERSION = "$(CURRENT_PROJECT_VERSION)";
DEVELOPMENT_TEAM = "$(XDRIP_DEVELOPMENT_TEAM)";
INFOPLIST_FILE = "xDrip4iOS Widget/Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
@ -4724,6 +4725,7 @@
CODE_SIGN_ENTITLEMENTS = "$(XDRIP_WIDGET_ENTITLEMENTS_RELEASE)";
CODE_SIGN_IDENTITY = "$(XDRIP_CODE_SIGN_IDENTITY_RELEASE)";
CODE_SIGN_STYLE = "$(XDRIP_CODE_SIGN_STYLE)";
CURRENT_PROJECT_VERSION = "$(CURRENT_PROJECT_VERSION)";
DEVELOPMENT_TEAM = "$(XDRIP_DEVELOPMENT_TEAM)";
INFOPLIST_FILE = "xDrip4iOS Widget/Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
@ -4879,8 +4881,9 @@
CODE_SIGN_ENTITLEMENTS = "$(XDRIP_ENTITLEMENTS_DEBUG)";
CODE_SIGN_IDENTITY = "$(XDRIP_CODE_SIGN_IDENTITY_DEBUG)";
CODE_SIGN_STYLE = "$(XDRIP_CODE_SIGN_STYLE)";
CURRENT_PROJECT_VERSION = "$(CURRENT_PROJECT_VERSION)";
DEVELOPMENT_TEAM = "$(XDRIP_DEVELOPMENT_TEAM)";
INFOPLIST_FILE = "$(SRCROOT)/xdrip/Supporting Files/Info.plist";
INFOPLIST_FILE = "xdrip/Supporting Files/Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
@ -4906,8 +4909,9 @@
CODE_SIGN_ENTITLEMENTS = "$(XDRIP_ENTITLEMENTS_RELEASE)";
CODE_SIGN_IDENTITY = "$(XDRIP_CODE_SIGN_IDENTITY_RELEASE)";
CODE_SIGN_STYLE = "$(XDRIP_CODE_SIGN_STYLE)";
CURRENT_PROJECT_VERSION = "$(CURRENT_PROJECT_VERSION)";
DEVELOPMENT_TEAM = "$(XDRIP_DEVELOPMENT_TEAM)";
INFOPLIST_FILE = "$(SRCROOT)/xdrip/Supporting Files/Info.plist";
INFOPLIST_FILE = "xdrip/Supporting Files/Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",

View File

@ -4,7 +4,7 @@
//
//
MAIN_APP_BUNDLE_IDENTIFIER = net.johandegraeve.xdripswift
MAIN_APP_BUNDLE_IDENTIFIER = com.$(DEVELOPMENT_TEAM).xdripswift
// Application name [DEFAULT]
MAIN_APP_DISPLAY_NAME = xDrip4iO5