Skip to content

Commit aa46433

Browse files
authored
Merge pull request #2 from openSVM/copilot/fix-1
Upgrade dependencies and rebrand to SVMSeek
2 parents 702d53b + f8292f8 commit aa46433

File tree

164 files changed

+62018
-11319
lines changed

Some content is hidden

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

164 files changed

+62018
-11319
lines changed
Lines changed: 279 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,279 @@
1+
name: Build Android APK
2+
3+
on:
4+
push:
5+
branches: [ main, develop, 'feature/*', 'fix/*', 'copilot/*' ]
6+
paths-ignore:
7+
- 'docs/**'
8+
- '*.md'
9+
pull_request:
10+
branches: [ main, develop ]
11+
paths-ignore:
12+
- 'docs/**'
13+
- '*.md'
14+
workflow_dispatch:
15+
16+
jobs:
17+
build-android:
18+
runs-on: ubuntu-latest
19+
20+
steps:
21+
- name: Checkout repository
22+
uses: actions/checkout@v4
23+
24+
- name: Setup Node.js
25+
uses: actions/setup-node@v4
26+
with:
27+
node-version: '20.x'
28+
cache: 'yarn'
29+
30+
- name: Cache Node modules
31+
uses: actions/cache@v4
32+
with:
33+
path: |
34+
node_modules
35+
~/.yarn/cache
36+
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
37+
restore-keys: |
38+
${{ runner.os }}-yarn-
39+
40+
- name: Install dependencies
41+
run: yarn install --frozen-lockfile
42+
43+
- name: Build web application
44+
run: yarn build
45+
46+
- name: Initialize Capacitor (if not exists)
47+
run: |
48+
# Check if Capacitor is already configured by looking for config file
49+
CAPACITOR_CONFIG=""
50+
if [ -f "capacitor.config.json" ]; then
51+
CAPACITOR_CONFIG="capacitor.config.json"
52+
elif [ -f "capacitor.config.ts" ]; then
53+
CAPACITOR_CONFIG="capacitor.config.ts"
54+
fi
55+
56+
# Initialize only if no config exists AND no android directory
57+
if [ -z "$CAPACITOR_CONFIG" ] && [ ! -d "android" ]; then
58+
echo "No Capacitor configuration found, initializing..."
59+
npx cap init "SVMSeek Wallet" "com.svmseek.wallet" --web-dir=build
60+
else
61+
echo "Capacitor already configured with: ${CAPACITOR_CONFIG:-android directory}"
62+
fi
63+
64+
- name: Add Android platform (if not exists)
65+
run: |
66+
if [ ! -d "android" ]; then
67+
npx cap add android
68+
fi
69+
70+
- name: Setup Java JDK
71+
uses: actions/setup-java@v4
72+
with:
73+
distribution: 'temurin'
74+
java-version: '17'
75+
76+
- name: Setup Android SDK
77+
uses: android-actions/setup-android@v3
78+
with:
79+
api-level: 34
80+
build-tools: 34.0.0
81+
82+
- name: Cache Gradle packages
83+
uses: actions/cache@v4
84+
if: hashFiles('android/**/*.gradle*', 'android/**/gradle-wrapper.properties') != ''
85+
with:
86+
path: |
87+
~/.gradle/caches
88+
~/.gradle/wrapper
89+
android/.gradle
90+
android/app/.gradle
91+
key: ${{ runner.os }}-gradle-${{ hashFiles('android/**/*.gradle*', 'android/**/gradle-wrapper.properties') }}
92+
restore-keys: |
93+
${{ runner.os }}-gradle-
94+
95+
- name: Sync Capacitor
96+
run: npx cap sync android
97+
98+
- name: Grant execute permission for gradlew
99+
run: chmod +x android/gradlew
100+
101+
- name: Clean Android project
102+
working-directory: ./android
103+
run: |
104+
# Clean project to remove any corrupted resources or build artifacts
105+
./gradlew clean --stacktrace
106+
107+
- name: Build Android APK
108+
working-directory: ./android
109+
run: |
110+
# Configure Gradle for faster builds
111+
export GRADLE_OPTS="-Dorg.gradle.jvmargs=-Xmx4096m -Dorg.gradle.parallel=true -Dorg.gradle.configureondemand=true -Dorg.gradle.daemon=true -Dorg.gradle.caching=true"
112+
./gradlew assembleDebug --stacktrace --build-cache --parallel --configure-on-demand
113+
114+
- name: Setup Production Keystore (if secrets available)
115+
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
116+
working-directory: ./android
117+
run: |
118+
# Use environment variable instead of direct secret interpolation in shell
119+
if [ ! -z "$ANDROID_KEYSTORE_BASE64" ]; then
120+
echo "Setting up production keystore..."
121+
# Decode safely with error handling
122+
if echo "$ANDROID_KEYSTORE_BASE64" | base64 -d > release.keystore 2>/dev/null; then
123+
echo "Production keystore created successfully"
124+
else
125+
echo "ERROR: Failed to decode keystore base64 data"
126+
exit 1
127+
fi
128+
else
129+
echo "No production keystore secrets found, skipping production build"
130+
fi
131+
env:
132+
ANDROID_KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }}
133+
134+
- name: Build and Sign Production APK (if production keystore available)
135+
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
136+
working-directory: ./android
137+
run: |
138+
if [ -f "release.keystore" ]; then
139+
echo "Building and signing production APK..."
140+
141+
# Clean before production build to prevent resource corruption
142+
./gradlew clean --stacktrace
143+
144+
# Configure signing in gradle.properties for production build
145+
echo "MYAPP_UPLOAD_STORE_FILE=../release.keystore" >> gradle.properties
146+
echo "MYAPP_UPLOAD_KEY_ALIAS=${{ secrets.ANDROID_KEY_ALIAS }}" >> gradle.properties
147+
echo "MYAPP_UPLOAD_STORE_PASSWORD=${{ secrets.ANDROID_KEYSTORE_PASSWORD }}" >> gradle.properties
148+
echo "MYAPP_UPLOAD_KEY_PASSWORD=${{ secrets.ANDROID_KEY_PASSWORD }}" >> gradle.properties
149+
150+
# Build signed release APK directly (Gradle handles signing automatically)
151+
export GRADLE_OPTS="-Dorg.gradle.jvmargs=-Xmx4096m -Dorg.gradle.parallel=true -Dorg.gradle.configureondemand=true -Dorg.gradle.daemon=true -Dorg.gradle.caching=true"
152+
./gradlew assembleRelease --stacktrace --build-cache --parallel --configure-on-demand
153+
154+
# Verify the signed APK exists and is properly signed with production-grade validation
155+
if [ -f "app/build/outputs/apk/release/app-release.apk" ]; then
156+
echo "Production APK built successfully, performing comprehensive signature validation..."
157+
158+
# 1. Check APK structure integrity
159+
APK_FILE="app/build/outputs/apk/release/app-release.apk"
160+
161+
# 2. Verify APK can be read without corruption
162+
if ! unzip -t "$APK_FILE" > /dev/null 2>&1; then
163+
echo "ERROR: APK file is corrupted or invalid"
164+
exit 1
165+
fi
166+
167+
# 3. Check for proper signature files (META-INF directory)
168+
SIGNATURE_FILES=$(unzip -l "$APK_FILE" | grep -E "META-INF.*\.(RSA|DSA|EC)" | wc -l)
169+
if [ "$SIGNATURE_FILES" -eq 0 ]; then
170+
echo "ERROR: No signature files found in APK"
171+
exit 1
172+
fi
173+
echo "Found $SIGNATURE_FILES signature file(s)"
174+
175+
# 4. Verify APK signature using jarsigner (more thorough than grep)
176+
if command -v jarsigner >/dev/null 2>&1; then
177+
echo "Verifying APK signature with jarsigner..."
178+
if jarsigner -verify -verbose "$APK_FILE" > /tmp/apk_verify.log 2>&1; then
179+
echo "✓ APK signature verification passed"
180+
# Check for timestamp and certificate details
181+
if grep -q "jar verified" /tmp/apk_verify.log; then
182+
echo "✓ APK is properly signed and verified"
183+
else
184+
echo "WARNING: APK verification completed but with potential issues"
185+
cat /tmp/apk_verify.log
186+
fi
187+
else
188+
echo "ERROR: APK signature verification failed"
189+
cat /tmp/apk_verify.log
190+
exit 1
191+
fi
192+
else
193+
echo "WARNING: jarsigner not available, using basic signature check"
194+
unzip -l "$APK_FILE" | grep -E "META-INF.*\.(RSA|DSA|EC)" && echo "✓ Basic signature files present" || (echo "ERROR: No signature files found" && exit 1)
195+
fi
196+
197+
# 5. Check APK size is reasonable (not empty or suspiciously small)
198+
echo "Validating APK size..."
199+
if ! ./scripts/check-apk-size.sh "$APK_FILE" 1; then
200+
echo "ERROR: APK size validation failed"
201+
exit 1
202+
fi
203+
204+
# 6. Verify APK contains expected Android components
205+
REQUIRED_FILES="AndroidManifest.xml classes.dex resources.arsc"
206+
for required_file in $REQUIRED_FILES; do
207+
if ! unzip -l "$APK_FILE" | grep -q "$required_file"; then
208+
echo "ERROR: Required Android component '$required_file' not found in APK"
209+
exit 1
210+
fi
211+
done
212+
echo "✓ All required Android components present"
213+
214+
echo "🎉 Production APK passed comprehensive validation"
215+
else
216+
echo "Production APK not found after build"
217+
ls -la app/build/outputs/apk/release/ || echo "Release output directory not found"
218+
exit 1
219+
fi
220+
else
221+
echo "No production keystore found, skipping production build"
222+
fi
223+
env:
224+
ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
225+
ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
226+
ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
227+
228+
- name: Verify APKs
229+
working-directory: ./android
230+
run: |
231+
echo "Verifying debug APK..."
232+
if [ -f "app/build/outputs/apk/debug/app-debug.apk" ]; then
233+
# Basic APK structure validation
234+
unzip -l app/build/outputs/apk/debug/app-debug.apk > /dev/null && echo "Debug APK structure is valid"
235+
echo "Debug APK verified successfully"
236+
else
237+
echo "Debug APK not found for verification"
238+
ls -la app/build/outputs/apk/debug/ || echo "Debug output directory not found"
239+
exit 1
240+
fi
241+
242+
# Verify production APK if it exists
243+
if [ -f "app/build/outputs/apk/release/app-release.apk" ]; then
244+
echo "Verifying production APK..."
245+
unzip -l app/build/outputs/apk/release/app-release.apk > /dev/null && echo "Production APK structure is valid"
246+
echo "Production APK verified successfully"
247+
fi
248+
249+
- name: Rename APK with version
250+
run: |
251+
VERSION=$(node -p "require('./package.json').version")
252+
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
253+
254+
# Copy debug APK (always available)
255+
cp android/app/build/outputs/apk/debug/app-debug.apk svmseek-wallet-debug-v${VERSION}-${TIMESTAMP}.apk
256+
257+
# Copy production APK if available
258+
if [ -f "android/app/build/outputs/apk/release/app-release.apk" ]; then
259+
cp android/app/build/outputs/apk/release/app-release.apk svmseek-wallet-production-v${VERSION}-${TIMESTAMP}.apk
260+
echo "Production APK renamed: svmseek-wallet-production-v${VERSION}-${TIMESTAMP}.apk"
261+
fi
262+
263+
echo "Debug APK renamed: svmseek-wallet-debug-v${VERSION}-${TIMESTAMP}.apk"
264+
265+
- name: Upload APK artifact
266+
uses: actions/upload-artifact@v4
267+
with:
268+
name: svmseek-wallet-apk
269+
path: svmseek-wallet-*.apk
270+
retention-days: 30
271+
272+
- name: Upload Production APK as release asset (on main branch)
273+
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
274+
uses: actions/upload-artifact@v4
275+
with:
276+
name: svmseek-wallet-production-apk
277+
path: svmseek-wallet-production-*.apk
278+
retention-days: 90
279+
if-no-files-found: ignore

0 commit comments

Comments
 (0)