Explorar el Código

Merge branch 'beta-fixes-2nd-iteration' into beta-fixes-2nd-iteration

Andreas Stokholm hace 1 año
padre
commit
cc063188e6

+ 2 - 1
.github/ISSUE_TEMPLATE/bug-report.md

@@ -2,7 +2,8 @@
 name: "\U0001F41B Bug report"
 about: Create a report to help us fix things
 title: ''
-labels: ['bug', 'needs-triage']
+labels: ['needs-triage']
+type: "bug"
 assignees: ''
 projects: ['nightscout/2']
 

+ 2 - 1
.github/ISSUE_TEMPLATE/feature-request.md

@@ -2,7 +2,8 @@
 name: "\U0001F4A1 Feature request \U0001F4A1"
 about: Suggest an idea for this project
 title: ''
-labels: ['enhancement', 'needs-triage']
+labels: ['needs-triage']
+types: "feature"
 assignees: ''
 projects: ['nightscout/2']
 

+ 12 - 9
.github/workflows/build_trio.yml

@@ -10,7 +10,7 @@ on:
     - cron: "0 8 * * 3" # Checks for updates at 08:00 UTC every Wednesday
     - cron: "0 6 1 * *" # Builds the app on the 1st of every month at 06:00 UTC
 
-env:  
+env:
   UPSTREAM_REPO: nightscout/Trio
   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, and target branch on upstream to be kept alive (replace with specific branch name if needed)
@@ -18,15 +18,17 @@ env:
   ALIVE_BRANCH_DEV: alive-dev
 
 jobs:
-  validate:
-    name: Validate
-    uses: ./.github/workflows/validate_secrets.yml
+  # Checks if Distribution certificate is present and valid, optionally nukes and
+  # creates new certs if the repository variable ENABLE_NUKE_CERTS == 'true'
+  check_certs:
+    name: Check certificates
+    uses: ./.github/workflows/create_certs.yml
     secrets: inherit
 
   # Checks if GH_PAT holds workflow permissions
   # Checks for existence of alive branch; if non-existent creates it
   check_alive_and_permissions:
-    needs: validate
+    needs: check_certs
     runs-on: ubuntu-latest
     name: Check alive branch and permissions
     permissions:
@@ -96,7 +98,7 @@ jobs:
   # Checks for changes in upstream repository; if changes exist prompts sync for build
   # Performs keepalive to avoid stale fork
   check_latest_from_upstream:
-    needs: [validate, check_alive_and_permissions]
+    needs: [check_certs, check_alive_and_permissions]
     runs-on: ubuntu-latest
     name: Check upstream and keep alive
     outputs:
@@ -181,11 +183,12 @@ jobs:
           echo "Synchronizing your fork of <code>Trio</code> with the upstream repository <code>nightscout/Trio</code> will be skipped." >> $GITHUB_STEP_SUMMARY
           echo "If you want to enable automatic builds and updates for your Trio, please follow the instructions \
               under the following path <code>Trio/fastlane/testflight.md</code>." >> $GITHUB_STEP_SUMMARY
-  
+
   # Builds Trio
   build:
     name: Build
-    needs: [validate, check_alive_and_permissions, check_latest_from_upstream]
+    needs:
+      [check_certs, check_alive_and_permissions, check_latest_from_upstream]
     runs-on: macos-15
     permissions:
       contents: write
@@ -199,7 +202,7 @@ jobs:
     steps:
       - name: Select Xcode version
         run: "sudo xcode-select --switch /Applications/Xcode_16.0.app/Contents/Developer"
-      
+
       - name: Checkout Repo for syncing
         if: |
           needs.check_alive_and_permissions.outputs.WORKFLOW_PERMISSION == 'true' &&

+ 89 - 19
.github/workflows/create_certs.yml

@@ -1,7 +1,16 @@
 name: 3. Create Certificates
 run-name: Create Certificates (${{ github.ref_name }})
-on:
-  workflow_dispatch:
+
+on: [workflow_call, workflow_dispatch]
+
+env:
+  TEAMID: ${{ secrets.TEAMID }}
+  GH_PAT: ${{ secrets.GH_PAT }}
+  GH_TOKEN: ${{ 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 }}
 
 jobs:
   validate:
@@ -9,12 +18,14 @@ jobs:
     uses: ./.github/workflows/validate_secrets.yml
     secrets: inherit
 
-  certificates:
-    name: Create Certificates
+  create_certs:
+    name: Certificates
     needs: validate
     runs-on: macos-15
-    steps:
+    outputs:
+      new_certificate_needed: ${{ steps.set_output.outputs.new_certificate_needed }}
 
+    steps:
       # Checks-out the repo
       - name: Checkout Repo
         uses: actions/checkout@v4
