Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
184 commits
Select commit Hold shift + click to select a range
d03e977
Update submodule URLs to loopkitdev forks; add sync doc
loopkitdev Mar 12, 2026
24f9d53
Update sync doc: preset scheduling history, alert permissions accuracy
loopkitdev Mar 12, 2026
5e98b22
Update submodule refs after filter-branch author rewrite; add workspa…
loopkitdev Mar 12, 2026
1a0b850
Update TidepoolService submodule ref (restore LoopAlgorithm imports)
loopkitdev Mar 13, 2026
c960899
Update sync doc with TidepoolServiceKit import fix
loopkitdev Mar 13, 2026
530057f
Update TidepoolKit to latest dev (4f4747ff)
loopkitdev Mar 25, 2026
2498db2
Update mixpanel-swift to v6.0.0 (b4cb3f6e)
loopkitdev Mar 25, 2026
4e4ae7c
MixpanelService: bump deployment target to iOS 17.0
loopkitdev Mar 25, 2026
577beb9
MixpanelService: add instance pluginIdentifier for Pluggable conformance
loopkitdev Mar 25, 2026
fa192ac
MixpanelService: update ServiceUI conformance for new LoopKitUI API
loopkitdev Mar 25, 2026
a11ce41
OmniBLE: fix private(set) whitespace for Swift 6 compatibility
loopkitdev Mar 25, 2026
3ee6fe3
OmniBLE: fix Swift 6 compile errors
loopkitdev Mar 25, 2026
b048699
Package.resolved: add json-logic-swift 1.2.4 (resolved by Xcode)
loopkitdev Mar 25, 2026
e1c6955
OmniKit: fix Swift 6 compile errors
loopkitdev Mar 25, 2026
da186a3
Bump submodule pins to current tidepool-sync/2026-03-10 heads
loopkitdev Apr 10, 2026
71525a1
OmniBLE: bump to e9425ad (fix reentrant lock crash in isSignalLost)
loopkitdev Apr 23, 2026
99c1e57
Bump submodule pins to tidepool-sync/2026-05-11 heads
ps2 May 11, 2026
212bc27
Sync log + progress for 2026-05-11
ps2 May 11, 2026
0f207ef
Catch up Loop, CGMBLEKit, OmniBLE pins to origin/dev
ps2 May 11, 2026
932d5c3
Bump Loop pin: post-dose updateRemoteRecommendation for NS uploads
ps2 May 11, 2026
d65fff9
Bump OmniKit + OmniBLE pins: setState compile fix
ps2 May 11, 2026
7c53cca
Bump MinimedKit pin: restore DIY CAGE/IAGE viewModel properties
ps2 May 11, 2026
6c7cd54
Bump LoopKit pin: restore public on ResizeablePicker
ps2 May 11, 2026
83646c7
Bump Loop + LoopKit pins: remove ResizeablePicker DIY divergence
ps2 May 11, 2026
1f603e3
Bump Loop pin: fix Live Activity preset SF Symbol rendering
ps2 May 11, 2026
168e3f1
Bump Loop pin: top-leading alignment for Live Activity preset label
ps2 May 11, 2026
429d7ad
Bump Loop pin: preset label inside plot area
ps2 May 11, 2026
193ef5a
Bump Loop pin: promote Live Activity to top-level settings
ps2 May 11, 2026
4010975
Add LibreLoop submodule for FreeStyle Libre 3 CGM plugin
loopkitdev May 12, 2026
478b615
LibreLoop: catch up to 5d6aeba on tidepool-sync base
loopkitdev May 15, 2026
becb131
LibreLoop: tidepool-sync protocol conformance + LibreCRKit pin
loopkitdev May 15, 2026
afc5ff0
LibreLoop: rolling on-disk log + lifecycle traces
loopkitdev May 15, 2026
2b834fc
LibreLoop: log glucose values we forward to Loop
loopkitdev May 15, 2026
023ed61
LibreLoop: bracket reconnect with UIApplication background task
loopkitdev May 15, 2026
58e7733
LibreLoop: cached/direct reconnect handshake (skips Phase 1-4)
loopkitdev May 15, 2026
a548418
LibreLoop: label Recent Readings columns
loopkitdev May 15, 2026
ac78ed6
LibreLoop: drop backfill samples that duplicate realtime
loopkitdev May 16, 2026
84c959c
LibreLoop: handle CCCD-refresh-failed disconnect + re-arming watchdog
loopkitdev May 16, 2026
56cb70b
Loop: diagnostic report force-unwraps watch/statusExtension managers
loopkitdev May 16, 2026
186f32b
Bump Loop pin: diagnostic report force-unwraps watch/statusExtension …
loopkitdev May 16, 2026
de32a9d
LibreLoop: throttle realtime forwarding to ~5-min cadence (opt-out to…
loopkitdev May 16, 2026
e6fd8ec
Bump Loop: restore predicted carb-effect line on ICE and food-insight…
May 16, 2026
6882612
Merge remote-tracking branch 'loopkitdev/tidepool-sync/2026-05-11' in…
loopkitdev May 16, 2026
37e84f0
LibreLoop: keep experimental-mode warning sheet alive on toggle
loopkitdev May 16, 2026
699cddd
Bump Loop: restore submodule SHAs in diagnostic report
May 16, 2026
487c0dd
LibreLoop: stop waiting on stuck reconnect work after bg expiration
loopkitdev May 17, 2026
a04648f
LibreLoop: repin LibreCRKit to private loopkitdev fork + BLE timing b…
loopkitdev May 17, 2026
e90c77b
LibreLoop: route PairingFlow handshake events into the file log
loopkitdev May 17, 2026
a602856
LibreLoop: bump LibreCRKit pin (handshake-write timeouts)
loopkitdev May 17, 2026
3037048
LibreLoop: bump LibreCRKit pin (2s awaitCommandResponse)
loopkitdev May 17, 2026
1473d17
LibreLoop: bump LibreCRKit pin (write-timeout that actually fires)
loopkitdev May 17, 2026
6652cfe
LibreLoop: also pull clinical backfill stream on reconnect (diagnostic)
loopkitdev May 18, 2026
a37bd1a
LibreLoop: stop forwarding clinical samples (forensic logging only)
loopkitdev May 18, 2026
e22b7b3
LibreLoop: forward clinical samples for per-minute backfill
loopkitdev May 18, 2026
1f8b25a
LibreLoop: serialize clinical backfill after historical, retry ATT er…
loopkitdev May 18, 2026
c8c1b13
LibreLoop: drop reconnect connect-timeout, let iOS drive
loopkitdev May 18, 2026
0311157
LibreLoop: verify central + peripheral state before connect
loopkitdev May 18, 2026
80df1d9
LibreLoop: G7-style peripheral recovery + central state listener
loopkitdev May 18, 2026
d55b822
LibreLoop: drop bg-task wrap around reconnect
loopkitdev May 19, 2026
3b68b6d
Bump Loop: External Insulin log labeling/delete, presets cleanup
May 20, 2026
a85ee45
Bump Loop: merge origin/dev into tidepool-sync (resolve WatchApp plis…
May 20, 2026
0658821
Merge origin/dev into tidepool-sync/2026-05-11
May 20, 2026
45214e7
LibreLoop: persist receiverID + auto-recover on fresh-pair
loopkitdev May 20, 2026
68ce294
Bump Loop: fix WatchApp Info.plist (drop WKWatchKitApp) for watch emb…
May 20, 2026
7d11a75
Bump Loop: Apple Health access status screen in Settings
May 20, 2026
7fd02d6
Bump LoopKit: keep Loop-written doses on external HealthKit deletion
May 21, 2026
a31116a
Bump Loop: flag-gated dose deletion from dose details (with active-in…
May 21, 2026
d1768c3
Bump Loop: enable dose deletion by default
May 21, 2026
a36ee93
Bump Loop: clear stale Last Bolus after dose deletion
May 21, 2026
ffeadc0
Bump Loop: don't allow deleting in-progress doses
May 21, 2026
9987f2a
Bump LoopKit: don't crash on legacy bolus without units; note divergence
May 21, 2026
a7c68cf
Bump LoopKit: preserve dose amounts across Modelv4→v6 migration
May 21, 2026
f1e137a
docs: note one-way upgrade (Core Data v4→v6) in sync notes
May 21, 2026
a775f2f
Bump Loop: restore localized Intents.strings refs (ITMS-90626)
May 21, 2026
4046252
Bump OmniBLE/OmniKit pins: report pod faults as pump events
May 21, 2026
14d1987
Bump version to 3.15.0
May 21, 2026
f56ea73
LibreLoop: sensor end-of-life UX
loopkitdev May 21, 2026
abad56f
Build against LoopKit/LoopAlgorithm main
ps2 May 21, 2026
76305d9
Bump Loop: show carb sharing status on Apple Health page
ps2 May 22, 2026
817b0cd
LibreLoop: tappable sample detail view from Recent Readings
loopkitdev May 22, 2026
fc9293e
Bump Loop: default to automatic bolus, deprecate temp basal
ps2 May 23, 2026
1a0c518
Bump NightscoutRemoteCGM: API Secret optional for read-only sites
ps2 May 24, 2026
b26221f
Bump NightscoutRemoteCGM: add verification logging
ps2 May 24, 2026
3ee4abd
Bump NightscoutRemoteCGM: 'Optional' placeholder for API Secret
ps2 May 24, 2026
a641759
Bump NightscoutRemoteCGM: log periodic glucose fetches
ps2 May 24, 2026
f839eaa
Bump NightscoutRemoteCGM: fix glucose filtering (sort before filterDa…
ps2 May 24, 2026
8cb791e
Bump LibreTransmitter: sort glucose before filterDateRange
ps2 May 24, 2026
980bf9c
Bump LoopAlgorithm pin to main HEAD (sorted assert merged)
ps2 May 25, 2026
ed1bb5f
Vendor LoopAlgorithm as a submodule instead of a remote package
ps2 May 25, 2026
339b188
Declare LoopAlgorithm as a workspace-level local package
ps2 May 25, 2026
4e5d839
Bump LoopKit: sort normalized doses before filterDateRange
ps2 May 25, 2026
262ef31
Correct sync-revert docs: dev can downgrade-open the v6 store
ps2 May 26, 2026
8448b82
Bump Loop: merge dev into sync branch (brings in dev-branch warning)
ps2 May 26, 2026
963763c
Merge remote-tracking branch 'origin/dev' into tidepool-sync/2026-05-11
ps2 May 26, 2026
66422e1
Bump LibreLoop, Loop, LoopKit submodule pointers
loopkitdev May 27, 2026
cb71076
Merge tidepool-sync/2026-05-11 into libreloop
loopkitdev May 27, 2026
c439dcb
Bump MinimedKit: fix off-queue BLE crash in fetchPumpHistory
loopkitdev May 31, 2026
967fcbb
Bump MinimedKit: fix off-queue BLE crash in fetchPumpHistory
ps2 May 31, 2026
5147564
Bump Loop: sort annotated doses before filterDateRange
ps2 Jun 1, 2026
1b894ff
Bump Loop: watch app crash fix, assets cleanup, missing presets image
ps2 Jun 1, 2026
f03461f
Bump TidepoolKit pin to pick up TSession backward-compat fix
ps2 Jun 1, 2026
2064197
Bring in tidepool-sync/2026-05-11 latest; bump LibreLoop, Loop, LoopKit
loopkitdev Jun 1, 2026
eacb6c8
Bump LibreLoop and NightscoutService; pin LibreCRKit 88508ae
loopkitdev Jun 2, 2026
e1ec25b
Bump Loop: pause loop while a manual temp basal is running
ps2 Jun 2, 2026
0bf111f
Bump Loop + LoopKit: MockPump manual temp basal feature and HUD wiring
ps2 Jun 3, 2026
ad1a2a0
Bump Loop + LoopKit: manual temp basal localization strings
ps2 Jun 3, 2026
b3d223e
Bump LoopKit: MockPump settings UI cleanup
ps2 Jun 3, 2026
c593efd
Update gitlinks
ps2 Jun 3, 2026
8364c86
Bump Loop: project ongoing suspend into display forecast, coalesce ba…
ps2 Jun 3, 2026
0df3885
Bump LoopAlgorithm: merge LoopKit/main + Tidepool/main into tidepool-…
ps2 Jun 3, 2026
db892a8
Bump LoopKit: list Automatic Bolus first in Dosing Strategy picker
ps2 Jun 3, 2026
565ef39
Bump Loop + LoopKit: SettingsView version row, de-brand "Tidepool Loop"
ps2 Jun 3, 2026
ef78f98
Bump Loop: focus-mode imagesets for IOSFocusModesView
ps2 Jun 3, 2026
5d9350c
Update gitlinks
ps2 Jun 4, 2026
b2aec29
Bump LibreLoop and Loop; add LibreCRKit submodule (upstream airedev326)
loopkitdev Jun 4, 2026
5ea6bdc
add OmnipodKit submodule
marionbarker Jun 6, 2026
c81ed16
Update Loop: selectable alarm sounds + Delay 1st Alert for highs
loopkitdev Jun 6, 2026
cc2572f
Update submodules: BLE timeouts/reconnect hardening, Nightscout local…
loopkitdev Jun 6, 2026
43c04ac
Merge pull request #456 from LoopKit/add-omnipodkit-to-next-dev
ps2 Jun 9, 2026
1ffa139
Wire MedtrumKit + EversenseKit into next-dev; bump LoopKit to latest
ps2 Jun 9, 2026
15193ae
Drop OmniBLE + OmniKit (superseded by OmnipodKit)
ps2 Jun 9, 2026
b96563c
Bump LibreLoop: update glucose-alerting safety section
loopkitdev Jun 9, 2026
b643e2a
Actually wire MedtrumKit + EversenseKit into workspace + scheme
ps2 Jun 10, 2026
6d2be55
Merge remote-tracking branch 'origin/next-dev' into integrate-next-dev
loopkitdev Jun 10, 2026
e6b08b2
Bump LoopOnboarding: link NightscoutServiceKitUI to fix explicit-modu…
loopkitdev Jun 10, 2026
4218bad
Merge pull request #457 from loopkitdev/integrate-next-dev
loopkitdev Jun 10, 2026
25b6c93
Merge origin/dev (3.14.2) into next-dev
ps2 Jun 10, 2026
b7682a9
Point LibreLoop submodule at LoopKit/LibreLoop
ps2 Jun 10, 2026
c9d849f
CI: bump CircleCI Xcode 16.4 -> 26.4, drop simulator OS pin
ps2 Jun 10, 2026
b2bc1b3
CI: use iPhone 17 simulator (iPhone 16 not on the iOS 26 runtime)
ps2 Jun 10, 2026
b9fa1d6
Repoint OmnipodKit at loopkitdev fork for the OmniTests decisionId fix
ps2 Jun 10, 2026
ba9f5d0
Bump Loop: add isManualTempBasalRunning to MockDeliveryDelegate (fixe…
ps2 Jun 10, 2026
19fd676
Bump LoopKit: fix stale LoopKitTests expectations (guardrails 87/67, …
ps2 Jun 10, 2026
1537b18
Bump Loop: LoopDataManagerTests current with LoopAlgorithm + DIY beha…
ps2 Jun 10, 2026
09ba9eb
Bump Loop: fix full-scheme test isolation (testOpenLoopCancelsTempBasal)
ps2 Jun 11, 2026
c7cd0b5
CI: use xcbeautify instead of xcpretty so test failures surface
ps2 Jun 11, 2026
71b5ffd
Bump OmnipodKit: CryptoSwift 1.9+ compatibility (Data.bytes -> Array(…
ps2 Jun 11, 2026
b98982e
Bump EversenseKit: set deployment target to 17.0
ps2 Jun 11, 2026
9c5b5da
Bump Loop: glucose alerts issue only the most severe low-side alert
ps2 Jun 11, 2026
c3e6b65
Bump Loop + LibreLoop: AlarmKit critical alerts; backfill recent-wind…
loopkitdev Jun 12, 2026
bc52c97
Bump LibreLoop: forward llog events to Loop's DeviceLog
loopkitdev Jun 12, 2026
08a78b9
Bump Loop: Statistics/AGP page + watch glucose backfill
ps2 Jun 18, 2026
58d2d6b
Bump LibreLoop: Glucose Streams debug view + continuous clinical + re…
ps2 Jun 24, 2026
b8528c0
Bump LibreLoop: stable receiverID, pairing/stability fixes, LibreCRKi…
ps2 Jun 25, 2026
ebd3441
Bump LibreCRKit submodule to e69dbd6 (fixes CI: Libre3SensorAttention)
ps2 Jun 25, 2026
3d62a3c
Bump LibreLoop: reconnect backoff + re-scan prompt (no futile first-p…
ps2 Jun 26, 2026
b3cde61
Bump LibreLoop: 'Replace sensor' status highlight + page on sensor fa…
ps2 Jun 26, 2026
aac021f
Bump LibreLoop: polished + localized sensor-failure UI
ps2 Jun 26, 2026
38f9a95
Bump LibreLoop: respect mg/dL vs mmol/L display preference in setting…
ps2 Jun 27, 2026
32dea60
Bump LibreLoop: localize all user-facing strings (module-scoped)
ps2 Jun 27, 2026
c90e4b7
Bump LibreLoop: String Catalogs + Loop's language set
ps2 Jun 27, 2026
e1e1354
Bump LibreLoop: pin standalone LibreCRKit reference to e69dbd6
ps2 Jun 27, 2026
2e8e0e7
Bump LibreLoop: use host app name instead of hardcoded Loop
ps2 Jun 28, 2026
1fd0878
Bump LoopKit: fix indefinite-override OOM during bolus recommendation
ps2 Jun 28, 2026
2e02b8a
Bump LibreCRKit (CCCD re-arm on patchControl reconnect) + LibreLoop pin
ps2 Jun 29, 2026
60a3282
Bump LibreLoop: fix Recent Readings detail crash (missing DisplayGluc…
ps2 Jun 30, 2026
10bcba2
Update OmnipodKit
ps2 Jul 3, 2026
2adc099
Update OmnipodKit to loop-next-dev 7a37f29
ps2 Jul 7, 2026
3e97f18
Update OmnipodKit to loop-next-dev 4727535 (fix OmniTests fault-time …
ps2 Jul 8, 2026
d770452
Bump LibreCRKit (66920c6) + LibreLoop (b21aa70): Libre 3 backfill fix
ps2 Jul 11, 2026
8caa651
Switch submodules from loopkitdev forks to LoopKit
ps2 Jul 13, 2026
d4271d1
Bump Loop to 5f0686cf (carb-entry LoopConstants PR)
ps2 Jul 13, 2026
6dcc2e3
Bump Loop: glucose alert thresholds in the user's display unit
ps2 Jul 15, 2026
b0e1026
docs: consolidate Tidepool sync docs into user + developer guides
ps2 Jul 15, 2026
c8b872e
docs: correct What's New (drop de-branding, add Omnipod 5, main-scree…
ps2 Jul 15, 2026
4f3ce07
docs: note the redesigned Insulin Delivery Log in What's New
ps2 Jul 15, 2026
89e59af
docs: clarify mid-absorption ISF fix and note the gradual-transitions…
ps2 Jul 15, 2026
d02e80e
docs: fix mid-absorption ISF description (insulin absorption, not carb)
ps2 Jul 15, 2026
584bdaf
Bump LibreLoop: sensor Expired vs failed; transient code-7 comms erro…
ps2 Jul 15, 2026
14b70bd
Bump LibreLoop: log the connect→no-stream arming livelock (counter + …
ps2 Jul 18, 2026
4119611
Bump submodules to latest integration branches
ps2 Jul 24, 2026
69e1a15
Loop: fix Live Activity cooperative-pool starvation deadlock
ps2 Jul 24, 2026
47fb757
Bump version to 3.15.1; point OmnipodKit at loopandlearn/next-dev
ps2 Jul 24, 2026
7701761
Bump Loop and OmnipodKit to next-dev tips
ps2 Jul 27, 2026
c5f5149
Bump LoopKit: fix watchOS crash reading glucose row with NULL startDate
ps2 Jul 27, 2026
0621dbc
Bump LoopKit: revert glucose startDate crash fix pending verification
ps2 Jul 27, 2026
6dd0add
Bump Loop: watch GlucoseStore gets its own Core Data stack (#2475)
ps2 Jul 29, 2026
a3149ef
Merge pull request #461 from daghaian-loop-org/add-build-autotrigger
daghaian Jun 30, 2026
8508375
improve clarity: when build is skipped, run is canceled no green che…
marionbarker Jul 20, 2026
9a2353f
update to fastlane 2.236.1
marionbarker Jun 21, 2026
370e481
update fastlane to 2.237.0
marionbarker Jul 29, 2026
a73eb6e
Update browser build for next dev (#476)
marionbarker Jul 30, 2026
e972e97
LibreLoop: pull in reconnect-standing-connect-intent fixes (#5)
ps2 Jul 30, 2026
495a2db
LoopAlgorithm: bound IntegralRC correction rate (safety, #37)
ps2 Jul 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
jobs:
build_and_test:
macos:
xcode: 16.4
xcode: 26.4
steps:
- checkout
- run:
name: Checkout submodules
command: git submodule update --init --recursive --depth 1
- run:
name: Install xcbeautify
command: brew list xcbeautify >/dev/null 2>&1 || brew install xcbeautify
- run:
name: Build Loop
command: set -o pipefail && time xcodebuild -workspace LoopWorkspace.xcworkspace -scheme 'LoopWorkspace' -destination 'platform=iOS Simulator,name=iPhone 16,OS=18.5' build | xcpretty
command: set -o pipefail && time xcodebuild -workspace LoopWorkspace.xcworkspace -scheme 'LoopWorkspace' -destination 'platform=iOS Simulator,name=iPhone 17' build | xcbeautify
- run:
name: Run Tests
command: set -o pipefail && time xcodebuild -workspace LoopWorkspace.xcworkspace -scheme 'LoopWorkspace' -destination 'platform=iOS Simulator,name=iPhone 16,OS=18.5' test | xcpretty
command: set -o pipefail && time xcodebuild -workspace LoopWorkspace.xcworkspace -scheme 'LoopWorkspace' -destination 'platform=iOS Simulator,name=iPhone 17' test | xcbeautify --report junit --report-path test-results/results.xml
- store_test_results:
path: test-results
workflows:
version: 2
build_and_test:
Expand Down
10 changes: 3 additions & 7 deletions .github/workflows/build_loop.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
name: 4. Build Loop
run-name: Build Loop (${{ github.ref_name }})
name: 4. Build Loop Manual
run-name: Build Loop Manual (${{ github.ref_name }})
on:
workflow_dispatch:
schedule:
# Check for updates every Sunday
# Later logic builds if there are updates or if it is the 2nd Sunday of the month
- cron: "33 7 * * 0" # Sunday at UTC 7:33

env:
GH_PAT: ${{ secrets.GH_PAT }}
Expand Down Expand Up @@ -260,4 +256,4 @@ jobs:
name: build-artifacts
path: |
artifacts
buildlog
buildlog
287 changes: 287 additions & 0 deletions .github/workflows/build_loop_auto.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,287 @@
name: 4. Build Loop Auto
run-name: Build Loop Auto (${{ github.ref_name }})
on:
workflow_dispatch:
schedule:
# Check for updates every Sunday
# Later logic builds if there are updates or if it is the 2nd Sunday of the month
- cron: "33 7 * * 0" # Sunday at UTC 7:33

env:
GH_PAT: ${{ secrets.GH_PAT }}
UPSTREAM_REPO: LoopKit/LoopWorkspace
UPSTREAM_BRANCH: ${{ github.ref_name }} # branch on upstream repository to sync from (replace with specific branch name if needed)
TARGET_BRANCH: ${{ github.ref_name }} # target branch on fork to be kept in sync

jobs:
# use a single runner for these sequential steps
check_status:
runs-on: ubuntu-latest
name: Check status to decide whether to build
permissions:
contents: write
outputs:
NEW_COMMITS: ${{ steps.sync.outputs.has_new_commits }}
IS_SECOND_IN_MONTH: ${{ steps.date-check.outputs.is_second_instance }}

# Check GH_PAT, sync repository, check day in month
steps:

- name: Access
id: workflow-permission
run: |
# Validate Access Token

# Ensure that gh exit codes are handled when output is piped.
set -o pipefail

# Define patterns to validate the access token (GH_PAT) and distinguish between classic and fine-grained tokens.
GH_PAT_CLASSIC_PATTERN='^ghp_[a-zA-Z0-9]{36}$'
GH_PAT_FINE_GRAINED_PATTERN='^github_pat_[a-zA-Z0-9]{22}_[a-zA-Z0-9]{59}$'

# Validate Access Token (GH_PAT)
if [ -z "$GH_PAT" ]; then
failed=true
echo "::error::The GH_PAT secret is unset or empty. Set it and try again."
else
if [[ $GH_PAT =~ $GH_PAT_CLASSIC_PATTERN ]]; then
provides_scopes=true
echo "The GH_PAT secret is a structurally valid classic token."
elif [[ $GH_PAT =~ $GH_PAT_FINE_GRAINED_PATTERN ]]; then
echo "The GH_PAT secret is a structurally valid fine-grained token."
else
unknown_format=true
echo "The GH_PAT secret does not have a known token format."
fi

# Attempt to capture the x-oauth-scopes scopes of the token.
if ! scopes=$(curl -sS -f -I -H "Authorization: token $GH_PAT" https://api.github.com | { grep -i '^x-oauth-scopes:' || true; } | cut -d ' ' -f2- | tr -d '\r'); then
failed=true
if [ $unknown_format ]; then
echo "::error::Unable to connect to GitHub using the GH_PAT secret. Verify that it is set correctly (including the 'ghp_' or 'github_pat_' prefix) and try again."
else
echo "::error::Unable to connect to GitHub using the GH_PAT secret. Verify that the token exists and has not expired at https://github.com/settings/tokens. If necessary, regenerate or create a new token (and update the secret), then try again."
fi
elif [[ $scopes =~ workflow ]]; then
echo "The GH_PAT secret has repo and workflow permissions."
echo "has_permission=true" >> $GITHUB_OUTPUT
elif [[ $scopes =~ repo ]]; then
echo "The GH_PAT secret has repo (but not workflow) permissions."
elif [ $provides_scopes ]; then
failed=true
if [ -z "$scopes" ]; then
echo "The GH_PAT secret is valid and can be used to connect to GitHub, but it does not provide any permission scopes."
else
echo "The GH_PAT secret is valid and can be used to connect to GitHub, but it only provides the following permission scopes: $scopes"
fi
echo "::error::The GH_PAT secret is lacking at least the 'repo' permission scope required to access the Match-Secrets repository. Update the token permissions at https://github.com/settings/tokens (to include the 'repo' and 'workflow' scopes) and try again."
else
echo "The GH_PAT secret is valid and can be used to connect to GitHub, but it does not provide inspectable scopes. Assuming that the 'repo' and 'workflow' permission scopes required to access the Match-Secrets repository and perform automations are present."
echo "has_permission=true" >> $GITHUB_OUTPUT
fi
fi

# Exit unsuccessfully if secret validation failed.
if [ $failed ]; then
exit 2
fi

- name: Checkout target repo
if: |
steps.workflow-permission.outputs.has_permission == 'true' &&
(vars.SCHEDULED_BUILD != 'false' || vars.SCHEDULED_SYNC != 'false')
uses: actions/checkout@v5
with:
token: ${{ secrets.GH_PAT }}

# This syncs any target branch to upstream branch of the same name
- name: Sync upstream changes
if: | # do not run the upstream sync action on the upstream repository
steps.workflow-permission.outputs.has_permission == 'true' &&
vars.SCHEDULED_SYNC != 'false' && github.repository_owner != 'LoopKit'
id: sync
uses: aormsby/Fork-Sync-With-Upstream-action@v3.4.2
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 }}

# Display a sample message based on the sync output var 'has_new_commits'
- name: New commits found
if: |
steps.workflow-permission.outputs.has_permission == 'true' &&
vars.SCHEDULED_SYNC != 'false' && steps.sync.outputs.has_new_commits == 'true'
run: echo "New commits were found to sync."

- name: No new commits
if: |
steps.workflow-permission.outputs.has_permission == 'true' &&
vars.SCHEDULED_SYNC != 'false' && steps.sync.outputs.has_new_commits == 'false'
run: echo "There were no new commits."

- name: Show value of 'has_new_commits'
if: steps.workflow-permission.outputs.has_permission == 'true' && vars.SCHEDULED_SYNC != 'false'
run: |
echo ${{ steps.sync.outputs.has_new_commits }}
echo "NEW_COMMITS=${{ steps.sync.outputs.has_new_commits }}" >> $GITHUB_OUTPUT

- name: Show scheduled build configuration message
if: steps.workflow-permission.outputs.has_permission != 'true'
run: |
echo "### :calendar: Scheduled Sync and Build Disabled :mobile_phone_off:" >> $GITHUB_STEP_SUMMARY
echo "You have not yet configured the scheduled sync and build for Loop's browser build." >> $GITHUB_STEP_SUMMARY
echo "Synchronizing your fork of <code>LoopWorkspace</code> with the upstream repository <code>LoopKit/LoopWorkspace</code> will be skipped." >> $GITHUB_STEP_SUMMARY
echo "If you want to enable automatic builds and updates for your Loop, please follow the instructions \
under the following path <code>LoopWorkspace/fastlane/testflight.md</code>." >> $GITHUB_STEP_SUMMARY

# Set a logic flag if this is the second instance of this day-of-week in this month
- name: Check if this is the second time this day-of-week happens this month
id: date-check
run: |
DAY_OF_MONTH=$(date +%-d)
WEEK_OF_MONTH=$(( ($(date +%-d) - 1) / 7 + 1 ))
if [[ $WEEK_OF_MONTH -eq 2 ]]; then
echo "is_second_instance=true" >> "$GITHUB_OUTPUT"
else
echo "is_second_instance=false" >> "$GITHUB_OUTPUT"
fi

# When a scheduled run finds nothing new to build, cancel the run so it shows
# as "cancelled" instead of "success" — a green check should always mean a
# new build was made.
cancel_when_no_build:
needs: check_status
name: Cancel run when there is nothing to build
runs-on: ubuntu-latest
permissions:
actions: write
if: |
github.event_name == 'schedule' &&
!(vars.SCHEDULED_BUILD != 'false' && needs.check_status.outputs.IS_SECOND_IN_MONTH == 'true') &&
!(vars.SCHEDULED_SYNC != 'false' && needs.check_status.outputs.NEW_COMMITS == 'true')
steps:
- name: Cancel the run
env:
GH_TOKEN: ${{ github.token }}
run: |
echo "Repository is up to date and no monthly build is due — cancelling this run so it is not reported as a successful build." >> "$GITHUB_STEP_SUMMARY"
gh run cancel ${{ github.run_id }} --repo ${{ github.repository }}
# Keep the job alive until the cancellation lands, so the run ends as
# "cancelled" rather than completing successfully first.
sleep 300

# Checks if Distribution certificate is present and valid, optionally nukes and
# creates new certs if the repository variable ENABLE_NUKE_CERTS == 'true'
# only run if a build is planned
check_certs:
needs: [check_status]
name: Check certificates
uses: ./.github/workflows/create_certs.yml
secrets: inherit
if: |
github.event_name == 'workflow_dispatch' ||
(vars.SCHEDULED_BUILD != 'false' && needs.check_status.outputs.IS_SECOND_IN_MONTH == 'true') ||
(vars.SCHEDULED_SYNC != 'false' && needs.check_status.outputs.NEW_COMMITS == 'true' )

# Builds Loop
build:
name: Build
needs: [check_certs, check_status]
runs-on: macos-26
permissions:
contents: write
if:
| # builds with manual start; if scheduled: once a month or when new commits are found
github.event_name == 'workflow_dispatch' ||
(vars.SCHEDULED_BUILD != 'false' && needs.check_status.outputs.IS_SECOND_IN_MONTH == 'true') ||
(vars.SCHEDULED_SYNC != 'false' && needs.check_status.outputs.NEW_COMMITS == 'true' )
steps:
- name: Select Xcode version
run: "sudo xcode-select --switch /Applications/Xcode_26.5.app/Contents/Developer"

- name: Checkout Repo for building
uses: actions/checkout@v5
with:
token: ${{ secrets.GH_PAT }}
submodules: recursive
ref: ${{ env.TARGET_BRANCH }}

# Customize Loop: Download and apply patches
- name: Customize Loop
run: |

# LoopWorkspace patches
# -applies any patches located in the LoopWorkspace/patches/ directory
if $(ls ./patches/* &> /dev/null); then
git apply ./patches/* --allow-empty -v --whitespace=fix
fi

# Submodule Loop patches:
# Template for customizing submodule Loop (changes Loop app name to "CustomLoop")
# Remove the "#" sign from the beginning of the line below to activate:
#curl https://github.com/loopnlearn/Loop/commit/d206432b024279ef710df462b20bd464cd9682d4.patch | git apply --directory=Loop -v --whitespace=fix

# Submodule LoopKit patches:
# General template for customizing submodule LoopKit
# Copy url from a GitHub commit or pull request and insert below, and remove the "#" sign from the beginning of the line to activate:
#curl url_to_github_commit.patch | git apply --directory=LoopKit -v --whitespace=fix

# Submodule xxxxx patches:

# Add patches for customization of additional submodules by following the templates above,
# and make sure to specify the submodule by setting "--directory=(submodule_name)".
# Several patches may be added per submodule.
# Adding comments (#) may be useful to easily tell the individual patches apart.

# Patch Fastlane Match to not print tables
- name: Patch Match Tables
run: |
TABLE_PRINTER_PATH=$(ruby -e 'puts Gem::Specification.find_by_name("fastlane").gem_dir')/match/lib/match/table_printer.rb
if [ -f "$TABLE_PRINTER_PATH" ]; then
sed -i "" "/puts(Terminal::Table.new(params))/d" "$TABLE_PRINTER_PATH"
else
echo "table_printer.rb not found"
exit 1
fi

# Install project dependencies
- name: Install Project Dependencies
run: bundle install

# Sync the GitHub runner clock with the Windows time server (workaround as suggested in https://github.com/actions/runner/issues/2996)
- name: Sync clock
run: sudo sntp -sS time.windows.com

# Build signed Loop IPA file
- name: Fastlane Build & Archive
run: bundle exec fastlane build_loop
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: bundle exec 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@v6
with:
name: build-artifacts
path: |
artifacts
buildlog
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,15 @@ xcuserdata/
*.xcscmblueprint
*.xcuserstate
.DS_Store
.claude

## Obj-C/Swift specific
*.hmap
*.ipa


## Playgrounds
*.playground
playground.xcworkspace
timeline.xctimeline

22 changes: 17 additions & 5 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,24 @@
[submodule "LibreTransmitter"]
path = LibreTransmitter
url = https://github.com/LoopKit/LibreTransmitter.git
[submodule "LibreLoop"]
path = LibreLoop
url = https://github.com/LoopKit/LibreLoop.git
[submodule "LoopAlgorithm"]
path = LoopAlgorithm
url = https://github.com/LoopKit/LoopAlgorithm.git
[submodule "LibreCRKit"]
path = LibreCRKit
url = https://github.com/airedev326/LibreCRKit.git
[submodule "OmnipodKit"]
path = OmnipodKit
url = https://github.com/loopandlearn/OmnipodKit
[submodule "MedtrumKit"]
path = MedtrumKit
url = https://github.com/jbr7rr/MedtrumKit
url = https://github.com/loopandlearn/OmnipodKit.git
branch = next-dev
[submodule "EversenseKit"]
path = EversenseKit
url = https://github.com/bastiaanv/EversenseKit
url = https://github.com/LoopKit/EversenseKit.git
branch = loop-next-dev
[submodule "MedtrumKit"]
path = MedtrumKit
url = https://github.com/LoopKit/MedtrumKit.git
branch = next-dev
2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
source "https://rubygems.org"
gem "fastlane", "2.235.0"
gem "fastlane", "2.237.0"
Loading