diff --git a/.gitea/workflows/android-build.yml b/.gitea/workflows/android-build.yml new file mode 100644 index 0000000..5212e2b --- /dev/null +++ b/.gitea/workflows/android-build.yml @@ -0,0 +1,158 @@ +name: Android Build + +on: + push: + branches: [main, develop] + paths: + - 'carry-your-live/**' + - '.gitea/workflows/android-build.yml' + pull_request: + branches: [main, develop] + paths: + - 'carry-your-live/**' + workflow_dispatch: + +env: + NODE_VERSION: '20' + JAVA_VERSION: '17' + ANDROID_HOME: ${{ runner.tool_cache }}/android-sdk + ANDROID_SDK_ROOT: ${{ runner.tool_cache }}/android-sdk + +jobs: + build-android: + name: Build Android + runs-on: ubuntu-latest + timeout-minutes: 45 + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: 'npm' + cache-dependency-path: carry-your-live/package-lock.json + + - name: Setup Java + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: ${{ env.JAVA_VERSION }} + cache: 'gradle' + cache-dependency-path: carry-your-live/android/gradle/wrapper/gradle-wrapper.properties + + - name: Install Android SDK + run: | + if [ -x "$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager" ]; then + echo "Android SDK already present in cache" + else + rm -rf "$ANDROID_HOME/cmdline-tools" + mkdir -p "$ANDROID_HOME/cmdline-tools" + wget -q https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip -O /tmp/cmdtools.zip + unzip -q /tmp/cmdtools.zip -d "$ANDROID_HOME/cmdline-tools" + mv "$ANDROID_HOME/cmdline-tools/cmdline-tools" "$ANDROID_HOME/cmdline-tools/latest" + fi + mkdir -p "$ANDROID_HOME/licenses" + echo "8933bad161af4178b1185d1a37fbf41ea5269c55" > "$ANDROID_HOME/licenses/android-sdk-license" + echo "d56f5187479451eabf01fb78af6dfcb131a6481e" >> "$ANDROID_HOME/licenses/android-sdk-license" + echo "24333f8a63b6825ea9c5514f83c2829b004d1fee" >> "$ANDROID_HOME/licenses/android-sdk-license" + "$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager" \ + "platform-tools" \ + "platforms;android-36" \ + "build-tools;36.0.0" \ + "ndk;27.1.12297006" + mkdir -p carry-your-live/android/app + echo "sdk.dir=$ANDROID_HOME" > carry-your-live/android/app/local.properties + + - name: Install dependencies + working-directory: carry-your-live + run: | + for i in {1..3}; do + npm ci && break || sleep 10 + done + + - name: Prebuild Android + working-directory: carry-your-live + run: npx expo prebuild --platform android --clean + + - name: Build Android Debug APK + working-directory: carry-your-live/android + run: ./gradlew assembleDebug --no-daemon + + - name: Build Android Release APK + working-directory: carry-your-live/android + run: ./gradlew assembleRelease --no-daemon + + - name: Build Android Bundle (AAB) + working-directory: carry-your-live/android + run: ./gradlew bundleRelease --no-daemon + + - name: Run Unit Tests + working-directory: carry-your-live/android + run: ./gradlew test --no-daemon + + - name: Run Lint + working-directory: carry-your-live/android + run: ./gradlew lint --no-daemon + + - name: Find Debug APK + id: find-debug-apk + run: | + APK_PATH=$(find carry-your-live/android/app/build/outputs -name "*debug*.apk" -type f | head -n 1) + echo "apk_path=$APK_PATH" >> "$GITHUB_OUTPUT" + echo "Found Debug APK at: $APK_PATH" + + - name: Find Release APK + id: find-release-apk + run: | + APK_PATH=$(find carry-your-live/android/app/build/outputs -name "*release*.apk" -type f | head -n 1) + echo "apk_path=$APK_PATH" >> "$GITHUB_OUTPUT" + echo "Found Release APK at: $APK_PATH" + + - name: Find Release Bundle (AAB) + id: find-release-aab + run: | + AAB_PATH=$(find carry-your-live/android/app/build/outputs -name "*release*.aab" -type f | head -n 1) + echo "aab_path=$AAB_PATH" >> "$GITHUB_OUTPUT" + echo "Found Release AAB at: $AAB_PATH" + + - name: Upload Debug APK + uses: actions/upload-artifact@v4 + with: + name: android-debug-apk + path: ${{ steps.find-debug-apk.outputs.apk_path }} + retention-days: 30 + + - name: Upload Release APK + uses: actions/upload-artifact@v4 + with: + name: android-release-apk + path: ${{ steps.find-release-apk.outputs.apk_path }} + retention-days: 30 + + - name: Upload Release Bundle (AAB) + uses: actions/upload-artifact@v4 + with: + name: android-release-aab + path: ${{ steps.find-release-aab.outputs.aab_path }} + retention-days: 30 + + - name: Upload Test Reports + if: always() + uses: actions/upload-artifact@v4 + with: + name: android-test-reports + path: carry-your-live/android/app/build/reports/tests/ + retention-days: 14 + + - name: Upload Lint Reports + if: always() + uses: actions/upload-artifact@v4 + with: + name: android-lint-reports + path: carry-your-live/android/app/build/reports/lint-results*.html + retention-days: 14 \ No newline at end of file diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index e22a1c9..c063c0e 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -1,4 +1,4 @@ -name: Build and Release APK +name: Build and Release Android on: push: @@ -7,28 +7,40 @@ on: workflow_dispatch: env: + NODE_VERSION: '20' + JAVA_VERSION: '17' ANDROID_HOME: ${{ runner.tool_cache }}/android-sdk ANDROID_SDK_ROOT: ${{ runner.tool_cache }}/android-sdk + GITEA_SERVER_URL: "https://techmos.gitea.org" jobs: build: + name: Build Android runs-on: ubuntu-latest + timeout-minutes: 45 + permissions: + contents: read + steps: - - name: Checkout + - name: Checkout repository uses: actions/checkout@v4 with: fetch-depth: 0 - - name: Set up Node + - name: Setup Node.js uses: actions/setup-node@v4 with: - node-version: 22 + node-version: ${{ env.NODE_VERSION }} + cache: 'npm' + cache-dependency-path: carry-your-live/package-lock.json - - name: Set up JDK + - name: Setup Java uses: actions/setup-java@v4 with: - distribution: temurin - java-version: 17 + distribution: 'temurin' + java-version: ${{ env.JAVA_VERSION }} + cache: 'gradle' + cache-dependency-path: carry-your-live/android/gradle/wrapper/gradle-wrapper.properties - name: Install Android SDK run: | @@ -53,48 +65,123 @@ jobs: mkdir -p carry-your-live/android/app echo "sdk.dir=$ANDROID_HOME" > carry-your-live/android/app/local.properties - - name: Install JS dependencies + - name: Install dependencies working-directory: carry-your-live run: | for i in {1..3}; do npm ci && break || sleep 10 done - - name: Generate debug keystore (signs the release APK) - run: | - mkdir -p carry-your-live/android/app - rm -f carry-your-live/android/app/debug.keystore - keytool -genkeypair -v \ - -keystore carry-your-live/android/app/debug.keystore \ - -alias androiddebugkey -storepass android -keypass android \ - -keyalg RSA -keysize 2048 -validity 10000 \ - -dname "CN=Android Debug,O=Android,C=US" + - name: Prebuild Android + working-directory: carry-your-live + run: npx expo prebuild --platform android --clean - - name: Build release APK + - name: Configure Release Signing + if: env.KEYSTORE_BASE64 != '' + working-directory: carry-your-live/android/app run: | - cd carry-your-live/android - ./gradlew assembleRelease + echo "${{ secrets.KEYSTORE_BASE64 }}" | base64 -d > release.keystore + cat > ../release-signing.properties <> "$GITHUB_OUTPUT" - echo "Found APK at: $APK_PATH" + echo "Found Release APK at: $APK_PATH" - - name: Create Gitea Release and Upload APK - if: startsWith(github.ref, 'refs/tags/v') + - name: Find Release Bundle (AAB) + id: find-release-aab + run: | + AAB_PATH=$(find carry-your-live/android/app/build/outputs -name "*release*.aab" -type f | head -n 1) + echo "aab_path=$AAB_PATH" >> "$GITHUB_OUTPUT" + echo "Found Release AAB at: $AAB_PATH" + + - name: Upload Build Artifacts + uses: actions/upload-artifact@v4 + with: + name: android-release-build + path: | + ${{ steps.find-release-apk.outputs.apk_path }} + ${{ steps.find-release-aab.outputs.aab_path }} + retention-days: 90 + + - name: Upload Test Reports + if: always() + uses: actions/upload-artifact@v4 + with: + name: android-test-reports + path: carry-your-live/android/app/build/reports/tests/ + retention-days: 30 + + - name: Upload Lint Reports + if: always() + uses: actions/upload-artifact@v4 + with: + name: android-lint-reports + path: carry-your-live/android/app/build/reports/lint-results*.html + retention-days: 30 + + release: + name: Create Gitea Release + needs: build + if: startsWith(github.ref, 'refs/tags/v') + runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: + contents: write + + steps: + - name: Download Build Artifacts + uses: actions/download-artifact@v4 + with: + name: android-release-build + path: artifacts + + - name: Create Gitea Release env: GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} run: | TAG_NAME="${GITHUB_REF#refs/tags/}" REPO_OWNER="${GITHUB_REPOSITORY%/*}" REPO_NAME="${GITHUB_REPOSITORY#*/}" - APK_PATH="${{ steps.find-apk.outputs.apk_path }}" - APK_NAME="carry-your-live-${TAG_NAME}.apk" - # Create release using Gitea API - RESPONSE=$(curl -s -X POST "https://techmos.gitea.org/api/v1/repos/${REPO_OWNER}/${REPO_NAME}/releases" \ + # Detect Gitea server URL + GITEA_URL="${GITEA_SERVER_URL:-https://techmos.gitea.org}" + + # Create release + RESPONSE=$(curl -s -X POST "${GITEA_URL}/api/v1/repos/${REPO_OWNER}/${REPO_NAME}/releases" \ -H "Authorization: token ${GITEA_TOKEN}" \ -H "Content-Type: application/json" \ -d "{ @@ -110,12 +197,41 @@ jobs: if [ "$RELEASE_ID" = "null" ] || [ -z "$RELEASE_ID" ]; then echo "Release might already exist, fetching existing release..." RELEASE_ID=$(curl -s -H "Authorization: token ${GITEA_TOKEN}" \ - "https://techmos.gitea.org/api/v1/repos/${REPO_OWNER}/${REPO_NAME}/releases/tags/${TAG_NAME}" | jq -r '.id') + "${GITEA_URL}/api/v1/repos/${REPO_OWNER}/${REPO_NAME}/releases/tags/${TAG_NAME}" | jq -r '.id') fi - # Upload APK as attachment - curl -s -X POST "https://techmos.gitea.org/api/v1/repos/${REPO_OWNER}/${REPO_NAME}/releases/${RELEASE_ID}/assets" \ - -H "Authorization: token ${GITEA_TOKEN}" \ - -F "attachment=@${APK_PATH};filename=${APK_NAME}" + if [ "$RELEASE_ID" = "null" ] || [ -z "$RELEASE_ID" ]; then + echo "Failed to create or find release" + exit 1 + fi - echo "APK uploaded to release ${TAG_NAME}" \ No newline at end of file + echo "Release ID: $RELEASE_ID" + echo "release_id=$RELEASE_ID" >> "$GITHUB_OUTPUT" + id: create-release + + - name: Upload APK to Release + env: + GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} + run: | + GITEA_URL="${GITEA_URL:-https://techmos.gitea.org}" + REPO_OWNER="${GITHUB_REPOSITORY%/*}" + REPO_NAME="${GITHUB_REPOSITORY#*/}" + RELEASE_ID="${{ steps.create-release.outputs.release_id }}" + TAG_NAME="${GITHUB_REF#refs/tags/}" + + for file in artifacts/*; do + if [[ "$file" == *.apk ]]; then + FILENAME="carry-your-live-${TAG_NAME}.apk" + elif [[ "$file" == *.aab ]]; then + FILENAME="carry-your-live-${TAG_NAME}.aab" + else + continue + fi + + echo "Uploading $file as $FILENAME..." + curl -s -X POST "${GITEA_URL}/api/v1/repos/${REPO_OWNER}/${REPO_NAME}/releases/${RELEASE_ID}/assets" \ + -H "Authorization: token ${GITEA_TOKEN}" \ + -F "attachment=@${file};filename=${FILENAME}" + + echo "Uploaded $FILENAME" + done \ No newline at end of file diff --git a/carry-your-live/android/app/build.gradle b/carry-your-live/android/app/build.gradle index da4f1ef..274caa9 100644 --- a/carry-your-live/android/app/build.gradle +++ b/carry-your-live/android/app/build.gradle @@ -104,15 +104,27 @@ android { keyAlias 'androiddebugkey' keyPassword 'android' } + + release { + if (project.hasProperty('releaseSigningProperties')) { + def propsFile = file(project.property('releaseSigningProperties')) + if (propsFile.exists()) { + def props = new Properties() + props.load(new FileInputStream(propsFile)) + storeFile = file(props['storeFile']) + storePassword = props['storePassword'] + keyAlias = props['keyAlias'] + keyPassword = props['keyPassword'] + } + } + } } buildTypes { debug { signingConfig = signingConfigs.debug } release { - // Caution! In production, you need to generate your own keystore file. - // see https://reactnative.dev/docs/signed-apk-android. - signingConfig = signingConfigs.debug + signingConfig = signingConfigs.release def enableShrinkResources = findProperty('android.enableShrinkResourcesInReleaseBuilds') ?: 'false' shrinkResources = enableShrinkResources.toBoolean() minifyEnabled enableMinifyInReleaseBuilds