diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md deleted file mode 100644 index a32cae48d5c..00000000000 --- a/.github/CONTRIBUTING.md +++ /dev/null @@ -1,27 +0,0 @@ -## Submitting issues - -If the app is not behaving like it should, it's not necessarily a bug. Please consult the following resources before -submitting a new issue. - -- [User Manual](https://docs.k9mail.app/) -- [Frequently Asked Questions](https://forum.k9mail.app/c/faq) -- [Support Forum](https://forum.k9mail.app/) - -### Bug report guidelines - -- The issue tracker is solely for bug reports and feature/enhancement requests. If you have a question of any kind, - please use the [support forum](https://forum.k9mail.app/c/support) instead. -- Search the [existing issues](https://github.com/thunderbird/thunderbird-android/issues?q=) first to make sure your - issue hasn't been reported before. - -## Translations - -We're using [Weblate](https://hosted.weblate.org/projects/tb-android/) to manage translations. - -## Contributing code - -We love [pull requests](https://github.com/thunderbird/thunderbird-android/pulls) from everyone! - -Any contributions, large or small, major features, bug fixes, unit/integration tests are welcomed and appreciated -but will be thoroughly reviewed and discussed. -Please make sure you read the [Code Style Guidelines](https://github.com/thunderbird/thunderbird-android/wiki/CodeStyle). diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index dfd8d7b27c5..94ad2a67128 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -14,6 +14,16 @@ body: required: true - label: I will describe the problem with as much detail as possible. required: true + - type: dropdown + id: app + attributes: + label: App + multiple: false + options: + - Thunderbird for Android + - K-9 Mail + validations: + required: true - type: input id: version attributes: @@ -21,7 +31,7 @@ body: description: | What's the "latest version" changes all the time, so we need the exact version number. You can find it inside the app under *Settings → About*. - placeholder: x.yyy + placeholder: x.y validations: required: true - type: dropdown diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 1b65bc7bd8b..3c1af7b53b4 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,5 +1,5 @@ blank_issues_enabled: false contact_links: - - name: K-9 Mail Forum - url: https://forum.k9mail.app/ + - name: Mozilla Support Forum (SUMO) + url: https://support.mozilla.org/en-US/products/thunderbird-android about: Most issues are not bugs. Ask the community for help. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 2a7da388ea8..fdb98398539 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -3,7 +3,7 @@ Please ensure that your pull request meets the following requirements - thanks! - Does not contain merge commits. Rebase instead. - Contains commits with descriptive titles. - New code is written in Kotlin whenever possible. -- Follows our existing codestyle (`gradlew spotlessCheck` to check and `gradlew spotlessFormat` to format your source code; will be checked by CI). +- Follows our existing codestyle (`gradlew spotlessCheck` to check and `gradlew spotlessApply` to format your source code; will be checked by CI). - Does not break any unit tests (`gradlew testDebugUnitTest`; will be checked by CI). - Uses a descriptive title; don't put issue numbers in there. - Contains a reference to the issue that it fixes (e.g. _Closes #XXX_ or _Fixes #XXX_) in the body text. diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index ec4c63fe963..a2d04b039fb 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: gradle/wrapper-validation-action@v2 + - uses: gradle/actions/wrapper-validation@v4 build: runs-on: ubuntu-latest @@ -35,7 +35,7 @@ jobs: java-version: 17 - name: Setup Gradle - uses: gradle/actions/setup-gradle@v3 + uses: gradle/actions/setup-gradle@v4 - name: Quality - Spotless run: ./gradlew spotlessCheck diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 9df236ba1f1..715baf44cb3 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -24,7 +24,7 @@ jobs: java-version: 17 - name: Setup Gradle - uses: gradle/actions/setup-gradle@v3 + uses: gradle/actions/setup-gradle@v4 - uses: github/codeql-action/init@v3 with: diff --git a/.github/workflows/daily_builds.yml b/.github/workflows/daily_builds.yml new file mode 100644 index 00000000000..9f99146c29d --- /dev/null +++ b/.github/workflows/daily_builds.yml @@ -0,0 +1,13 @@ +--- +name: Daily Builds +on: + workflow_dispatch: + schedule: + - cron: '8 20 * * *' + +jobs: + trigger_daily_build: + uses: ./.github/workflows/shippable_builds.yml + secrets: inherit + permissions: + contents: write diff --git a/.github/workflows/gradle-cache.yml b/.github/workflows/gradle-cache.yml index 9d31c61d5e7..86b913abb35 100644 --- a/.github/workflows/gradle-cache.yml +++ b/.github/workflows/gradle-cache.yml @@ -25,7 +25,7 @@ jobs: java-version: 17 - name: Setup Gradle - uses: gradle/actions/setup-gradle@v3 + uses: gradle/actions/setup-gradle@v4 with: gradle-home-cache-cleanup: true diff --git a/.github/workflows/markdown.yml b/.github/workflows/markdown.yml index 2c1359d8242..3b3c12e0abb 100644 --- a/.github/workflows/markdown.yml +++ b/.github/workflows/markdown.yml @@ -21,7 +21,7 @@ jobs: java-version: 17 - name: Setup Gradle - uses: gradle/actions/setup-gradle@v3 + uses: gradle/actions/setup-gradle@v4 - name: Quality - Spotless Markdown Check run: ./gradlew spotlessMarkdownCheck diff --git a/.github/workflows/needinfo-remove.yml b/.github/workflows/needinfo-remove.yml new file mode 100644 index 00000000000..73e3c64be89 --- /dev/null +++ b/.github/workflows/needinfo-remove.yml @@ -0,0 +1,25 @@ +--- +name: Remove needinfo label + +on: + issue_comment: + types: + - created + +jobs: + build: + runs-on: ubuntu-latest + if: | + github.event.comment.author_association != 'OWNER' && + github.event.comment.author_association != 'MEMBER' && + github.event.comment.author_association != 'COLLABORATOR' + permissions: + issues: write + pull-requests: write + steps: + # https://github.com/octokit/request-action/issues/118 + - name: Remove needinfo label + run: | + curl --request DELETE \ + --url 'https://api.github.com/repos/${{ github.repository }}/issues/${{ github.event.issue.number }}/labels/status%3A%20needs%20information' \ + --header 'Authorization: token ${{ secrets.GITHUB_TOKEN }}' diff --git a/.github/workflows/needinfo-stale.yml b/.github/workflows/needinfo-stale.yml new file mode 100644 index 00000000000..d71c052bba4 --- /dev/null +++ b/.github/workflows/needinfo-stale.yml @@ -0,0 +1,26 @@ +--- +name: Close old issues with the needinfo label + +on: + schedule: + - cron: "0 0 * * *" + workflow_dispatch: + +jobs: + build: + runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write + steps: + - name: Close old issues with the needinfo tag + uses: dwieeb/needs-reply@v2 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + issue-label: "status: needs information" + days-before-close: 30 + close-message: > + Thank you for taking the time to report this issue. It looks like we haven't heard back + in a while, therefore we're closing this issue. If the problem persists in the latest + version please open a new issue and reference this one so we can follow up more + effectively. diff --git a/.github/workflows/shippable_builds.yml b/.github/workflows/shippable_builds.yml new file mode 100644 index 00000000000..9121cfcdeb1 --- /dev/null +++ b/.github/workflows/shippable_builds.yml @@ -0,0 +1,855 @@ +--- +name: Shippable Build & Signing +on: + workflow_call: + workflow_dispatch: + inputs: + skipThunderbird: + type: boolean + description: Skip building Thunderbird + skipK9Mail: + type: boolean + description: Skip building K-9 Mail + skipTests: + type: boolean + description: Skip running tests + skipBetaBump: + type: boolean + description: Skip version bump (beta) + skipGooglePlay: + type: boolean + description: Skip Google Play publish + draftGooglePlay: + type: boolean + description: Leave Play Store version in draft state + +jobs: + get_environment: + name: Determine Release Environment + runs-on: ubuntu-latest + outputs: + releaseEnv: ${{ steps.releaseEnv.outputs.result }} + steps: + - uses: actions/github-script@v7 + id: releaseEnv + with: + result-encoding: string + script: | + const RELEASE_ENVS = { + "refs/heads/main": "thunderbird_daily", + "refs/heads/beta": "thunderbird_beta", + "refs/heads/release": "thunderbird_release", + }; + + if (context.ref in RELEASE_ENVS) { + return RELEASE_ENVS[context.ref]; + } else { + core.setFailed(`Unknown branch ${context.ref} for shippable builds!`) + return ""; + } + + dump_config: + name: Show Release Environment + runs-on: ubuntu-latest + needs: get_environment + environment: ${{ needs.get_environment.outputs.releaseEnv }} + outputs: + matrixInclude: ${{ steps.dump.outputs.matrixInclude }} + releaseType: ${{ vars.RELEASE_TYPE }} + steps: + - name: Show Environment + uses: actions/github-script@v7 + id: dump + env: + matrixInclude: ${{ vars.MATRIX_INCLUDE }} + releaseType: ${{ vars.RELEASE_TYPE }} + skipThunderbird: ${{ inputs.skipThunderbird }} + skipK9Mail: ${{ inputs.skipK9Mail }} + skipTests: ${{ inputs.skipTests }} + skipBetaBump: ${{ inputs.skipBetaBump }} + skipGooglePlay: ${{ inputs.skipGooglePlay }} + draftGooglePlay: ${{ inputs.draftGooglePlay }} + with: + script: | + let matrix = JSON.parse(process.env.matrixInclude); + let skipThunderbird = process.env.skipThunderbird == "true"; + let skipK9Mail = process.env.skipK9Mail == "true"; + + if (!matrix.every(item => !!item.appName && !!item.packageFormat)) { + core.setFailed("MATRIX_INCLUDE is missing appName or packageFormat"); + } + + let matrixFull = matrix.filter(item => { + return !((item.appName == "k9mail" && skipK9Mail) || + (item.appName == "thunderbird" && skipThunderbird)); + }); + + if (!matrixFull.length) { + core.setFailed("There are no builds to run"); + return; + } + + core.setOutput("matrixInclude", matrixFull); + + await core.summary + .addRaw(`Beginning a ${process.env.releaseType} build with the following configurations:`, true) + .addTable([ + [ + { data: "App Name", header: true }, + { data: "Flavor", header: true }, + { data: "Format", header: true }, + { data: "Release Target", header: true }, + { data: "Play Store Track", header: true }, + ], + ...matrixFull.map(item => [ + { data: item.appName }, + { data: item.packageFlavor || "default" }, + { data: item.packageFormat }, + { data: item.releaseTarget?.replace(/\|/g, ", ") || "artifact only" }, + { data: item.playTargetTrack || "none" }, + ]) + ]) + .write(); + + if (skipThunderbird) { + await core.summary.addList(["Thunderbird build is being skipped"]).write(); + } + if (skipK9Mail) { + await core.summary.addList(["K-9 Mail build is being skipped"]).write(); + } + if (process.env.skipTests == "true") { + await core.summary.addList(["Tests are being skipped"]).write(); + } + if (process.env.skipBetaBump == "true" && process.env.releaseType == "beta") { + await core.summary.addList(["Beta bump is being skipped"]).write(); + } + if (process.env.skipGooglePlay == "true") { + await core.summary.addList(["Play Store upload is being skipped"]).write(); + } + if (process.env.skipGooglePlay != "true" && process.env.draftGooglePlay == "true") { + await core.summary.addList(["Play Store upload is being kept in the draft state"]).write(); + } + + notify_build_start: + name: Notify Build Start + runs-on: ubuntu-latest + needs: [dump_config] + if: ${{ needs.dump_config.outputs.releaseType == 'beta' || needs.dump_config.outputs.releaseType == 'release' }} + environment: notify_matrix + outputs: + actorLink: ${{ steps.actorLink.outputs.actorLink }} + steps: + - name: Triggering Actor Link + id: actorLink + uses: actions/github-script@v7 + env: + userMap: ${{ vars.MATRIX_NOTIFY_USER_MAP }} + with: + script: | + let userMap = JSON.parse(process.env.userMap || "{}"); + if (Object.hasOwn(userMap, context.actor)) { + let mxid = userMap[context.actor]; + core.setOutput("actorLink", `[${mxid}](https://matrix.to/#/${mxid})`); + } else { + core.setOutput("actorLink", `[@${context.actor}](https://github.com/${context.actor})`); + } + + - name: Notify Build Start + if: ${{ vars.MATRIX_NOTIFY_ROOM }} + uses: kewisch/action-matrix-notify@v1 + with: + matrixHomeserver: ${{ vars.MATRIX_NOTIFY_HOMESERVER }} + matrixRoomId: ${{ vars.MATRIX_NOTIFY_ROOM }} + matrixToken: ${{ secrets.MATRIX_NOTIFY_TOKEN }} + message: >- + 🔵 [${{ vars.RELEASE_TYPE }} build ${{ github.run_number}}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) + was started by ${{ steps.actorLink.outputs.actorLink }} + + + release_commit: + name: Release Bumps + runs-on: ubuntu-latest + needs: [dump_config, get_environment] + if: ${{ needs.dump_config.outputs.releaseType == 'beta' || needs.dump_config.outputs.releaseType == 'release' }} + environment: ${{ needs.get_environment.outputs.releaseEnv }} + strategy: + matrix: + include: "${{ fromJSON(needs.dump_config.outputs.matrixInclude) }}" + permissions: + contents: write + outputs: + k9mail_sha: ${{ steps.commit.outputs.k9mail_sha }} + thunderbird_sha: ${{ steps.commit.outputs.thunderbird_sha }} + k9mail_github_notes: ${{ steps.render_notes.outputs.k9mail_github_notes }} + thunderbird_github_notes: ${{ steps.render_notes.outputs.thunderbird_github_notes }} + steps: + - name: Checkout repository + if: ${{ contains(matrix.releaseTarget, 'github') }} + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Copy CI gradle.properties + if: ${{ contains(matrix.releaseTarget, 'github') }} + shell: bash + run: mkdir -p ~/.gradle ; cp .github/ci-gradle.properties ~/.gradle/gradle.properties + + - uses: actions/setup-java@v4 + if: ${{ contains(matrix.releaseTarget, 'github') }} + with: + distribution: temurin + java-version: 17 + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + if: ${{ contains(matrix.releaseTarget, 'github') }} + with: + cache-disabled: ${{ needs.dump_config.outputs.releaseType == 'beta' || needs.dump_config.outputs.releaseType == 'release' }} + add-job-summary: never + + - name: Get application info + id: appinfo + shell: bash + if: ${{ contains(matrix.releaseTarget, 'github') }} + env: + RELEASE_TYPE: ${{ vars.RELEASE_TYPE }} + PACKAGE_FLAVOR: ${{ matrix.packageFlavor }} + APP_NAME: ${{ matrix.appName }} + run: | + if [[ "${APP_NAME}" == "k9mail" && "${RELEASE_TYPE}" == "beta" ]]; then + # k9mail uses release for betas as well. Later on we should align the structures and + # remove this hack + RELEASE_TYPE=release + fi + + ./gradlew :app-${APP_NAME}:printVersionInfo -PbuildType=${RELEASE_TYPE} -PflavorName=${PACKAGE_FLAVOR} --configure-on-demand + + - name: Bump version code + id: bump_version_code + if: ${{ contains(matrix.releaseTarget, 'github') }} + shell: bash + env: + APP_NAME: ${{ matrix.appName }} + OLD_VERSION_CODE: ${{ steps.appinfo.outputs.VERSION_CODE }} + run: | + NEW_VERSION_CODE=$(($OLD_VERSION_CODE + 1)) + sed "s/versionCode = $OLD_VERSION_CODE/versionCode = $NEW_VERSION_CODE/" app-${APP_NAME}/build.gradle.kts > tmp_gradle_kts + + ! diff -u app-${APP_NAME}/build.gradle.kts tmp_gradle_kts # flip return value to force error if no bump + mv tmp_gradle_kts app-${APP_NAME}/build.gradle.kts + + echo "CODE=${NEW_VERSION_CODE}" | tee $GITHUB_OUTPUT + + - name: Bump version suffix + id: bump_version_suffix + if: ${{ !inputs.skipBetaBump && contains(matrix.releaseTarget, 'github') && vars.RELEASE_TYPE == 'beta' }} + shell: bash + env: + APP_NAME: ${{ matrix.appName }} + OLD_VERSION_SUFFIX: ${{ steps.appinfo.outputs.VERSION_NAME_SUFFIX }} + run: | + NEW_VERSION_SUFFIX=b$((${OLD_VERSION_SUFFIX:1} + 1)) + sed "s/versionNameSuffix = \"$OLD_VERSION_SUFFIX\"/versionNameSuffix = \"$NEW_VERSION_SUFFIX\"/" app-${APP_NAME}/build.gradle.kts > tmp_gradle_kts + + ! diff -u app-${APP_NAME}/build.gradle.kts tmp_gradle_kts # flip return value to force error if no bump + mv tmp_gradle_kts app-${APP_NAME}/build.gradle.kts + + echo "SUFFIX=${NEW_VERSION_SUFFIX}" >> $GITHUB_OUTPUT + + cat $GITHUB_OUTPUT + + - name: Render Release Notes + id: render_notes + if: ${{ contains(matrix.releaseTarget, 'github') }} + shell: bash + env: + APP_NAME: ${{ matrix.appName }} + APPLICATION_ID: ${{ steps.appinfo.outputs.APPLICATION_ID }} + APPLICATION_LABEL: ${{ steps.appinfo.outputs.APPLICATION_LABEL }} + VERSION_CODE: ${{ steps.bump_version_code.outputs.CODE }} + FULL_VERSION_NAME: ${{ steps.appinfo.outputs.VERSION_NAME }}${{ steps.bump_version_suffix.outputs.SUFFIX || steps.appinfo.outputs.VERSION_NAME_SUFFIX }} + run: | + mkdir -p ./app-metadata/${APPLICATION_ID}/en-US/changelogs + GITHUB_NOTES_FILE="$(mktemp -d)/long-notes.txt" + python ./scripts/render-notes.py ${APPLICATION_ID} ${FULL_VERSION_NAME} ${VERSION_CODE} ${GITHUB_NOTES_FILE} + + echo "${APP_NAME}_github_notes<> $GITHUB_OUTPUT + cat $GITHUB_NOTES_FILE >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + echo "

