diff --git a/.github/workflows/android-branch_ci.yml b/.github/workflows/android-branch_ci.yml index 71725531f..5b1c8f435 100644 --- a/.github/workflows/android-branch_ci.yml +++ b/.github/workflows/android-branch_ci.yml @@ -27,24 +27,26 @@ jobs: distribution: 'temurin' cache: gradle - - uses: actions/cache@v5.0.1 - with: - path: | - ~/.gradle/caches - ~/.gradle/wrapper - key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }} - restore-keys: | - ${{ runner.os }}-gradle- - - name: Grant execute permission for gradlew run: chmod +x gradlew - - name: Build with Gradle - run: ./gradlew clean && ./gradlew assembleProdDebug + - name: Stop Gradle daemons + run: ./gradlew --stop - - name: Upload Artifact - uses: actions/upload-artifact@v6.0.0 + - name: Decode keystore + id: write_base64_file + uses: Swisyn/Base64-hash-to-file@v1.0 with: - name: Signed app apk - path: app/build/outputs/apk/debug/*.apk - retention-days: 3 + destinationFileName: 'mLauncher.jks' + destinationPath: 'app' + encodedString: ${{ secrets.SIGNINGKEY_BASE64 }} + + - name: Build with Gradle + run: ./gradlew clean assembleProdDebug --refresh-dependencies --no-daemon + env: + JAVA_TOOL_OPTIONS: "-Dhttps.protocols=TLSv1.2" + KEY_STORE_FILE: ${{ steps.write_base64_file.outputs.filePath }} + KEY_STORE_PASSWORD: ${{ secrets.KEY_STORE_PASSWORD }} + KEY_ALIAS: ${{ secrets.KEY_ALIAS }} + KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }} + diff --git a/.github/workflows/android-main_ci.yml b/.github/workflows/android-main_ci.yml index f92a5f4c3..1c3fc4029 100644 --- a/.github/workflows/android-main_ci.yml +++ b/.github/workflows/android-main_ci.yml @@ -25,17 +25,25 @@ jobs: distribution: 'temurin' cache: gradle - - uses: actions/cache@v5.0.1 - with: - path: | - ~/.gradle/caches - ~/.gradle/wrapper - key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }} - restore-keys: | - ${{ runner.os }}-gradle- - - name: Grant execute permission for gradlew run: chmod +x gradlew + - name: Stop Gradle daemons + run: ./gradlew --stop + + - name: Decode keystore + id: write_base64_file + uses: Swisyn/Base64-hash-to-file@v1.0 + with: + destinationFileName: 'mLauncher.jks' + destinationPath: 'app' + encodedString: ${{ secrets.SIGNINGKEY_BASE64 }} + - name: Build with Gradle - run: ./gradlew clean && ./gradlew assembleProdDebug + run: ./gradlew clean assembleProdDebug --refresh-dependencies --no-daemon + env: + JAVA_TOOL_OPTIONS: "-Dhttps.protocols=TLSv1.2" + KEY_STORE_FILE: ${{ steps.write_base64_file.outputs.filePath }} + KEY_STORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }} + KEY_ALIAS: ${{ secrets.KEY_ALIAS }} + KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }} diff --git a/.github/workflows/android-pr_ci.yml b/.github/workflows/android-pr_ci.yml index 8c8979d21..318319173 100644 --- a/.github/workflows/android-pr_ci.yml +++ b/.github/workflows/android-pr_ci.yml @@ -25,24 +25,26 @@ jobs: distribution: 'temurin' cache: gradle - - uses: actions/cache@v5.0.1 - with: - path: | - ~/.gradle/caches - ~/.gradle/wrapper - key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }} - restore-keys: | - ${{ runner.os }}-gradle- - - name: Grant execute permission for gradlew run: chmod +x gradlew - - name: Build with Gradle - run: ./gradlew clean && ./gradlew assembleProdDebug + - name: Stop Gradle daemons + run: ./gradlew --stop - - name: Upload Artifact - uses: actions/upload-artifact@v6.0.0 + - name: Decode keystore + id: write_base64_file + uses: Swisyn/Base64-hash-to-file@v1.0 with: - name: Signed app apk - path: app/build/outputs/apk/debug/*.apk - retention-days: 3 + destinationFileName: 'mLauncher.jks' + destinationPath: 'app' + encodedString: ${{ secrets.SIGNINGKEY_BASE64 }} + + - name: Build with Gradle + run: ./gradlew clean assembleProdDebug --refresh-dependencies --no-daemon + env: + JAVA_TOOL_OPTIONS: "-Dhttps.protocols=TLSv1.2" + KEY_STORE_FILE: ${{ steps.write_base64_file.outputs.filePath }} + KEY_STORE_PASSWORD: ${{ secrets.KEY_STORE_PASSWORD }} + KEY_ALIAS: ${{ secrets.KEY_ALIAS }} + KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }} + diff --git a/.github/workflows/android-release_ci.yml b/.github/workflows/android-release_ci.yml index 8d8cadcf0..9612408b5 100644 --- a/.github/workflows/android-release_ci.yml +++ b/.github/workflows/android-release_ci.yml @@ -25,38 +25,34 @@ jobs: distribution: 'temurin' cache: gradle - - uses: actions/cache@v5.0.1 - with: - path: | - ~/.gradle/caches - ~/.gradle/wrapper - key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }} - restore-keys: | - ${{ runner.os }}-gradle- - - name: Grant execute permission for gradlew run: chmod +x gradlew - - name: Build - run: ./gradlew clean && ./gradlew assembleProdRelease && ./gradlew bundleProdRelease + - name: Stop Gradle daemons + run: ./gradlew --stop - - name: Sign APK - uses: ilharp/sign-android-release@v2.0.0 - # ID used to access action output - id: sign_app_apk + - name: Decode keystore + id: write_base64_file + uses: Swisyn/Base64-hash-to-file@v1.0 with: - releaseDir: app/build/outputs/apk/prod/release - signingKey: ${{ secrets.SIGNINGKEY_BASE64 }} - keyAlias: ${{ secrets.KEY_ALIAS }} - keyStorePassword: ${{ secrets.KEY_STORE_PASSWORD }} - keyPassword: ${{ secrets.KEY_PASSWORD }} - buildToolsVersion: 35.0.0 + destinationFileName: 'mLauncher.jks' + destinationPath: 'app' + encodedString: ${{ secrets.SIGNINGKEY_BASE64 }} + + - name: Build + run: ./gradlew clean assembleProdRelease --refresh-dependencies --no-daemon && ./gradlew clean bundleProdRelease --refresh-dependencies --no-daemon + env: + JAVA_TOOL_OPTIONS: "-Dhttps.protocols=TLSv1.2" + KEY_STORE_FILE: ${{ steps.write_base64_file.outputs.filePath }} + KEY_STORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }} + KEY_ALIAS: ${{ secrets.KEY_ALIAS }} + KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }} - name: Release to GitHub uses: svenstaro/upload-release-action@2.11.3 with: repo_token: ${{ secrets.GITHUB_TOKEN }} - file: ${{steps.sign_app_apk.outputs.signedFile}} + file: app/build/outputs/apk/prob/release/app-prod-release.apk asset_name: MultiLauncher-${{ github.ref_name }}-Signed.apk tag: ${{ github.ref }} overwrite: true @@ -70,23 +66,11 @@ jobs: tag: ${{ github.ref }} overwrite: true - - name: Sign AAB - uses: ilharp/sign-android-release@v2.0.0 - # ID used to access action output - id: sign_app_aab - with: - releaseDir: app/build/outputs/bundle/prodRelease/ - signingKey: ${{ secrets.SIGNINGKEY_BASE64 }} - keyAlias: ${{ secrets.KEY_ALIAS }} - keyStorePassword: ${{ secrets.KEY_STORE_PASSWORD }} - keyPassword: ${{ secrets.KEY_PASSWORD }} - buildToolsVersion: 35.0.0 - - name: Release to GitHub uses: svenstaro/upload-release-action@2.11.3 with: repo_token: ${{ secrets.GITHUB_TOKEN }} - file: ${{steps.sign_app_aab.outputs.signedFile}} + file: app/build/outputs/bundle/prodRelease/app-prod-release.aab asset_name: MultiLauncher-${{ github.ref_name }}-Signed.aab tag: ${{ github.ref }} overwrite: true diff --git a/.github/workflows/nightly-release.yml b/.github/workflows/nightly-release.yml index 33bcb4858..e1143a725 100644 --- a/.github/workflows/nightly-release.yml +++ b/.github/workflows/nightly-release.yml @@ -20,11 +20,11 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: bash clearRelease.sh - - + build: name: Build, Sign & Release runs-on: ubuntu-latest + needs: release steps: - name: Checkout project @@ -39,32 +39,28 @@ jobs: distribution: "temurin" cache: gradle - - uses: actions/cache@v4.2.3 - with: - path: | - ~/.gradle/caches - ~/.gradle/wrapper - key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }} - restore-keys: | - ${{ runner.os }}-gradle- - - name: Grant execute permission for gradlew run: chmod +x gradlew - - name: Build with Gradle - run: ./gradlew clean && ./gradlew assembleNightlyRelease + - name: Stop Gradle daemons + run: ./gradlew --stop - - name: Sign APK - Nightly - uses: ilharp/sign-android-release@v2.0.0 - # ID used to access action output - id: sign_app_nightly + - name: Decode keystore + id: write_base64_file + uses: Swisyn/Base64-hash-to-file@v1.0 with: - releaseDir: app/build/outputs/apk/nightly/release - signingKey: ${{ secrets.SIGNINGKEY_BASE64 }} - keyAlias: ${{ secrets.KEY_ALIAS }} - keyStorePassword: ${{ secrets.KEY_STORE_PASSWORD }} - keyPassword: ${{ secrets.KEY_PASSWORD }} - buildToolsVersion: 35.0.0 + destinationFileName: 'mLauncher.jks' + destinationPath: 'app' + encodedString: ${{ secrets.SIGNINGKEY_BASE64 }} + + - name: Build with Gradle + run: ./gradlew clean assembleNightlyRelease --refresh-dependencies --no-daemon + env: + JAVA_TOOL_OPTIONS: "-Dhttps.protocols=TLSv1.2" + KEY_STORE_FILE: ${{ steps.write_base64_file.outputs.filePath }} + KEY_STORE_PASSWORD: ${{ secrets.KEY_STORE_PASSWORD }} + KEY_ALIAS: ${{ secrets.KEY_ALIAS }} + KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }} - name: Extract Version id: extract_version @@ -82,7 +78,7 @@ jobs: - name: Rename files run: | mkdir -p ./build/release/ - mv ${{steps.sign_app_nightly.outputs.signedFile}} ./build/release/mLauncher-Nightly-Signed.apk + mv app/build/outputs/apk/nightly/release/*.apk ./build/release/mLauncher-Nightly-Signed.apk shell: bash - name: Create and Upload Release diff --git a/.gitignore b/.gitignore index 651d7c683..cc1bd38de 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ app/release app/build app/debug diff.* +*.jks diff --git a/BuildApp.py b/BuildApp.py new file mode 100644 index 000000000..db024e3c1 --- /dev/null +++ b/BuildApp.py @@ -0,0 +1,38 @@ +import os +import subprocess +import sys + +build_type = sys.argv[1] if len(sys.argv) > 1 else "Prod" +print(f"Building {build_type} version...") + +required_vars = ["KEY_STORE_PASSWORD", "KEY_ALIAS", "KEY_PASSWORD"] +for var in required_vars: + if not os.getenv(var): + print(f"ERROR: {var} is not set!") + sys.exit(1) + +print(f"Using signing key: {os.getenv('KEY_ALIAS')}") + + +def run(cmd): + subprocess.check_call(cmd, shell=True) + + +run(f"gradlew clean") +run(f"gradlew assemble{build_type}Release --no-configuration-cache --refresh-dependencies") +run(f"gradlew bundle{build_type}Release --no-configuration-cache --refresh-dependencies") + +apk_path = f"app/build/outputs/apk/{build_type}/release/app_{build_type}_release.apk".lower() +bundle_path = f"app/build/outputs/bundle/{build_type}Release/app-{build_type}-release.aab".lower() + +if os.path.exists(apk_path): + print(f"APK built successfully: {apk_path}") +else: + print(f"APK not found: {apk_path}") + +if os.path.exists(bundle_path): + print(f"Bundle built successfully: {bundle_path}") +else: + print(f"Bundle not found: {bundle_path}") + +print("Build finished.") diff --git a/CHANGELOG.md b/CHANGELOG.md index 720d319ab..451d34f1f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ All notable changes to this project will be documented in this file. See [conventional commits](https://www.conventionalcommits.org/) for commit guidelines. -## [1.11.3.2 → Unreleased](https://github.com/CodeWorksCreativeHub/mLauncher/tree/main) - In Development +## [1110303 (1.11.3.3) - Multi Launcher ‧ Home Screen](https://github.com/CodeWorksCreativeHub/mLauncher/tree/1.11.3.3) - (06, January 2026) ### :sparkles: Enhancements: @@ -139,19 +139,5 @@ All notable changes to this project will be documented in this file. See [conven * Updated Language Files. ([#936](https://github.com/CodeWorksCreativeHub/mLauncher/pull/936)) ([dd0bc103](https://github.com/CodeWorksCreativeHub/mLauncher/commit/dd0bc103)) -## [1110203 (1.11.2.3) - Multi Launcher ‧ Home Screen](https://github.com/CodeWorksCreativeHub/mLauncher/tree/1.11.2.3) - (16, October 2025) - -### :sparkles: Enhancements: - -* Add option to force colored wallpaper ([03fac865](https://github.com/CodeWorksCreativeHub/mLauncher/commit/03fac865)) - -### :bug: Bug Fixes: - -* Adjust font sizes for consistency ([07046ce1](https://github.com/CodeWorksCreativeHub/mLauncher/commit/07046ce1)) - -### :books: Documentation: - -* Remove "Known Issues" references ([06a9923f](https://github.com/CodeWorksCreativeHub/mLauncher/commit/06a9923f)) - --- > Generated by CodeWorks Creative Hub \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts index d2affeb19..fec335ffe 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,3 +1,5 @@ +import java.util.Base64 + plugins { alias(libs.plugins.android.application) alias(libs.plugins.kotlin.android) @@ -12,9 +14,9 @@ plugins { val major = 1 val minor = 11 val patch = 3 -val build = 3 +val build = 4 -val baseVersionName = "$major.$minor.$patch" +val baseVersionName = "$major.$minor.$patch Build $build" val versionCodeBase = (String.format("%02d", major) + @@ -69,6 +71,47 @@ android { } } + signingConfigs { + val keystoreB64: String = + System.getenv("KEY_STORE_FILE") ?: throw GradleException("KEY_STORE_FILE not set.") + val keystorePassword: String = + System.getenv("KEY_STORE_PASSWORD") ?: throw GradleException("KEY_STORE_PASSWORD not set") + val keyAlias: String = + System.getenv("KEY_ALIAS") ?: throw GradleException("KEY_ALIAS not set") + val keyPassword: String = + System.getenv("KEY_PASSWORD") ?: throw GradleException("KEY_PASSWORD not set") + + // Use file helper to ensure correct path + val localKeystore = rootProject.file("app/mLauncher.jks") + val ciKeystore = layout.buildDirectory.file("temp-keystore.jks").get().asFile + + val keystoreFile = when { + localKeystore.exists() -> localKeystore + else -> ciKeystore + } + + if (!keystoreFile.exists()) { + keystoreFile.parentFile.mkdirs() + + val bytes = Base64.getDecoder().decode(keystoreB64) + + if (bytes.size < 1024) { + throw GradleException("Decoded keystore is too small (${bytes.size} bytes)") + } + + keystoreFile.writeBytes(bytes) + } + + println("Using keystore: ${keystoreFile.absolutePath} (${keystoreFile.length()} bytes)") + + create("release") { + storeFile = keystoreFile + storePassword = keystorePassword + this.keyAlias = keyAlias + this.keyPassword = keyPassword + } + } + buildTypes { getByName("debug") { isDebuggable = true @@ -76,6 +119,8 @@ android { isShrinkResources = false applicationIdSuffix = ".dev" + signingConfig = signingConfigs["release"] + resValue("string", "app_version", baseVersionName) resValue("string", "empty", "") } @@ -89,23 +134,22 @@ android { "proguard-rules.pro" ) + signingConfig = signingConfigs["release"] + resValue("string", "app_version", baseVersionName) resValue("string", "empty", "") } } applicationVariants.all { - - val applicationId = this.applicationId val flavorName = this.flavorName - val versionName = this.versionName outputs.all { val output = this as com.android.build.gradle.internal.api.BaseVariantOutputImpl output.outputFileName = - "${applicationId}_${flavorName}_${versionName}.apk" + "app_${flavorName}_release.apk" } } diff --git a/gradle.properties b/gradle.properties index 0fc35d068..35e0768e4 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,7 +6,8 @@ # http://www.gradle.org/docs/current/userguide/build_environment.html # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. -org.gradle.jvmargs=-Xmx2048m +org.gradle.jvmargs=-Xmx4g -Dfile.encoding=UTF-8 +org.gradle.internal.http.useHead=false # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects diff --git a/settings.gradle.kts b/settings.gradle.kts index 97a32757a..3dae8478d 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -2,9 +2,11 @@ pluginManagement { repositories { - gradlePluginPortal() - google() - mavenCentral() + google() // for Android dependencies + gradlePluginPortal() // for plugins + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/dev") + maven("https://jitpack.io") + mavenCentral() // for Kotlin stdlib } } @@ -15,14 +17,11 @@ plugins { dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { - google() - mavenCentral { - metadataSources { - mavenPom() - artifact() - } - } + google() // for Android dependencies + gradlePluginPortal() // for plugins + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/dev") maven("https://jitpack.io") + mavenCentral() // for Kotlin stdlib } }