Skip to content

Commit 40bb5e8

Browse files
committed
Add CICD deployment files for github actions
1 parent 6b28288 commit 40bb5e8

File tree

9 files changed

+551
-32
lines changed

9 files changed

+551
-32
lines changed

.github/workflows/build.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,16 @@ jobs:
2626

2727
- name: Build solution
2828
run: dotnet build src/Bible.Alarm.sln --configuration Release --no-restore
29+
30+
- name: Run tests
31+
run: dotnet test src/Bible.Alarm.sln --configuration Release --no-build --verbosity normal --logger "trx;LogFileName=test-results.trx" --collect:"XPlat Code Coverage"
32+
33+
- name: Upload test results
34+
uses: actions/upload-artifact@v4
35+
if: always()
36+
with:
37+
name: test-results
38+
path: |
39+
**/test-results.trx
40+
**/coverage.cobertura.xml
41+
retention-days: 30
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
name: Deploy Android to Google Play
2+
3+
on:
4+
push:
5+
branches:
6+
- stable
7+
8+
jobs:
9+
deploy-android:
10+
runs-on: ubuntu-latest
11+
12+
env:
13+
SYNCFUSION_LICENSE_KEY: ${{ secrets.SYNCFUSION_LICENSE_KEY }}
14+
15+
steps:
16+
- name: Checkout code
17+
uses: actions/checkout@v4
18+
with:
19+
ref: stable
20+
token: ${{ secrets.GITHUB_TOKEN }}
21+
22+
- name: Setup .NET
23+
uses: actions/setup-dotnet@v4
24+
with:
25+
dotnet-version: '10.0.x'
26+
27+
- name: Setup Android SDK
28+
uses: android-actions/setup-android@v3
29+
30+
- name: Setup Java
31+
uses: actions/setup-java@v4
32+
with:
33+
distribution: 'temurin'
34+
java-version: '17'
35+
36+
# Decode and setup the keystore for signing
37+
- name: Decode Keystore
38+
run: |
39+
# Use temp directory to ensure keystore is never committed
40+
KEYSTORE_PATH="${{ runner.temp }}/android-keystore.jks"
41+
echo "${{ secrets.ANDROID_KEYSTORE_BASE64 }}" | base64 --decode > "$KEYSTORE_PATH"
42+
echo "KEYSTORE_PATH=$KEYSTORE_PATH" >> $GITHUB_ENV
43+
44+
- name: Restore dependencies
45+
run: dotnet restore src/Bible.Alarm.sln
46+
47+
# Patch version using the version patcher tool
48+
- name: Patch Android Version
49+
run: |
50+
# Run version patcher for Android
51+
dotnet run --project .tools/Bible.Alarm.VersionPatcher/Bible.Alarm.VersionPatcher.csproj --configuration Release
52+
53+
# Also increment ApplicationVersion in csproj for MAUI
54+
CSPROJ_FILE="src/Bible.Alarm/Bible.Alarm.csproj"
55+
CURRENT_VERSION=$(grep -oP '(?<=<ApplicationVersion>)\d+(?=</ApplicationVersion>)' "$CSPROJ_FILE")
56+
NEW_VERSION=$((CURRENT_VERSION + 1))
57+
sed -i "s/<ApplicationVersion>$CURRENT_VERSION<\/ApplicationVersion>/<ApplicationVersion>$NEW_VERSION<\/ApplicationVersion>/" "$CSPROJ_FILE"
58+
59+
# Increment ApplicationDisplayVersion (minor version)
60+
CURRENT_DISPLAY=$(grep -oP '(?<=<ApplicationDisplayVersion>)[^<]+(?=</ApplicationDisplayVersion>)' "$CSPROJ_FILE")
61+
MAJOR=$(echo $CURRENT_DISPLAY | cut -d. -f1)
62+
MINOR=$(echo $CURRENT_DISPLAY | cut -d. -f2)
63+
NEW_MINOR=$((MINOR + 1))
64+
if [ $NEW_MINOR -gt 99 ]; then
65+
NEW_MINOR=0
66+
MAJOR=$((MAJOR + 1))
67+
fi
68+
NEW_DISPLAY="$MAJOR.$NEW_MINOR"
69+
sed -i "s/<ApplicationDisplayVersion>$CURRENT_DISPLAY<\/ApplicationDisplayVersion>/<ApplicationDisplayVersion>$NEW_DISPLAY<\/ApplicationDisplayVersion>/" "$CSPROJ_FILE"
70+
71+
echo "Updated ApplicationVersion: $CURRENT_VERSION -> $NEW_VERSION"
72+
echo "Updated ApplicationDisplayVersion: $CURRENT_DISPLAY -> $NEW_DISPLAY"
73+
74+
# Build Android release
75+
- name: Build Android Release
76+
run: |
77+
dotnet publish src/Bible.Alarm/Bible.Alarm.csproj \
78+
--configuration Release \
79+
--framework net10.0-android \
80+
-p:AndroidKeyStore=true \
81+
-p:AndroidSigningKeyStore=${{ runner.temp }}/android-keystore.jks \
82+
-p:AndroidSigningKeyAlias=${{ secrets.ANDROID_KEY_ALIAS }} \
83+
-p:AndroidSigningKeyPass=${{ secrets.ANDROID_KEY_PASSWORD }} \
84+
-p:AndroidSigningStorePass=${{ secrets.ANDROID_KEYSTORE_PASSWORD }} \
85+
-p:AndroidPackageFormat=aab \
86+
--output ./artifacts/android
87+
88+
# Upload to Google Play Store
89+
- name: Upload to Google Play
90+
uses: r0adkll/upload-google-play@v1
91+
with:
92+
serviceAccountJsonPlainText: ${{ secrets.GOOGLE_PLAY_SERVICE_ACCOUNT_JSON }}
93+
packageName: com.jthomas.info.Bible.Alarm
94+
releaseFiles: ./artifacts/android/*.aab
95+
track: production
96+
status: completed
97+
98+
# Clean up keystore
99+
- name: Clean up keystore
100+
if: always()
101+
run: rm -f "${{ runner.temp }}/android-keystore.jks"
102+
103+
# Verify no secrets are staged before committing
104+
- name: Verify no secrets in working directory
105+
run: |
106+
# Check for common secret file patterns
107+
if find . -name "*.jks" -o -name "*.keystore" -o -name "*.pfx" -o -name "*.p12" -o -name "*.p8" -o -name "*.mobileprovision" | grep -v ".git"; then
108+
echo "ERROR: Secret files found in working directory!"
109+
find . -name "*.jks" -o -name "*.keystore" -o -name "*.pfx" -o -name "*.p12" -o -name "*.p8" -o -name "*.mobileprovision" | grep -v ".git"
110+
exit 1
111+
fi
112+
echo "✓ No secret files found in working directory"
113+
114+
# Commit version changes (only specific version files)
115+
- name: Commit version changes
116+
uses: EndBug/add-and-commit@v9
117+
with:
118+
author_name: github-actions[bot]
119+
author_email: github-actions[bot]@users.noreply.github.com
120+
message: '[Android] Update app version for Play Store release'
121+
add: |
122+
src/Bible.Alarm/Bible.Alarm.csproj
123+
src/Bible.Alarm/Platforms/Android/AndroidManifest.xml
124+
# Explicitly ignore all other files to prevent accidental commits
125+
default_author: github_actions
126+
skip_dirty_check: false
127+
push: true

.github/workflows/deploy-ios.yml

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
name: Deploy iOS to App Store
2+
3+
on:
4+
push:
5+
branches:
6+
- stable
7+
8+
jobs:
9+
deploy-ios:
10+
runs-on: macos-latest
11+
12+
env:
13+
SYNCFUSION_LICENSE_KEY: ${{ secrets.SYNCFUSION_LICENSE_KEY }}
14+
15+
steps:
16+
- name: Checkout code
17+
uses: actions/checkout@v4
18+
with:
19+
ref: stable
20+
token: ${{ secrets.GITHUB_TOKEN }}
21+
22+
- name: Setup .NET
23+
uses: actions/setup-dotnet@v4
24+
with:
25+
dotnet-version: '10.0.x'
26+
27+
- name: Select Xcode version
28+
run: sudo xcode-select -s /Applications/Xcode.app/Contents/Developer
29+
30+
# Decode and install Apple certificates and provisioning profiles
31+
- name: Install Apple Certificate
32+
env:
33+
APPLE_CERTIFICATE_BASE64: ${{ secrets.APPLE_CERTIFICATE_BASE64 }}
34+
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
35+
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
36+
run: |
37+
# Create variables
38+
CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12
39+
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
40+
41+
# Decode certificate
42+
echo -n "$APPLE_CERTIFICATE_BASE64" | base64 --decode -o $CERTIFICATE_PATH
43+
44+
# Create temporary keychain
45+
security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
46+
security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
47+
security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
48+
49+
# Import certificate to keychain
50+
security import $CERTIFICATE_PATH -P "$APPLE_CERTIFICATE_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
51+
security set-key-partition-list -S apple-tool:,apple: -s -k "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
52+
security list-keychain -d user -s $KEYCHAIN_PATH
53+
54+
- name: Install Provisioning Profile
55+
env:
56+
PROVISIONING_PROFILE_BASE64: ${{ secrets.IOS_PROVISIONING_PROFILE_BASE64 }}
57+
run: |
58+
# Create Provisioning Profiles directory
59+
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
60+
61+
# Decode and install provisioning profile
62+
echo -n "$PROVISIONING_PROFILE_BASE64" | base64 --decode -o ~/Library/MobileDevice/Provisioning\ Profiles/profile.mobileprovision
63+
64+
- name: Restore dependencies
65+
run: dotnet restore src/Bible.Alarm.sln
66+
67+
# Patch version
68+
- name: Patch iOS Version
69+
run: |
70+
# Run version patcher
71+
dotnet run --project .tools/Bible.Alarm.VersionPatcher/Bible.Alarm.VersionPatcher.csproj --configuration Release
72+
73+
# Also increment ApplicationVersion in csproj for MAUI
74+
CSPROJ_FILE="src/Bible.Alarm/Bible.Alarm.csproj"
75+
CURRENT_VERSION=$(grep -oE '<ApplicationVersion>[0-9]+</ApplicationVersion>' "$CSPROJ_FILE" | grep -oE '[0-9]+')
76+
NEW_VERSION=$((CURRENT_VERSION + 1))
77+
sed -i '' "s/<ApplicationVersion>$CURRENT_VERSION<\/ApplicationVersion>/<ApplicationVersion>$NEW_VERSION<\/ApplicationVersion>/" "$CSPROJ_FILE"
78+
79+
# Increment CFBundleVersion in Info.plist
80+
PLIST_FILE="src/Bible.Alarm/Platforms/iOS/Info.plist"
81+
CURRENT_BUNDLE_VERSION=$(/usr/libexec/PlistBuddy -c "Print :CFBundleVersion" "$PLIST_FILE")
82+
MAJOR=$(echo $CURRENT_BUNDLE_VERSION | cut -d. -f1)
83+
MINOR=$(echo $CURRENT_BUNDLE_VERSION | cut -d. -f2)
84+
NEW_MINOR=$((MINOR + 1))
85+
if [ $NEW_MINOR -gt 99 ]; then
86+
NEW_MINOR=0
87+
MAJOR=$((MAJOR + 1))
88+
fi
89+
NEW_BUNDLE_VERSION="$MAJOR.$NEW_MINOR"
90+
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $NEW_BUNDLE_VERSION" "$PLIST_FILE"
91+
92+
echo "Updated ApplicationVersion: $CURRENT_VERSION -> $NEW_VERSION"
93+
echo "Updated CFBundleVersion: $CURRENT_BUNDLE_VERSION -> $NEW_BUNDLE_VERSION"
94+
95+
# Build iOS release
96+
- name: Build iOS Release
97+
run: |
98+
dotnet publish src/Bible.Alarm/Bible.Alarm.csproj \
99+
--configuration Release \
100+
--framework net10.0-ios \
101+
-p:ArchiveOnBuild=true \
102+
-p:CodesignKey="${{ secrets.APPLE_CODESIGN_IDENTITY }}" \
103+
-p:CodesignProvision="${{ secrets.IOS_PROVISIONING_PROFILE_NAME }}" \
104+
--output ./artifacts/ios
105+
106+
# Upload to App Store Connect using Transporter
107+
- name: Upload to App Store Connect
108+
env:
109+
APP_STORE_CONNECT_API_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }}
110+
APP_STORE_CONNECT_API_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_API_ISSUER_ID }}
111+
APP_STORE_CONNECT_API_KEY_BASE64: ${{ secrets.APP_STORE_CONNECT_API_KEY_BASE64 }}
112+
run: |
113+
# Decode API key
114+
mkdir -p ~/private_keys
115+
echo -n "$APP_STORE_CONNECT_API_KEY_BASE64" | base64 --decode -o ~/private_keys/AuthKey_$APP_STORE_CONNECT_API_KEY_ID.p8
116+
117+
# Find the .ipa file
118+
IPA_PATH=$(find ./artifacts/ios -name "*.ipa" | head -1)
119+
120+
if [ -z "$IPA_PATH" ]; then
121+
echo "No .ipa file found, looking for .app bundle..."
122+
# If no IPA, try to create one from the app bundle
123+
APP_PATH=$(find ./artifacts/ios -name "*.app" -type d | head -1)
124+
if [ -n "$APP_PATH" ]; then
125+
mkdir -p Payload
126+
cp -R "$APP_PATH" Payload/
127+
zip -r ./artifacts/ios/Bible.Alarm.ipa Payload
128+
IPA_PATH="./artifacts/ios/Bible.Alarm.ipa"
129+
rm -rf Payload
130+
fi
131+
fi
132+
133+
# Upload using xcrun altool
134+
xcrun altool --upload-app \
135+
--type ios \
136+
--file "$IPA_PATH" \
137+
--apiKey "$APP_STORE_CONNECT_API_KEY_ID" \
138+
--apiIssuer "$APP_STORE_CONNECT_API_ISSUER_ID"
139+
140+
# Clean up keychain and provisioning profile
141+
- name: Cleanup
142+
if: always()
143+
run: |
144+
security delete-keychain $RUNNER_TEMP/app-signing.keychain-db || true
145+
rm -rf ~/Library/MobileDevice/Provisioning\ Profiles/profile.mobileprovision || true
146+
rm -rf ~/private_keys || true
147+
148+
# Verify no secrets are staged before committing
149+
- name: Verify no secrets in working directory
150+
run: |
151+
# Check for common secret file patterns
152+
if find . -name "*.jks" -o -name "*.keystore" -o -name "*.pfx" -o -name "*.p12" -o -name "*.p8" -o -name "*.mobileprovision" | grep -v ".git"; then
153+
echo "ERROR: Secret files found in working directory!"
154+
find . -name "*.jks" -o -name "*.keystore" -o -name "*.pfx" -o -name "*.p12" -o -name "*.p8" -o -name "*.mobileprovision" | grep -v ".git"
155+
exit 1
156+
fi
157+
echo "✓ No secret files found in working directory"
158+
159+
# Commit version changes (only specific version files)
160+
- name: Commit version changes
161+
uses: EndBug/add-and-commit@v9
162+
with:
163+
author_name: github-actions[bot]
164+
author_email: github-actions[bot]@users.noreply.github.com
165+
message: '[iOS] Update app version for App Store release'
166+
add: |
167+
src/Bible.Alarm/Bible.Alarm.csproj
168+
src/Bible.Alarm/Platforms/iOS/Info.plist
169+
# Explicitly ignore all other files to prevent accidental commits
170+
default_author: github_actions
171+
skip_dirty_check: false
172+
push: true

0 commit comments

Comments
 (0)