${APPLICATION_LABEL} ${FULL_VERSION_NAME} Release Notes (${VERSION_CODE})

" | tee -a $GITHUB_STEP_SUMMARY + echo -e "Summarized Version Notes\n\n\`\`\`" | tee -a $GITHUB_STEP_SUMMARY + cat ./app-metadata/${APPLICATION_ID}/en-US/changelogs/${VERSION_CODE}.txt | tee -a $GITHUB_STEP_SUMMARY + echo -e "\n\`\`\`\n\nLong Version Notes\n\n\`\`\`" | tee -a $GITHUB_STEP_SUMMARY + cat $GITHUB_NOTES_FILE | tee -a $GITHUB_STEP_SUMMARY + echo -e "\`\`\`" | tee -a $GITHUB_STEP_SUMMARY + + - name: Validate Release Notes Length + if: ${{ contains(matrix.releaseTarget, 'github') }} + shell: bash + env: + APPLICATION_ID: ${{ steps.appinfo.outputs.APPLICATION_ID }} + VERSION_CODE: ${{ steps.bump_version_code.outputs.CODE }} + run: | + wc -c ./app-metadata/${APPLICATION_ID}/en-US/changelogs/${VERSION_CODE}.txt + RELNOTES_LENGTH=$(wc -c ./app-metadata/${APPLICATION_ID}/en-US/changelogs/${VERSION_CODE}.txt | awk '{print $1}') + + if [[ "${RELNOTES_LENGTH}" -gt 500 ]]; then + echo "Release Notes are too long. Found ${RELNOTES_LENGTH} characters, need a maximum of 500" + exit 1 + fi + + - name: Release Commits + if: ${{ contains(matrix.releaseTarget, 'github') }} + id: commit + shell: bash + env: + APPLICATION_LABEL: ${{ steps.appinfo.outputs.APPLICATION_LABEL }} + APPLICATION_ID: ${{ steps.appinfo.outputs.APPLICATION_ID }} + APP_NAME: ${{ matrix.appName }} + FULL_VERSION_NAME: ${{ steps.appinfo.outputs.VERSION_NAME }}${{ steps.bump_version_suffix.outputs.SUFFIX || steps.appinfo.outputs.VERSION_NAME_SUFFIX }} + run: | + git config --global user.name "GitHub Actions Bot" + git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com" + + # We need the metadata to point to the right application for the release commit + rm metadata + ln -sf app-metadata/${APPLICATION_ID} metadata + + # Add changelogs, build version changes and metadata symlink + git add ./app-metadata/${APPLICATION_ID}/en-US/changelogs/* + git add ./app-${APP_NAME}/src/main/res/raw/changelog_master.xml + git add ./app-${APP_NAME}/build.gradle.kts + git add metadata + + # Ready to commit. Make sure to pull again to reduce likelihood of race conditions + git status + git commit -m "Release: ${APPLICATION_LABEL} ${FULL_VERSION_NAME}" + git pull --rebase -X ours + git log -n 5 + + set +e + git push + GIT_RESULT=$? + set -e + + if [ $GIT_RESULT -gt 0 ]; then + echo "Push rejected, trying again once in 5 seconds" + sleep 5 + git pull --rebase -X ours + git log -n 5 + git push + fi + + echo "${APP_NAME}_sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT + echo "sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT + + - name: Summary + if: ${{ contains(matrix.releaseTarget, 'github') }} + uses: actions/github-script@v7 + env: + bump_sha: ${{ steps.commit.outputs.sha }} + applicationId: ${{ steps.appinfo.outputs.APPLICATION_ID }} + oldFullVersion: ${{ steps.appinfo.outputs.VERSION_NAME }}${{ steps.appinfo.outputs.VERSION_NAME_SUFFIX }} + oldVersionCode: ${{ steps.appinfo.outputs.VERSION_CODE }} + newFullVersion: ${{ steps.appinfo.outputs.VERSION_NAME }}${{ steps.bump_version_suffix.outputs.SUFFIX || steps.appinfo.outputs.VERSION_NAME_SUFFIX }} + newVersionCode: ${{ steps.bump_version_code.outputs.CODE }} + with: + script: | + let env = process.env; + console.log(env); + await core.summary + .addRaw(`Version for ${env.applicationId} bumped from ${env.oldFullVersion} (${env.oldVersionCode}) to ${env.newFullVersion} (${env.newVersionCode}) in `) + .addLink(process.env.bump_sha, `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/commit/${env.bump_sha}`) + .addEOL() + .write(); + + build_unsigned: + name: Build Unsigned + runs-on: ubuntu-latest + timeout-minutes: 90 + if: ${{ !failure() && !cancelled() }} # Run if release_commit is skipped + needs: [dump_config, get_environment, release_commit] + strategy: + matrix: + include: "${{ fromJSON(needs.dump_config.outputs.matrixInclude) }}" + environment: ${{ needs.get_environment.outputs.releaseEnv }} + steps: + - name: Get release sha + id: sha + shell: bash + env: + THUNDERBIRD_SHA: ${{ needs.release_commit.outputs.thunderbird_sha }} + K9MAIL_SHA: ${{ needs.release_commit.outputs.k9mail_sha }} + APP_NAME: ${{ matrix.appName }} + run: | + case "${APP_NAME}" in + thunderbird) APP_SHA=$THUNDERBIRD_SHA ;; + k9mail) APP_SHA=$K9MAIL_SHA ;; + *) APP_SHA=$GITHUB_SHA ;; + esac + + echo "app_sha=$APP_SHA" >> $GITHUB_OUTPUT + cat $GITHUB_OUTPUT + + - name: Checkout repository + uses: actions/checkout@v4 + with: + ref: ${{ steps.sha.outputs.app_sha }} + + - name: Copy CI gradle.properties + shell: bash + run: mkdir -p ~/.gradle ; cp .github/ci-gradle.properties ~/.gradle/gradle.properties + + - uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: 17 + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + with: + cache-disabled: ${{ needs.dump_config.outputs.releaseType == 'beta' || needs.dump_config.outputs.releaseType == 'release' }} + add-job-summary: on-failure + + - name: Build It + shell: bash + env: + PACKAGE_FORMAT: ${{ matrix.packageFormat }} + PACKAGE_FLAVOR: ${{ matrix.packageFlavor }} + APP_NAME: ${{ matrix.appName }} + RELEASE_TYPE: ${{ vars.RELEASE_TYPE }} + run: | + if [[ "$APP_NAME" = "thunderbird" && "$PACKAGE_FORMAT" = "apk" ]]; then + BUILD_COMMAND="assemble${PACKAGE_FLAVOR^}${RELEASE_TYPE^}" + elif [[ "$APP_NAME" = "thunderbird" && "${PACKAGE_FORMAT}" = "aab" ]]; then + BUILD_COMMAND="bundle${PACKAGE_FLAVOR^}${RELEASE_TYPE^}" + elif [[ "$APP_NAME" = "k9mail" ]]; then + BUILD_COMMAND="assembleRelease" + fi + + echo "BUILDING: :app-${APP_NAME}:${BUILD_COMMAND}" + ./gradlew clean :app-${APP_NAME}:${BUILD_COMMAND} --no-build-cache --no-configuration-cache + echo "Status: $?" + + - name: Test It + if: ${{ !inputs.skipTests }} + shell: bash + run: ./gradlew testsOnCi + + - name: Move apps to upload directory + shell: bash + env: + PACKAGE_FORMAT: ${{ matrix.packageFormat }} + PACKAGE_FLAVOR: ${{ matrix.packageFlavor }} + APP_NAME: ${{ matrix.appName }} + RELEASE_TYPE: ${{ vars.RELEASE_TYPE }} + UPLOAD_PATH: "uploads" + run: | + OUT_BASE=app-${APP_NAME}/build/outputs/ + + if [[ "$APP_NAME" = "thunderbird" && "$PACKAGE_FORMAT" = "apk" ]]; then + OUT_PATH="${OUT_BASE}/apk/${PACKAGE_FLAVOR}/${RELEASE_TYPE}" + OUT_FILE="app-${APP_NAME}-${PACKAGE_FLAVOR}-${RELEASE_TYPE}-unsigned.apk" + UPLOAD_FILE="${APP_NAME}-${PACKAGE_FLAVOR}-${RELEASE_TYPE}.apk" + elif [[ "$APP_NAME" = "thunderbird" && "${PACKAGE_FORMAT}" = "aab" ]]; then + OUT_PATH="${OUT_BASE}/bundle/${PACKAGE_FLAVOR}${RELEASE_TYPE^}" + OUT_FILE="app-${APP_NAME}-${PACKAGE_FLAVOR}-${RELEASE_TYPE}.aab" + UPLOAD_FILE="${APP_NAME}-${PACKAGE_FLAVOR}-${RELEASE_TYPE}.aab" + elif [[ "$APP_NAME" = "k9mail" ]]; then + OUT_PATH="${OUT_BASE}/apk/release" + OUT_FILE="app-${APP_NAME}-release-unsigned.apk" + UPLOAD_FILE="${APP_NAME}-default-${RELEASE_TYPE}.apk" + else + echo "PACKAGE_FORMAT $PACKAGE_FORMAT is unknown. Exiting." + exit 23 + fi + + mkdir -p "${UPLOAD_PATH}" + + if [[ -f "${OUT_PATH}/${OUT_FILE}" ]]; then + mv -f "${OUT_PATH}/${OUT_FILE}" "${UPLOAD_PATH}/${UPLOAD_FILE}" + else + echo "Build file ${OUT_PATH}/${OUT_FILE} not found. Exiting." + ls -l ${OUT_PATH} + exit 24 + fi + echo "Upload contents:" + ls -l ${UPLOAD_PATH}/ + + - name: Upload unsigned + uses: actions/upload-artifact@v4 + env: + UPLOAD_PATH: "uploads" + with: + name: unsigned-${{ matrix.appName }}-${{ matrix.packageFormat }}-${{ matrix.packageFlavor || 'default' }} + path: ${{ env.UPLOAD_PATH }}/ + if-no-files-found: error + + sign_mobile: + name: Sign Packages + runs-on: ubuntu-latest + if: ${{ !failure() && !cancelled() }} # Run if previous step is skipped + needs: [build_unsigned, dump_config] + strategy: + matrix: + include: "${{ fromJSON(needs.dump_config.outputs.matrixInclude) }}" + environment: ${{ matrix.appName }}_${{ needs.dump_config.outputs.releaseType }}_${{ matrix.packageFlavor || 'default' }} + env: + RELEASE_TYPE: ${{ needs.dump_config.outputs.releaseType }} + steps: + - uses: actions/download-artifact@v4 + with: + name: unsigned-${{ matrix.appName }}-${{ matrix.packageFormat }}-${{ matrix.packageFlavor || 'default' }} + path: uploads/ + + - uses: noriban/sign-android-release@5f144321d3c7c2233266e78b42360345d8bbe403 # v5.1 + name: Sign package + with: + releaseDirectory: uploads/ + signingKeyBase64: ${{ secrets.SIGNING_KEY }} + alias: ${{ secrets.KEY_ALIAS }} + keyPassword: ${{ secrets.KEY_PASSWORD }} + keyStorePassword: ${{ secrets.KEY_STORE_PASSWORD }} + + - name: Rename assets + if: ${{ matrix.packageFormat == 'apk' }} + env: + APP_NAME: ${{ matrix.appName }} + PACKAGE_FLAVOR: ${{ matrix.packageFlavor || 'default' }} + run: | + mv uploads/${APP_NAME}-${PACKAGE_FLAVOR}-${RELEASE_TYPE}-signed.apk uploads/${APP_NAME}-${PACKAGE_FLAVOR}-${RELEASE_TYPE}.apk + rm uploads/*-aligned.apk + + - name: Remove JKS file + shell: bash + run: | + rm -f uploads/*.jks + + - name: Upload signed + uses: actions/upload-artifact@v4 + with: + name: signed-${{ matrix.appName }}-${{ matrix.packageFormat }}-${{ matrix.packageFlavor || 'default' }} + if-no-files-found: error + path: | + uploads/*.apk + uploads/*.aab + + notify_pre_publish: + name: Notify Publish Approval + needs: [dump_config, sign_mobile, notify_build_start] + if: ${{ needs.dump_config.outputs.releaseType == 'beta' || needs.dump_config.outputs.releaseType == 'release' }} + runs-on: ubuntu-latest + environment: notify_matrix + steps: + - uses: kewisch/action-matrix-notify@v1 + with: + matrixHomeserver: ${{ vars.MATRIX_NOTIFY_HOMESERVER }} + matrixRoomId: ${{ vars.MATRIX_NOTIFY_ROOM }} + matrixToken: ${{ secrets.MATRIX_NOTIFY_TOKEN }} + message: >- + 🟡 [${{ needs.dump_config.outputs.releaseType }} build ${{ github.run_number}}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) + waiting for publish approval (triggered by ${{ needs.notify_build_start.outputs.actorLink }}) + + pre_publish: + # This is a holding job meant to require approval before proceeding with the publishing jobs below + # The environment has a deployment protection rule requiring approval from a set of named reviewers + # before proceeding. + name: Wait for Approval + needs: [dump_config, sign_mobile] + if: ${{ needs.dump_config.outputs.releaseType == 'beta' || needs.dump_config.outputs.releaseType == 'release' }} + environment: publish_hold + runs-on: ubuntu-latest + steps: + - name: Approval + shell: bash + run: | + true + + publish_release: + name: Publish Release + needs: [pre_publish, dump_config, release_commit] + if: ${{ !failure() && !cancelled() }} # Run if previous step is skipped + runs-on: ubuntu-latest + strategy: + matrix: + include: "${{ fromJSON(needs.dump_config.outputs.matrixInclude) }}" + environment: publish_release + outputs: + thunderbird_release_url: ${{ steps.summary.outputs.thunderbird_release_url }} + k9mail_release_url: ${{ steps.summary.outputs.k9mail_release_url }} + thunderbird_full_version_name: ${{ steps.summary.outputs.thunderbird_full_version_name }} + k9mail_full_version_name: ${{ steps.summary.outputs.k9mail_full_version_name }} + env: + RELEASE_TYPE: ${{ needs.dump_config.outputs.releaseType }} + APP_NAME: ${{ matrix.appName }} + PACKAGE_FLAVOR: ${{ matrix.packageFlavor || 'default' }} + PACKAGE_FORMAT: ${{ matrix.packageFormat }} + steps: + - uses: actions/download-artifact@v4 + with: + name: signed-${{ matrix.appName }}-${{ matrix.packageFormat }}-${{ matrix.packageFlavor || 'default' }} + path: "uploads/" + + - name: Get Package Info + id: pkginfo + shell: bash + env: + GH_TOKEN: ${{ github.token }} + run: | + PKG_FILE="uploads/${APP_NAME}-${PACKAGE_FLAVOR}-${RELEASE_TYPE}.${PACKAGE_FORMAT}" + if [[ "${PACKAGE_FORMAT}" == "apk" ]]; then + LATEST_BUILD_TOOLS=$(ls -d ${ANDROID_SDK_ROOT}/build-tools/* | sort -V | tail -n1) + AAPT=${LATEST_BUILD_TOOLS}/aapt + + VERSION_NAME=$(${AAPT} dump badging $PKG_FILE | sed -n "s/^package:.*versionName='\([^']*\)'.*$/\1/p") + VERSION_CODE=$(${AAPT} dump badging $PKG_FILE | sed -n "s/^package:.*versionCode='\([^']*\)'.*$/\1/p") + APPLICATION_ID=$(${AAPT} dump badging $PKG_FILE | sed -n "s/^package: name='\([^']*\)'.*$/\1/p") + APPLICATION_LABEL=$(${AAPT} dump badging $PKG_FILE | sed -n "s/^application-label:'\([^']*\)'.*$/\1/p") + elif [[ "${PACKAGE_FORMAT}" == "aab" ]]; then + if [ ! -f bundletool.jar ]; then + gh release download -R google/bundletool -p 'bundletool-all-*.jar' -O bundletool.jar + fi + BUNDLETOOL="java -jar bundletool.jar" + + VERSION_NAME=$(${BUNDLETOOL} dump manifest --bundle ${PKG_FILE} --xpath '/manifest/@android:versionName') + VERSION_CODE=$(${BUNDLETOOL} dump manifest --bundle ${PKG_FILE} --xpath '/manifest/@android:versionCode') + APPLICATION_ID=$(${BUNDLETOOL} dump manifest --bundle ${PKG_FILE} --xpath '/manifest/@package') + + # Unfortunately no application label in the bundle + case "$APPLICATION_ID" in + net.thunderbird.android) APPLICATION_LABEL="Thunderbird" ;; + net.thunderbird.android.beta) APPLICATION_LABEL="Thunderbird Beta" ;; + net.thunderbird.android.daily) APPLICATION_LABEL="Thunderbird Daily" ;; + com.fsck.k9) APPLICATION_LABEL="K-9 Mail" ;; + esac + fi + + echo "TAG_NAME=${APP_NAME^^}_${VERSION_NAME//./_}" >> $GITHUB_OUTPUT + echo "FULL_VERSION_NAME=${APPLICATION_LABEL} ${VERSION_NAME}" >> $GITHUB_OUTPUT + echo "VERSION_NAME=${VERSION_NAME}" >> $GITHUB_OUTPUT + echo "VERSION_CODE=${VERSION_CODE}" >> $GITHUB_OUTPUT + echo "APPLICATION_ID=${APPLICATION_ID}" >> $GITHUB_OUTPUT + + cat $GITHUB_OUTPUT + + - name: Rename release assets + id: rename + shell: bash + env: + VERSION_NAME: ${{ steps.pkginfo.outputs.VERSION_NAME }} + run: | + PKG_FILE="${APP_NAME}-${PACKAGE_FLAVOR}-${RELEASE_TYPE}.${PACKAGE_FORMAT}" + PKG_FILE_PRETTY="${APP_NAME}-${VERSION_NAME}.${PACKAGE_FORMAT}" + mv uploads/${PKG_FILE} uploads/${PKG_FILE_PRETTY} + + echo "PKG_FILE=${PKG_FILE_PRETTY}" >> $GITHUB_OUTPUT + ls -l uploads/${PKG_FILE_PRETTY} + + - name: App Token Generate + uses: actions/create-github-app-token@v1 + if: ${{ contains(matrix.releaseTarget, 'github') && vars.RELEASER_APP_CLIENT_ID }} + id: app-token + with: + app-id: ${{ vars.RELEASER_APP_CLIENT_ID }} + private-key: ${{ secrets.RELEASER_APP_PRIVATE_KEY }} + + - name: Get release sha and notes + id: shanotes + shell: bash + env: + THUNDERBIRD_GITHUB_NOTES: ${{ needs.release_commit.outputs.thunderbird_github_notes }} + K9MAIL_GITHUB_NOTES: ${{ needs.release_commit.outputs.k9mail_github_notes }} + THUNDERBIRD_SHA: ${{ needs.release_commit.outputs.thunderbird_sha }} + K9MAIL_SHA: ${{ needs.release_commit.outputs.k9mail_sha }} + APP_NAME: ${{ matrix.appName }} + run: | + app_sha_name=${APP_NAME^^}_SHA + echo "app_sha=${!app_sha_name}" >> $GITHUB_OUTPUT + + app_relnotes_name=${APP_NAME^^}_GITHUB_NOTES + echo "app_github_notes<> $GITHUB_OUTPUT + echo "${!app_relnotes_name}" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + cat $GITHUB_OUTPUT + + - name: Publish to GitHub Releases + id: publish_gh + if: ${{ contains(matrix.releaseTarget, 'github') }} + uses: softprops/action-gh-release@c062e08bd532815e2082a85e87e3ef29c3e6d191 # v2.0.8 + with: + token: ${{ steps.app-token.outputs.token || github.token }} + target_commitish: ${{ steps.shanotes.outputs.app_sha }} + tag_name: ${{ steps.pkginfo.outputs.TAG_NAME }} + name: ${{ steps.pkginfo.outputs.FULL_VERSION_NAME }} + body: ${{ steps.shanotes.outputs.app_github_notes }} + prerelease: ${{ env.RELEASE_TYPE != 'release' }} + fail_on_unmatched_files: true + files: | + uploads/${{ steps.rename.outputs.PKG_FILE }} + + - name: Adjust release notes for play store upload + if: ${{ !inputs.skipGooglePlay && contains(matrix.releaseTarget, 'play') && matrix.playTargetTrack }} + shell: bash + env: + VERSION_CODE: ${{ steps.pkginfo.outputs.VERSION_CODE }} + APPLICATION_ID: ${{ steps.pkginfo.outputs.APPLICATION_ID }} + REPO: ${{ github.repository }} + APP_SHA: ${{ steps.shanotes.outputs.app_sha }} + run: | + # r0adkll/upload-google-play expects the release notes in a different structure + FILEPATH=app-metadata/${APPLICATION_ID}/en-US/changelogs/${VERSION_CODE}.txt + mkdir whatsnew + wget -O whatsnew/whatsnew-en-US https://raw.githubusercontent.com/${REPO}/${APP_SHA}/${FILEPATH} + + - name: Publish to Google Play + id: publish_play + uses: r0adkll/upload-google-play@v1 + if: ${{ !inputs.skipGooglePlay && contains(matrix.releaseTarget, 'play') && matrix.playTargetTrack }} + with: + serviceAccountJsonPlainText: ${{ secrets.PLAY_STORE_ACCOUNT }} + packageName: ${{ steps.pkginfo.outputs.APPLICATION_ID }} + track: ${{ matrix.playTargetTrack }} + releaseName: ${{ steps.pkginfo.outputs.VERSION_NAME }} + status: completed + changesNotSentForReview: ${{ inputs.draftGooglePlay }} + whatsNewDirectory: whatsnew + releaseFiles: | + uploads/${{ steps.rename.outputs.PKG_FILE }} + + - name: Summary + uses: actions/github-script@v7 + id: summary + env: + tagName: ${{ steps.pkginfo.outputs.TAG_NAME }} + fullVersionName: ${{ steps.pkginfo.outputs.FULL_VERSION_NAME }} + ghReleaseUrl: ${{ steps.publish_gh.outputs.url }} + playTargetTrack: ${{ matrix.playTargetTrack }} + applicationId: ${{ steps.pkginfo.outputs.APPLICATION_ID }} + releaseTarget: ${{ matrix.releaseTarget }} + appSha: ${{ steps.shanotes.outputs.app_sha }} + appName: ${{ matrix.appName }} + skipGooglePlay: ${{ inputs.skipGooglePlay }} + with: + script: | + await core.summary + .addHeading(`${process.env.fullVersionName} (${process.env.applicationId})`, 2) + .write(); + core.setOutput(`${process.env.appName}_full_version_name`, process.env.fullVersionName); + + if (!process.env.releaseTarget) { + await core.summary + .addRaw(`Artifact-only build at `) + .addLink(process.env.appSha, `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/commit/${process.env.appSha}`) + .addEOL() + .write(); + } else if (process.env.ghReleaseUrl) { + await core.summary + .addRaw(`Tag ${process.env.tagName} at `) + .addLink(process.env.appSha, `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/commit/${process.env.appSha}`) + .addEOL() + .addRaw(`Released to Github at `) + .addLink(process.env.ghReleaseUrl, process.env.ghReleaseUrl) + .addEOL() + .write(); + + core.setOutput(`${process.env.appName}_release_url`, process.env.ghReleaseUrl); + } + + if (process.env.skipGooglePlay != "true" && process.env.playTargetTrack && process.env.releaseTarget.includes("play")) { + await core.summary.addRaw(`Released to the ${process.env.playTargetTrack} track on Google Play`, true).write(); + } + + notify_build_result: + name: Notify Build Result + if: ${{ always() }} + needs: [dump_config, release_commit, build_unsigned, sign_mobile, publish_release, notify_build_start] + runs-on: ubuntu-latest + environment: notify_matrix + steps: + - name: Get previous workflow status + uses: Mercymeilya/last-workflow-status@3418710aefe8556d73b6f173a0564d38bcfd9a43 + id: last_status + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + + - name: Info + uses: actions/github-script@v7 + id: info + env: + needs: ${{ toJSON(needs) }} + with: + script: | + let needs = JSON.parse(process.env.needs); + let failures = []; + for (let [job, need] of Object.entries(needs)) { + if (need.result == 'failure') { + failures.push(job.replace(/([-_])/g, "\\$1")); + } + } + core.setOutput("fail_steps", failures.join(`, `)); + + - name: Notify Failure + if: ${{ vars.MATRIX_NOTIFY_ROOM && contains(needs.*.result, 'failure') }} + uses: kewisch/action-matrix-notify@v1 + with: + matrixHomeserver: ${{ vars.MATRIX_NOTIFY_HOMESERVER }} + matrixRoomId: ${{ vars.MATRIX_NOTIFY_ROOM }} + matrixToken: ${{ secrets.MATRIX_NOTIFY_TOKEN }} + message: >- + 🔴 [${{ needs.dump_config.outputs.releaseType }} build ${{ github.run_number}}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) + has failed at step ${{ steps.info.outputs.fail_steps }} (triggered by ${{ needs.notify_build_start.outputs.actorLink }}) + + - name: Notify Cancelled + if: ${{ vars.MATRIX_NOTIFY_ROOM && !contains(needs.*.result, 'failure') && contains(needs.*.result, 'cancelled') }} + uses: kewisch/action-matrix-notify@v1 + with: + matrixHomeserver: ${{ vars.MATRIX_NOTIFY_HOMESERVER }} + matrixRoomId: ${{ vars.MATRIX_NOTIFY_ROOM }} + matrixToken: ${{ secrets.MATRIX_NOTIFY_TOKEN }} + message: >- + ⚪ [${{ needs.dump_config.outputs.releaseType }} build ${{ github.run_number}}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) + was cancelled + + - name: Notify Success (Beta/Release) + if: ${{ vars.MATRIX_NOTIFY_ROOM && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') && (needs.dump_config.outputs.releaseType == 'beta' || needs.dump_config.outputs.releaseType == 'release') }} + uses: kewisch/action-matrix-notify@v1 + with: + matrixHomeserver: ${{ vars.MATRIX_NOTIFY_HOMESERVER }} + matrixRoomId: ${{ vars.MATRIX_NOTIFY_ROOM }} + matrixToken: ${{ secrets.MATRIX_NOTIFY_TOKEN }} + message: >- + 🟢 [${{ needs.dump_config.outputs.releaseType }} build ${{ github.run_number}}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) + has succeeded (triggered by ${{ needs.notify_build_start.outputs.actorLink }}) + + - name: Thunderbird Publish URL (Beta/Release) + if: ${{ vars.MATRIX_NOTIFY_ROOM && needs.publish_release.outputs.thunderbird_release_url && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') }} + uses: kewisch/action-matrix-notify@v1 + with: + matrixHomeserver: ${{ vars.MATRIX_NOTIFY_HOMESERVER }} + matrixRoomId: ${{ vars.MATRIX_NOTIFY_ROOM }} + matrixToken: ${{ secrets.MATRIX_NOTIFY_TOKEN }} + message: >- + ${{ needs.publish_release.outputs.thunderbird_full_version_name }} [is available](${{ needs.publish_release.outputs.thunderbird_release_url }}) + + - name: K-9 Mail Publish URL (Beta/Release) + if: ${{ vars.MATRIX_NOTIFY_ROOM && needs.publish_release.outputs.k9mail_release_url && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') }} + uses: kewisch/action-matrix-notify@v1 + with: + matrixHomeserver: ${{ vars.MATRIX_NOTIFY_HOMESERVER }} + matrixRoomId: ${{ vars.MATRIX_NOTIFY_ROOM }} + matrixToken: ${{ secrets.MATRIX_NOTIFY_TOKEN }} + message: >- + ${{ needs.publish_release.outputs.k9mail_full_version_name }} [is available](${{ needs.publish_release.outputs.k9mail_release_url }}) + + - name: Notify Success (Daily) + if: ${{ vars.MATRIX_NOTIFY_ROOM && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') && needs.dump_config.outputs.releaseType == 'daily' && steps.last_status.outputs.last_status == 'failure' }} + uses: kewisch/action-matrix-notify@v1 + with: + matrixHomeserver: ${{ vars.MATRIX_NOTIFY_HOMESERVER }} + matrixRoomId: ${{ vars.MATRIX_NOTIFY_ROOM }} + matrixToken: ${{ secrets.MATRIX_NOTIFY_TOKEN }} + message: >- + 🟢 [${{ needs.dump_config.outputs.releaseType }} build ${{ github.run_number}}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) + has recovered diff --git a/.gitignore b/.gitignore index a0a1a437ab2..485ed5e1868 100644 --- a/.gitignore +++ b/.gitignore @@ -1,32 +1,42 @@ -# Local per-repo rules can be added to the .git/info/exclude file in your -# repo. These rules are not committed with the repo so they are not shared -# with others. This method can be used for locally-generated files that you -# don’t expect other users to generate, like files created by your editor. -.DS_Store -.settings -.classpath -bin -captures -coverage -coverage.ec -coverage.em -gen -javadoc -junit-report.xml -lint-results.*ml -lint-results_files +# Gradle +.gradle/ local.properties -monkey.txt -*~ -*.iws -atlassian-ide-plugin.xml -target -build -.gradle -out -build.xml -proguard-project.txt -.idea/ -*.iml -user-manual/screenshots -user-manual/output + +# Kotlin +.kotlin/ + +# Generated folders +bin/ +build/ +gen/ +out/ + +# Generated files +*.aab +*.apk +*.ap_ +*.class +*.dex + +# Keystore files +*.jks +*.keystore + +# Signing files +.signing/ +*.signing.properties + +# IDEA/Android Studio ignores +*iml +.idea/* + +# IDEA/Android Studio includes +!.idea/icon.png +!.idea/codeStyles/ +!.idea/fileTemplates/ + +# Android Studio captures folder +captures/ + +# Mac thumbnail db +.DS_Store diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index 0858e3ff9b6..7f630e90f77 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -3,6 +3,26 @@