@@ -34,17 +45,76 @@ jobs:
       - 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
-
-      # Create or update certificates for app
-      - name: Create Certificates
-        run: bundle exec 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 }}
+      # Create or update Distribution certificate and provisioning profiles
+      - name: Check and create or update Distribution certificate and profiles if needed
+        run: |
+          echo "Running Fastlane certs lane..."
+          bundle exec fastlane certs || true # ignore and continue on errors without annotating an exit code
+
+      - name: Check Distribution certificate and launch Nuke certificates if needed
+        run: bundle exec fastlane check_and_renew_certificates
+        id: check_certs
+
+      - name: Set output and annotations based on Fastlane result
+        id: set_output
+        run: |
+          CERT_STATUS_FILE="${{ github.workspace }}/fastlane/new_certificate_needed.txt"
+          ENABLE_NUKE_CERTS=${{ vars.ENABLE_NUKE_CERTS }}
+
+          if [ -f "$CERT_STATUS_FILE" ]; then
+            CERT_STATUS=$(cat "$CERT_STATUS_FILE" | tr -d '\n' | tr -d '\r') # Read file content and strip newlines
+            echo "new_certificate_needed: $CERT_STATUS"
+            echo "new_certificate_needed=$CERT_STATUS" >> $GITHUB_OUTPUT
+          else
+            echo "Certificate status file not found. Defaulting to false."
+            echo "new_certificate_needed=false" >> $GITHUB_OUTPUT
+          fi
+
+          # Check if ENABLE_NUKE_CERTS is not set to true when certs are valid
+          if [ "$CERT_STATUS" != "true" ] && [ "$ENABLE_NUKE_CERTS" != "true" ]; then
+            echo "::notice::🔔 Automated renewal of certificates is disabled because the repository variable ENABLE_NUKE_CERTS is not set to 'true'."
+          fi
+
+          # Check if ENABLE_NUKE_CERTS is not set to true when certs are not valid
+          if [ "$CERT_STATUS" = "true" ] && [ "$ENABLE_NUKE_CERTS" != "true" ]; then
+            echo "::error::❌ No valid distribution certificate found. Automated renewal of certificates was skipped because the repository variable ENABLE_NUKE_CERTS is not set to 'true'."
+            exit 1
+          fi
+
+          # Check if vars.FORCE_NUKE_CERTS is not set to true
+          if [ vars.FORCE_NUKE_CERTS = "true" ]; then
+            echo "::warning::‼️ Nuking of certificates was forced because the repository variable FORCE_NUKE_CERTS is set to 'true'."
+          fi
+
+  # Nuke Certs if needed, and if the repository variable ENABLE_NUKE_CERTS is set to 'true', or if FORCE_NUKE_CERTS is set to 'true', which will always force certs to be nuked
+  nuke_certs:
+    name: Nuke certificates
+    needs: [validate, create_certs]
+    runs-on: macos-14
+    if: ${{ (needs.create_certs.outputs.new_certificate_needed == 'true' && vars.ENABLE_NUKE_CERTS == 'true') || vars.FORCE_NUKE_CERTS == 'true' }}
+    steps:
+      - name: Output from step id 'check_certs'
+        run: echo "new_certificate_needed=${{ needs.create_certs.outputs.new_certificate_needed }}"
+
+      - name: Checkout repository
+        uses: actions/checkout@v4
+
+      - name: Install dependencies
+        run: bundle install
+
+      - name: Run Fastlane nuke_certs
+        run: |
+          set -e # Set error immediately after this step if error occurs
+          bundle exec fastlane nuke_certs
+
+      - name: Recreate Distribution certificate after nuking
+        run: |
+          set -e # Set error immediately after this step if error occurs
+          bundle exec fastlane certs
+
+      - name: Add success annotations for nuke and certificate recreation
+        if: ${{ success() }}
+        run: |
+          echo "::warning::⚠️ All Distribution certificates and TestFlight profiles have been revoked and recreated."
+          echo "::warning::❗️ If you have other apps being distributed by GitHub Actions / Fastlane / TestFlight that does not renew certificates automatically, please run the '3. Create Certificates' workflow for each of these apps to allow these apps to be built."
+          echo "::warning::✅ But don't worry about your existing TestFlight builds, they will keep working!"

+ 4 - 5
.github/workflows/validate_secrets.yml

@@ -178,16 +178,15 @@ jobs:
           elif ! echo "$FASTLANE_KEY" | openssl pkcs8 -nocrypt >/dev/null; then
             failed=true
             echo "::error::The FASTLANE_KEY secret is set but invalid. Verify that you copied it correctly from the API Key file (*.p8) you downloaded and try again."
