Skip to content

Latest commit

Β 

History

History
291 lines (230 loc) Β· 11.4 KB

File metadata and controls

291 lines (230 loc) Β· 11.4 KB

LoopWorkspace Architecture

Generated by Tom 🐒 β€” 2026-03-15 For Justin's fork: https://github.com/justinr1234/LoopWorkspace

Overview

Loop is an open-source iOS automated insulin delivery (AID) system. It runs on iPhone, reads CGM data (Dexcom G7, Libre, etc.), communicates with insulin pumps (Omnipod, Minimed), and uses an algorithm to automate insulin dosing. LoopWorkspace is the umbrella repository that ties together ~20 submodule repos into a single Xcode workspace.

Current version: 3.13.0 (build 57) Default branch: dev Upstream: LoopKit/LoopWorkspace


Repository Structure

Top-Level Layout

LoopWorkspace/
β”œβ”€β”€ .circleci/              # CircleCI config (build + test on simulator)
β”œβ”€β”€ .github/workflows/      # GitHub Actions (build β†’ TestFlight pipeline)
β”œβ”€β”€ fastlane/               # Fastlane config (signing, build, upload)
β”œβ”€β”€ patches/                # ⭐ Custom patches applied during CI build
β”œβ”€β”€ Scripts/                # Translation/localization tooling
β”œβ”€β”€ docs/                   # Single screenshot
β”œβ”€β”€ LoopWorkspace.xcworkspace  # Xcode workspace (references all submodules)
β”œβ”€β”€ LoopConfigOverride.xcconfig # Build config overrides (team ID, features, bundle ID)
β”œβ”€β”€ VersionOverride.xcconfig    # Version number (3.13.0 / build 57)
β”œβ”€β”€ InfoCustomizations.txt      # Service config (TidepoolServiceClientId)
β”œβ”€β”€ OverrideAssetsLoop.xcassets      # Custom app icon overrides
β”œβ”€β”€ OverrideAssetsWatchApp.xcassets  # Custom watch app icon overrides
β”œβ”€β”€ Gemfile / Gemfile.lock  # Ruby deps for fastlane
└── README.md

Submodules (20 total)

All submodules point to LoopKit/ GitHub org repos. They fall into these categories:

Core App

Submodule Branch Description
Loop dev Main iOS app β€” UI, dosing algorithm, data management
LoopKit dev Core framework β€” types, protocols, data models, stores
LoopOnboarding dev First-run onboarding flow
LoopSupport dev Support/diagnostic utilities

CGM (Continuous Glucose Monitor) Drivers

Submodule Branch Description
CGMBLEKit dev Dexcom G5/G6 BLE communication
G7SensorKit main Dexcom G7 support
dexcom-share-client-swift dev Dexcom Share cloud API client
NightscoutRemoteCGM dev Nightscout as CGM data source
LibreTransmitter main FreeStyle Libre sensor support

Pump Drivers

Submodule Branch Description
OmniBLE dev Omnipod DASH (BLE) communication
OmniKit main Omnipod Eros communication
MinimedKit main Medtronic Minimed pump support
RileyLinkKit dev RileyLink BLE bridge (for Eros/Minimed)

Services / Integrations

Submodule Branch Description
NightscoutService dev Nightscout data upload
TidepoolService dev Tidepool data platform integration
AmplitudeService dev Analytics (Amplitude)
LogglyService dev Logging (Loggly)
MixpanelService dev Analytics (Mixpanel)

Utilities

Submodule Branch Description
TrueTime.swift dev NTP time synchronization
Minizip dev Zip/unzip utility

Build Process

Local Build (Xcode)

  1. Clone with --recurse-submodules
  2. Open LoopWorkspace.xcworkspace in Xcode
  3. Set LOOP_DEVELOPMENT_TEAM in LoopConfigOverride.xcconfig
  4. Select the LoopWorkspace scheme (not the "Loop" scheme)
  5. Build/Run

Key config files:

  • LoopConfigOverride.xcconfig β€” Team ID, bundle ID, feature flags
  • VersionOverride.xcconfig β€” Marketing version (3.13.0) and build number (57)
  • Feature flags: EXPERIMENTAL_FEATURES_ENABLED, SIMULATORS_ENABLED, ALLOW_ALGORITHM_EXPERIMENTS, DEBUG_FEATURES_ENABLED

Browser Build (GitHub Actions β†’ TestFlight)

This is the primary distribution method. No Mac needed.

Prerequisites:

  • Apple Developer account ($99/year)
  • 6 secrets configured in GitHub repo settings:
    • TEAMID β€” Apple Developer Team ID
    • FASTLANE_KEY_ID β€” App Store Connect API key ID
    • FASTLANE_ISSUER_ID β€” App Store Connect API issuer
    • FASTLANE_KEY β€” API private key content
    • GH_PAT β€” GitHub Personal Access Token (with repo + workflow scopes)
    • MATCH_PASSWORD β€” Password for Match-Secrets repo (cert storage)

CI/CD Pipeline

GitHub Actions Workflows

1. Validate Secrets (validate_secrets.yml)

  • Manual trigger only
  • Validates GH_PAT format and permissions
  • Tests App Store Connect API key
  • Checks Match certificate storage

2. Add Identifiers (add_identifiers.yml)

  • Manual trigger only
  • Creates Apple bundle identifiers via Fastlane
  • Configures capabilities (App Groups, HealthKit, Push, Siri, NFC)

