Skip to content

Commit 796d36e

Browse files
authored
Merge pull request nightscout#4444 from nightscout/release34
Release 3.4.0.0
2 parents 47bd2a1 + 438c2d1 commit 796d36e

3,409 files changed

Lines changed: 118362 additions & 42029 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.circleci/config.yml

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,19 @@ jobs:
1616
steps:
1717
- checkout
1818

19+
- run:
20+
name: Run compileFullDebugAndroidTestSources
21+
command: |
22+
export ANDROID_SDK_ROOT=/usr/lib/android-sdk
23+
export ANDROID_HOME=/usr/lib/android-sdk
24+
env
25+
./gradlew \
26+
-Dorg.gradle.jvmargs="-Xmx8g -XX:+UseParallelGC -Xss1024m" \
27+
-Dkotlin.daemon.jvm.options="-Xmx2g" \
28+
-Dkotlin.compiler.execution.strategy="in-process" \
29+
-Dorg.gradle.daemon=true \
30+
compileFullDebugAndroidTestSources
31+
1932
- run:
2033
name: Create avd
2134
command: |
@@ -35,7 +48,12 @@ jobs:
3548
export ANDROID_SDK_ROOT=/usr/lib/android-sdk
3649
export ANDROID_HOME=/usr/lib/android-sdk
3750
env
38-
./gradlew -Dorg.gradle.jvmargs=-Xmx6g connectedFullDebugAndroidTest
51+
./gradlew \
52+
-Dorg.gradle.jvmargs="-Xmx8g -XX:+UseParallelGC -Xss1024m" \
53+
-Dkotlin.daemon.jvm.options="-Xmx2g" \
54+
-Dkotlin.compiler.execution.strategy="in-process" \
55+
-Dorg.gradle.daemon=true \
56+
connectedFullDebugAndroidTest
3957
4058
- run:
4159
name: Kill emulators
@@ -48,7 +66,13 @@ jobs:
4866
command: |
4967
export ANDROID_SDK_ROOT=/usr/lib/android-sdk
5068
export ANDROID_HOME=/usr/lib/android-sdk
51-
./gradlew -Dorg.gradle.jvmargs=-Xmx6g testFullDebugUnitTest
69+
./gradlew \
70+
-Dorg.gradle.jvmargs="-Xmx8g -XX:+UseParallelGC -Xss1024m" \
71+
-Dkotlin.daemon.jvm.options="-Xmx2g" \
72+
-Dkotlin.compiler.execution.strategy="in-process" \
73+
-Dorg.gradle.workers.max=22 \
74+
-Dorg.gradle.daemon=true \
75+
testFullDebugUnitTest
5276
5377
- run:
5478
run: Run jacocoAllDebugReport

.github/workflows/aaps-ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ jobs:
188188

189189
- name: Build APKs
190190
run: |
191-
./gradlew assemble${{ env.BUILD_VARIANT }} \
191+
./gradlew :app:assemble${{ env.BUILD_VARIANT }} :wear:assemble${{ env.BUILD_VARIANT }} \
192192
-Dorg.gradle.jvmargs="-Xmx8g -XX:+UseParallelGC -Xss1024m" \
193193
-Dkotlin.daemon.jvm.options="-Xmx2g" \
194194
-Dkotlin.compiler.execution.strategy="in-process" \

.github/workflows/cherry-pick-ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ jobs:
218218

219219
- name: Build APKs
220220
run: |
221-
./gradlew assemble${{ env.BUILD_VARIANT }} \
221+
./gradlew :app:assemble${{ env.BUILD_VARIANT }} :wear:assemble${{ env.BUILD_VARIANT }} \
222222
-Dorg.gradle.jvmargs="-Xmx8g -XX:+UseParallelGC -Xss1024m" \
223223
-Dkotlin.daemon.jvm.options="-Xmx2g" \
224224
-Dkotlin.compiler.execution.strategy="in-process" \