-          elif ! bundle exec fastlane validate_secrets 2>&1 | tee fastlane.log; then
+          elif ! (bundle exec fastlane validate_secrets 2>&1 || true) | tee fastlane.log; then # ignore "fastlane validate_secrets" errors and continue on errors without annotating an exit code
             if grep -q "bad decrypt" fastlane.log; then
               failed=true
               echo "::error::Unable to decrypt the Match-Secrets repository using the MATCH_PASSWORD secret. Verify that it is set correctly and try again."
             elif grep -q -e "required agreement" -e "license agreement" fastlane.log; then
               failed=true
-              echo "::error::Unable to create a valid authorization token for the App Store Connect API. Verify that the latest developer program license agreement has been accepted at https://developer.apple.com/account (review and accept any updated agreement), then wait a few minutes for changes to propagate and try again."
-            elif ! grep -q -e "No code signing identity found" -e "Could not install WWDR certificate" fastlane.log; 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."
+              echo "::error::❗️ Verify that the latest developer program license agreement has been accepted at https://developer.apple.com/account (review and accept any updated agreement), then wait a few minutes for changes to take effect and try again."
+            elif grep -q "Your certificate .* is not valid" fastlane.log; then
+              echo "::notice::Your Distribution certificate is invalid or expired. Automated renewal of the certificate will be attempted."
             fi
           fi
 

+ 46 - 6
FreeAPS/Sources/Services/Network/Nightscout/NightscoutManager.swift

@@ -133,11 +133,22 @@ final class BaseNightscoutManager: NightscoutManager, Injectable {
 
         registerHandlers()
         setupNotification()
+
+        /// Ensure that Nightscout Manager holds the `lastEnactedDetermination`, if one exists, on initialization.
+        /// We have to set this here in `init()`, so there's a `lastEnactedDetermination` available after an app restart
+        /// for `uploadStatus()`, as within that fuction `lastEnactedDetermination` is reassigned at the very end of the function.
+        /// This way, we ensure the latest enacted determination is always part of `devicestatus` and avoid having instances
+        /// where the first uploaded non-enacted determination (i.e., "suggested"), lacks the "enacted" data.
+        Task {
+            async let lastEnactedDeterminationID = determinationStorage
+                .fetchLastDeterminationObjectID(predicate: NSPredicate.enactedDetermination)
+
+            self.lastEnactedDetermination = await determinationStorage
+                .getOrefDeterminationNotYetUploadedToNightscout(lastEnactedDeterminationID)
+        }
     }
 
     private func subscribe() {
-//        broadcaster.register(TempTargetsObserver.self, observer: self)
-
         _ = reachabilityManager.startListening(onQueue: processQueue) { status in
             debug(.nightscout, "Network status: \(status)")
         }
@@ -393,6 +404,24 @@ final class BaseNightscoutManager: NightscoutManager, Injectable {
         }
     }
 