3. Create Certificates (create_certs.yml)

  • Called by build workflow or manual trigger
  • Uses fastlane match for certificate management
  • Stores certs in private Match-Secrets repo
  • Auto-renews expired distribution certificates
  • Variable ENABLE_NUKE_CERTS controls auto-cleanup

4. Build Loop (build_loop.yml) ⭐ Main Pipeline

  • Triggers:
    • Manual (workflow_dispatch)
    • Weekly cron: Sunday 7:33 UTC
  • Flow:
    1. Check status β€” Sync fork with upstream, check for new commits
    2. Build decision β€” Build if: manual trigger, new commits found, OR 2nd Sunday of month
    3. Check certs β€” Validate/renew distribution certificates
    4. Build:
      • Runs on macos-15 with Xcode 16.4
      • Checkout with submodules
      • ⭐ Apply patches from patches/ directory
      • Install Ruby deps (bundle install)
      • fastlane build_loop β€” sign and archive IPA
    5. Deploy:
      • fastlane release β€” Upload to TestFlight
      • Upload artifacts (IPA, dSYM, build log)

CircleCI (secondary)

  • Builds and tests on iOS Simulator (iPhone 16, iOS 18.5)
  • Uses Xcode 16.4
  • Runs xcodebuild build then xcodebuild test

Fastlane Lanes

Lane Purpose
build_loop Sign, build, archive Loop IPA
release Upload IPA to TestFlight
identifiers Create/configure Apple bundle IDs
certs Provision certificates via Match
validate_secrets Validate all secrets
nuke_certs Remove all certificates
check_and_renew_certificates Check cert expiry, flag if renewal needed

Upstream Sync

The build_loop.yml workflow handles upstream syncing automatically:

  1. Uses aormsby/Fork-Sync-With-Upstream-action@v3.4.1
  2. Syncs TARGET_BRANCH (current branch) from LoopKit/LoopWorkspace same branch
  3. Only runs when github.repository_owner != 'LoopKit' (i.e., on forks)
  4. Controlled by SCHEDULED_SYNC variable (default: enabled)
  5. After sync, checks if new commits were found β†’ triggers build if so

Important: Upstream sync uses shallow clone (6 months ago). The fork's branch is fast-forwarded to match upstream.


⭐ Patch System (ALREADY EXISTS!)

The build pipeline has a built-in patch system in the "Customize Loop" step:

# 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 patches (via curl from GitHub commits/PRs):
# curl https://github.com/.../commit.patch | git apply --directory=Loop -v --whitespace=fix

How It Works

  1. Workspace-level patches: Drop .patch files in patches/ directory β€” applied with git apply
  2. Submodule patches: Use curl to fetch patches from GitHub commits/PRs, apply with --directory=SubmoduleName
  3. Patches are applied after checkout but before build
  4. This means patches survive upstream syncs β€” they're reapplied every build

Current State

The patches/ directory exists but is empty (just has save_patches_here.md).


What We Need for a Custom Patch Pipeline

The Good News

The infrastructure already exists! We just need to:

  1. Create patches β€” Generate .patch files for our customizations
  2. Drop them in patches/ β€” Workspace-level changes
  3. Add curl | git apply --directory=X lines β€” For submodule changes
  4. Commit to our fork β€” Patches persist across upstream syncs

Proposed Workflow

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Upstream    β”‚     β”‚  Justin's Fork   β”‚     β”‚   TestFlight    β”‚
β”‚  LoopKit/   │────▢│  justinr1234/    │────▢│   (iPhone)      β”‚
β”‚  LoopWorkspace    β”‚  LoopWorkspace   β”‚     β”‚                 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                          β”‚
                    patches/ directory
                    + custom curl lines
                    in build_loop.yml

Implementation Plan

Phase 2: Set Up Build Secrets

  • Justin needs to configure the 6 secrets in his fork's GitHub settings
  • Create App Store Connect API key if not already done
  • Set up Match-Secrets repository

Phase 3: Create First Patch

  • Identify a desired customization (e.g., custom Nightscout URL, algorithm tweaks)
  • Generate a .patch file
  • Test locally with git apply
  • Commit to patches/ directory
  • Trigger a build

Phase 4: Tom-Managed Patch Pipeline

  • Tom can create/modify patches and commit them to the fork
  • Each push triggers a build β†’ TestFlight
  • Justin installs updated Loop from TestFlight

Patch Generation Cheat Sheet

# Generate a patch for workspace-level changes
cd ~/p/LoopWorkspace
# Make changes...
git diff > patches/my-customization.patch

# Generate a patch for submodule changes
cd ~/p/LoopWorkspace/Loop
# Make changes...
git diff > ../patches/loop-custom.patch
# Note: apply with --directory=Loop in build_loop.yml

# Or reference a GitHub commit directly in build_loop.yml:
# curl https://github.com/user/Loop/commit/abc123.patch | git apply --directory=Loop -v --whitespace=fix

Key Considerations

  1. Patch conflicts: When upstream updates, patches might fail to apply. The build will fail, alerting us to update patches.
  2. Submodule patches are trickier: Since submodules auto-update, patches against them may break more often. Consider:
    • Pinning submodule versions (fork the submodule too)
    • Using broader context in patches (-C3 or more)
    • Keeping patches minimal and targeted
  3. Testing: CircleCI runs tests on every push β€” use this to validate patches before they hit TestFlight.
  4. 90-day rule: TestFlight builds expire after 90 days. The auto-build system handles this, but custom patches must not break the build.

Relevant Links