.github/workflows/claude.yml

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
name: Claude Code
2+
3+
on:
4+
issue_comment:
5+
types: [created]
6+
pull_request_review_comment:
7+
types: [created]
8+
issues:
9+
types: [opened, assigned]
10+
pull_request_review:
11+
types: [submitted]
12+
13+
jobs:
14+
claude:
15+
if: |
16+
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
17+
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
18+
(github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
19+
(github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))
20+
runs-on: ubuntu-latest
21+
permissions:
22+
contents: read
23+
pull-requests: write
24+
issues: read
25+
id-token: write
26+
actions: read # Required for Claude to read CI results on PRs
27+
steps:
28+
- name: Checkout repository
29+
uses: actions/checkout@v4
30+
with:
31+
fetch-depth: 1
32+
33+
- name: Run Claude Code
34+
id: claude
35+
uses: anthropics/claude-code-action@v1
36+
with:
37+
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
38+
39+
# This is an optional setting that allows Claude to read CI results on PRs
40+
additional_permissions: |
41+
actions: read
42+
43+
# Optional: Give a custom prompt to Claude. If this is not specified, Claude will perform the instructions specified in the comment that tagged it.
44+
# prompt: 'Update the pull request description to include a summary of changes.'
45+
46+
# Optional: Add claude_args to customize behavior and configuration
47+
# See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
48+
# or https://docs.claude.com/en/docs/claude-code/cli-reference for available options
49+
# claude_args: '--allowed-tools Bash(gh pr:*)'
50+
Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
name: CI Keystore Export
2+
3+
on:
4+
workflow_dispatch:
5+
6+
jobs:
7+
build:
8+
name: KeyStore Export
9+
runs-on: ubuntu-latest
10+
steps:
11+
- name: Decode Secrets Keystore Set and Oauth2 ZIP_PASSWORD to Env
12+
run: |
13+
if [ -n "${{ secrets.KEYSTORE_SET }}" ]; then
14+
echo "🔐 Decoding KEYSTORE_SET..."
15+
DECODED=$(echo "${{ secrets.KEYSTORE_SET }}" | base64 -d)
16+
17+
KEYSTORE_BASE64=$(echo "$DECODED" | cut -d'|' -f1)
18+
KEYSTORE_PASSWORD=$(echo "$DECODED" | cut -d'|' -f2)
19+
KEY_ALIAS=$(echo "$DECODED" | cut -d'|' -f3)
20+
KEY_PASSWORD=$(echo "$DECODED" | cut -d'|' -f4)
21+
22+
echo "KEYSTORE_BASE64=$KEYSTORE_BASE64" >> $GITHUB_ENV
23+
echo "KEYSTORE_PASSWORD=$KEYSTORE_PASSWORD" >> $GITHUB_ENV
24+
echo "KEY_ALIAS=$KEY_ALIAS" >> $GITHUB_ENV
25+
echo "KEY_PASSWORD=$KEY_PASSWORD" >> $GITHUB_ENV
26+
echo "KEYSTORE_SET=${{ secrets.KEYSTORE_SET }}" >> $GITHUB_ENV
27+
28+
echo "::add-mask::$KEYSTORE_BASE64"
29+
echo "::add-mask::$KEYSTORE_PASSWORD"
30+
echo "::add-mask::$KEY_ALIAS"
31+
echo "::add-mask::$KEY_PASSWORD"
32+
33+
echo "✅ Keystore parameters extracted from KEYSTORE_SET"
34+
else
35+
echo "ℹ️ KEYSTORE_SET not provided, using separate secrets."
36+
echo "KEYSTORE_BASE64=${{ secrets.KEYSTORE_BASE64 }}" >> $GITHUB_ENV
37+
echo "KEYSTORE_PASSWORD=${{ secrets.KEYSTORE_PASSWORD }}" >> $GITHUB_ENV
38+
echo "KEY_ALIAS=${{ secrets.KEY_ALIAS }}" >> $GITHUB_ENV
39+
echo "KEY_PASSWORD=${{ secrets.KEY_PASSWORD }}" >> $GITHUB_ENV
40+
fi
41+
echo "GDRIVE_OAUTH2=${{ secrets.GDRIVE_OAUTH2 }}" >> $GITHUB_ENV
42+
echo "ZIP_PASSWORD=${{ secrets.ZIP_PASSWORD }}" >> $GITHUB_ENV
43+
44+
- name: Check Secrets
45+
run: |
46+
echo "🔍 Checking required secrets..."
47+
MISSING=0
48+
49+
check_secret() {
50+
if [ -z "$1" ]; then
51+
echo "❌ Missing secret: $2"
52+
MISSING=1
53+
fi
54+
}
55+
56+
# Check secrets
57+
check_secret "$GDRIVE_OAUTH2" "GDRIVE_OAUTH2"
58+
check_secret "$ZIP_PASSWORD" "ZIP_PASSWORD"
59+
60+
check_secret "$KEYSTORE_BASE64" "KEYSTORE_BASE64"
61+
check_secret "$KEYSTORE_PASSWORD" "KEYSTORE_PASSWORD"
62+
check_secret "$KEY_ALIAS" "KEY_ALIAS"
63+
check_secret "$KEY_PASSWORD" "KEY_PASSWORD"
64+
65+
if [ "$MISSING" -eq 1 ]; then
66+
echo "🛑 Missing required secrets. Stopping build."
67+
exit 1
68+
fi
69+
70+
echo "✅ All required secrets are present."
71+
72+
- name: Decode keystore file
73+
run: |
74+
mkdir -p "$RUNNER_TEMP/keystore"
75+
echo "$KEYSTORE_BASE64" | base64 -d > "$RUNNER_TEMP/keystore/keystore.jks"
76+
77+
- name: Validating keystore, alias and password
78+
run: |
79+
set -x
80+
echo "🔐 Validating keystore, alias and password"
81+
82+
# Create a dummy JAR file (quick method using zip)
83+
echo "test" > dummy.txt
84+
zip -q dummy.jar dummy.txt
85+
rm dummy.txt
86+
87+
# Attempt to validate using jarsigner
88+
JARSIGNER_LOG=$(mktemp)
89+
if ! jarsigner \
90+
-keystore "$RUNNER_TEMP/keystore/keystore.jks" \
91+
-storepass "$KEYSTORE_PASSWORD" \
92+
-keypass "$KEY_PASSWORD" \
93+
dummy.jar "$KEY_ALIAS" > "$JARSIGNER_LOG" 2>&1; then
94+
echo "❌ Either KEYSTORE_BASE64, KEYSTORE_PASSWORD, KEY_PASSWORD, or KEY_ALIAS is incorrect"
95+
echo "🔍 jarsigner error output:"
96+
cat "$JARSIGNER_LOG"
97+
rm -f "$JARSIGNER_LOG" dummy.jar
98+
exit 1
99+
fi
100+
rm -f "$JARSIGNER_LOG" dummy.jar
101+
echo "✅ Keystore, alias, and key password are valid."
102+
103+
rm -f "$KEYTOOL_LOG"
104+
echo "✅ Keystore and credentials validated."
105+
106+
- name: Decode GDrive OAuth2 secrets
107+
run: |
108+
echo "🔐 Decoding GDRIVE_OAUTH2..."
109+
DECODED=$(echo "${{ secrets.GDRIVE_OAUTH2 }}" | base64 -d)
110+
111+
GDRIVE_CLIENT_ID=$(echo "$DECODED" | cut -d'|' -f1)
112+
GDRIVE_REFRESH_TOKEN=$(echo "$DECODED" | cut -d'|' -f2)
113+
114+
echo "::add-mask::$GDRIVE_CLIENT_ID"
115+
echo "::add-mask::$GDRIVE_REFRESH_TOKEN"
116+
117+
echo "GDRIVE_CLIENT_ID=$GDRIVE_CLIENT_ID" >> $GITHUB_ENV
118+
echo "GDRIVE_REFRESH_TOKEN=$GDRIVE_REFRESH_TOKEN" >> $GITHUB_ENV
119+
120+
echo "✅ GDRIVE_CLIENT_ID and GDRIVE_REFRESH_TOKEN extracted from GDRIVE_OAUTH2"
121+
122+
- name: Retrieving Google Drive access token
123+
run: |
124+
echo "🔐 Getting Google OAuth2 access token..."
125+
TOKEN_RESPONSE=$(curl -s -X POST https://oauth2.googleapis.com/token \
126+
-d client_id="$GDRIVE_CLIENT_ID" \
127+
-d refresh_token="$GDRIVE_REFRESH_TOKEN" \
128+
-d grant_type=refresh_token)
129+
ACCESS_TOKEN=$(echo "$TOKEN_RESPONSE" | jq -r .access_token)
130+
echo "::add-mask::$ACCESS_TOKEN"
131+
if [ -z "$ACCESS_TOKEN" ] || [ "$ACCESS_TOKEN" = "null" ]; then
132+
echo "❌ Failed to get access token."
133+
echo "$TOKEN_RESPONSE"
134+
exit 1
135+
fi
136+
echo "ACCESS_TOKEN=$ACCESS_TOKEN" >> $GITHUB_ENV
137+
echo "✅ Access token obtained."
138+
139+
- name: Encrypt the keystore using zip.
140+
run: |
141+
echo "🔐 Creating encrypted zip file from keystore..."
142+
cd "$RUNNER_TEMP/keystore"
143+
144+
# Create keyStoreInfo.txt with keystore passwords and alias
145+
echo "Creating keyStoreInfo.txt with keystore information..."
146+
cat > keyStoreInfo.txt << EOF
147+
KEYSTORE_PASSWORD=$KEYSTORE_PASSWORD
148+
KEY_PASSWORD=$KEY_PASSWORD
149+
ALIAS=$KEY_ALIAS
150+
EOF
151+
152+
# Create keystore_set.txt with KEYSTORE_SET content (only if KEYSTORE_SET has value)
153+
ZIP_FILES="keystore.jks keyStoreInfo.txt"
154+
if [ -n "$KEYSTORE_SET" ]; then
155+
echo "Creating keystore_set.txt with KEYSTORE_SET content..."
156+
echo "$KEYSTORE_SET" > keystore_set.txt
157+
ZIP_FILES="$ZIP_FILES keystore_set.txt"
158+
echo "📋 KEYSTORE_SET found, will include keystore_set.txt"
159+
else
160+
echo "📋 KEYSTORE_SET not found, skipping keystore_set.txt"
161+
fi
162+
163+
# Create encrypted zip with available files
164+
echo "📦 Adding files to encrypted zip: $ZIP_FILES"
165+
echo "🔐 Using password protection for zip file..."
166+
167+
# Use ZIP_PASSWORD secret directly with command substitution to avoid env var exposure
168+
set +x # Disable command echoing to prevent password leakage
169+
ZIP_ERROR_LOG=$(mktemp)
170+
if ! zip -P "$ZIP_PASSWORD" keystore.zip $ZIP_FILES > /dev/null 2>"$ZIP_ERROR_LOG"; then
171+
set -x # Re-enable command echoing for error output
172+
echo "❌ Failed to create encrypted zip file"
173+
echo "🔍 zip command error output:"
174+
cat "$ZIP_ERROR_LOG"
175+
rm -f "$ZIP_ERROR_LOG"
176+
exit 1
177+
fi
178+
rm -f "$ZIP_ERROR_LOG"
179+
set -x # Re-enable command echoing
180+
181+
echo "✅ Keystore and info files encrypted successfully as keystore.zip"
182+
echo "📋 Files included: $ZIP_FILES"
183+
184+
- name: Upload APKs to Google Drive
185+
run: |
186+
set -e
187+
echo "🔐 Start uploading APKs to Google Drive..."
188+
189+
echo "📁 Checking or creating AAPS folder"
190+
AAPS_FOLDER_ID=$(curl -s -X GET \
191+
-H "Authorization: Bearer $ACCESS_TOKEN" \
192+
"https://www.googleapis.com/drive/v3/files?q=name='AAPS'+and+mimeType='application/vnd.google-apps.folder'+and+trashed=false" \
193+
| jq -r '.files[0].id')
194+
195+
if [ "$AAPS_FOLDER_ID" == "null" ] || [ -z "$AAPS_FOLDER_ID" ]; then
196+
AAPS_FOLDER_ID=$(curl -s -X POST \
197+
-H "Authorization: Bearer $ACCESS_TOKEN" \
198+
-H "Content-Type: application/json" \
199+
-d '{"name": "AAPS", "mimeType": "application/vnd.google-apps.folder"}' \
200+
"https://www.googleapis.com/drive/v3/files" | jq -r '.id')
201+
echo "📂 Created AAPS folder: $AAPS_FOLDER_ID"
202+
else
203+
echo "📂 Found AAPS folder: $AAPS_FOLDER_ID"
204+
fi
205+
206+
echo "📁 Checking or creating KeyStore folder"
207+
KEYSTORE_FOLDER_ID=$(curl -s -X GET \
208+
-H "Authorization: Bearer $ACCESS_TOKEN" \
209+
"https://www.googleapis.com/drive/v3/files?q=name='KeyStore'+and+mimeType='application/vnd.google-apps.folder'+and+'$AAPS_FOLDER_ID'+in+parents+and+trashed=false" \
210+
| jq -r '.files[0].id')
211+
212+
if [ "$KEYSTORE_FOLDER_ID" == "null" ] || [ -z "$KEYSTORE_FOLDER_ID" ]; then
213+
KEYSTORE_FOLDER_ID=$(curl -s -X POST \
214+
-H "Authorization: Bearer $ACCESS_TOKEN" \
215+
-H "Content-Type: application/json" \
216+
-d "{\"name\": \"KeyStore\", \"mimeType\": \"application/vnd.google-apps.folder\", \"parents\": [\"$AAPS_FOLDER_ID\"]}" \
217+
"https://www.googleapis.com/drive/v3/files" | jq -r '.id')
218+
echo "📂 Created KeyStore folder"
219+
else
220+
echo "📂 Found KeyStore folder"
221+
fi
222+
223+
upload_to_gdrive() {
224+
FILE=$1
225+
NAME=$2
226+
if [ ! -f "$FILE" ]; then
227+
echo "❌ File not found: $FILE"
228+
exit 26
229+
fi
230+
231+
echo "📄 Checking if file $NAME already exists in Google Drive..."
232+
QUERY="name='${NAME}' and '${KEYSTORE_FOLDER_ID}' in parents and trashed=false"
233+
ENCODED_QUERY=$(python3 -c "import urllib.parse; print(urllib.parse.quote('''$QUERY'''))")
234+
FILE_ID=$(curl -s \
235+
-H "Authorization: Bearer $ACCESS_TOKEN" \
236+
"https://www.googleapis.com/drive/v3/files?q=${ENCODED_QUERY}&fields=files(id)" \
237+
| jq -r '.files[0].id')
238+
239+
if [[ -n "$FILE_ID" && "$FILE_ID" != "null" ]]; then
240+
echo "🗑️ Deleting existing file with ID: $FILE_ID"
241+
curl -s -X DELETE \
242+
-H "Authorization: Bearer $ACCESS_TOKEN" \
243+
"https://www.googleapis.com/drive/v3/files/${FILE_ID}"
244+
fi
245+
246+
echo "⬆️ Uploading $FILE as $NAME to Google Drive..."
247+
RESPONSE=$(curl -s -w "%{http_code}" -o /tmp/gdrive_response.json \
248+
-X POST \
249+
-H "Authorization: Bearer $ACCESS_TOKEN" \
250+
-F "metadata={\"name\":\"$NAME\", \"parents\":[\"$KEYSTORE_FOLDER_ID\"]};type=application/json;charset=UTF-8" \
251+
-F "file=@$FILE;type=application/zip" \
252+
"https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart")
253+
254+
HTTP_CODE="${RESPONSE: -3}"
255+
if [[ "$HTTP_CODE" != "200" && "$HTTP_CODE" != "201" ]]; then
256+
echo "❌ Upload failed with HTTP status: $HTTP_CODE"
257+
cat /tmp/gdrive_response.json
258+
exit 1
259+
fi
260+
261+
echo "✅ Uploaded: $NAME"
262+
}
263+
264+
# Upload the encrypted keystore zip file
265+
upload_to_gdrive "$RUNNER_TEMP/keystore/keystore.zip" "keystore-$(date +%Y%m%d-%H%M%S).zip"
266+
267+
echo "🎉 Encrypted keystore successfully uploaded to Google Drive!"

.github/workflows/pr-ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ jobs:
224224

225225
- name: Build APKs
226226
run: |
227-
./gradlew assemble${{ env.BUILD_VARIANT }} \
227+
./gradlew :app:assemble${{ env.BUILD_VARIANT }} :wear:assemble${{ env.BUILD_VARIANT }} \
228228
-Dorg.gradle.jvmargs="-Xmx8g -XX:+UseParallelGC -Xss1024m" \
229229
-Dkotlin.daemon.jvm.options="-Xmx2g" \
230230
-Dkotlin.compiler.execution.strategy="in-process" \

0 commit comments

Comments
 (0)