+    /// Asynchronously uploads the current status to Nightscout, including OpenAPS status, pump status, and uploader details.
+    ///
+    /// This function gathers and processes various pieces of information such as the "enacted" and "suggested" determinations,
+    /// pump battery and reservoir levels, insulin-on-board (IOB), and the uploader's battery status. It ensures that only
+    /// valid determinations are uploaded by filtering out duplicates and handling unit conversions based on the user's
+    /// settings. If the status upload is successful, it updates the determination storage to mark them as uploaded.
+    ///
+    /// Key steps:
+    /// - Fetch the last unuploaded enacted and suggested determinations from the storage.
+    /// - Retrieve pump-related data such as battery, reservoir, and status.
+    /// - Parse determinations to ensure they are properly formatted for Nightscout, including unit conversions if needed.
+    /// - Construct an `OpenAPSStatus` object with relevant information for upload.
+    /// - Construct a `NightscoutStatus` object with all gathered data.
+    /// - Attempt to upload the status to Nightscout. On success, update the storage to mark determinations as uploaded.
+    /// - Schedule a task to upload pod age data separately.
+    ///
+    /// - Note: Ensure `nightscoutAPI` is initialized and `isUploadEnabled` is set to `true` before invoking this function.
+    /// - Returns: Nothing.
     func uploadStatus() async {
         guard let nightscout = nightscoutAPI, isUploadEnabled else {
             debug(.nightscout, "NS API not available or upload disabled. Aborting NS Status upload.")
@@ -463,13 +492,19 @@ final class BaseNightscoutManager: NightscoutManager, Injectable {
 
         // Gather all relevant data for OpenAPS Status
         let iob = await fetchedIOBEntry
+
+        let suggestedToUpload = modifiedSuggestedDetermination ?? lastSuggestedDetermination
+        let enactedToUpload = fetchedEnactedDetermination ?? lastEnactedDetermination
+
         let openapsStatus = OpenAPSStatus(
             iob: iob?.first,
-            suggested: modifiedSuggestedDetermination,
-            enacted: settingsManager.settings.closedLoop ? fetchedEnactedDetermination : nil,
+            suggested: suggestedToUpload,
+            enacted: settingsManager.settings.closedLoop ? enactedToUpload : nil,
             version: Bundle.main.releaseVersionNumber ?? "Unknown"
         )
 
+        debug(.nightscout, "To be uploaded openapsStatus: \(openapsStatus)")
+
         // Gather all relevant data for NS Status
         let battery = await fetchedBattery
         let reservoir = await fetchedReservoir
@@ -507,8 +542,13 @@ final class BaseNightscoutManager: NightscoutManager, Injectable {
                 await updateOrefDeterminationAsUploaded([suggested])
             }
 
-            lastEnactedDetermination = fetchedEnactedDetermination
-            lastSuggestedDetermination = fetchedSuggestedDetermination
+            if let lastEnactedDetermination = fetchedEnactedDetermination {
+                self.lastEnactedDetermination = lastEnactedDetermination
+            }
+
+            if let lastSuggestedDetermination = fetchedSuggestedDetermination {
+                self.lastSuggestedDetermination = lastSuggestedDetermination
+            }
 
             debug(.nightscout, "NSDeviceStatus with Determination uploaded")
         } catch {

+ 0 - 1
LiveActivity/Views/LiveActivityView.swift

@@ -63,7 +63,6 @@ struct LiveActivityView: View {
                             case .currentGlucose:
                                 VStack {
                                     LiveActivityBGLabelView(context: context, additionalState: detailedViewState)
-                                        .foregroundStyle(glucoseColor)
 
                                     HStack {
                                         LiveActivityGlucoseDeltaLabelView(

+ 55 - 2
fastlane/Fastfile

@@ -218,7 +218,8 @@ platform :ios do
     
     match(
       type: "appstore",
-      force: true,
+      force: false,
+      verbose: true,
       git_basic_authorization: Base64.strict_encode64("#{GITHUB_REPOSITORY_OWNER}:#{GH_PAT}"),
       app_identifier: [
         "#{BUNDLE_ID}",
@@ -272,4 +273,56 @@ platform :ios do
       git_basic_authorization: Base64.strict_encode64("#{GITHUB_REPOSITORY_OWNER}:#{GH_PAT}")
     )
   end
-end
+
+  desc "Check Certificates and Trigger Workflow for Expired or Missing Certificates"
+  lane :check_and_renew_certificates do
+    setup_ci if ENV['CI']
+    ENV["MATCH_READONLY"] = false.to_s
+  
+    # Authenticate using App Store Connect API Key
+    api_key = app_store_connect_api_key(
+      key_id: ENV["FASTLANE_KEY_ID"],
+      issuer_id: ENV["FASTLANE_ISSUER_ID"],
+      key_content: ENV["FASTLANE_KEY"] # Ensure valid key content
+    )
+  
+    # Initialize flag to track if renewal of certificates is needed
+    new_certificate_needed = false
+  
+    # Fetch all certificates
+    certificates = Spaceship::ConnectAPI::Certificate.all
+  
+    # Filter for Distribution Certificates
+    distribution_certs = certificates.select { |cert| cert.certificate_type == "DISTRIBUTION" }
+  
+    # Handle case where no distribution certificates are found
+    if distribution_certs.empty?
+      puts "No Distribution certificates found! Triggering action to create certificate."
+      new_certificate_needed = true
+    else
+      # Check for expiration
+      distribution_certs.each do |cert|
+        expiration_date = Time.parse(cert.expiration_date)
+  
+        puts "Current Distribution Certificate: #{cert.id}, Expiration date: #{expiration_date}"
+  
+        if expiration_date < Time.now
+          puts "Distribution Certificate #{cert.id} is expired! Triggering action to renew certificate."
+          new_certificate_needed = true
+        else
+          puts "Distribution certificate #{cert.id} is valid. No action required."
+        end
+      end
+    end
+  
+    # Write result to new_certificate_needed.txt
+    file_path = File.expand_path('new_certificate_needed.txt')
+    File.write(file_path, new_certificate_needed ? 'true' : 'false')  
+  
+    # Log the absolute path and contents of the new_certificate_needed.txt file
+    puts ""
+    puts "Absolute path of new_certificate_needed.txt: #{file_path}"
+    new_certificate_needed_content = File.read(file_path)
+    puts "Certificate creation or renewal needed: #{new_certificate_needed_content}"
+  end
+end

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 26 - 11
fastlane/testflight.md