Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Thunderbird/.github/workflows/   (Thunderbird Email Client Version 5.6©)  Datei vom 22.9.2026 mit Größe 55 kB image not shown  

Quelle  shippable_builds.yml  Sprache: unbekannt

 
Spracherkennung für: .yml vermutete Sprache: Fortran {Fortran[24] VHDL[16] ADA[14]} [Methode: maximale Elemente, drei Dimensionen]

---
name: Shippable Build & Signing
on:
  workflow_call:
  workflow_dispatch:
    inputs:
      pointRelease:
        type: boolean
        description: Bump minor versionName (e.g. 21.0 -> 21.1) for a point release
      skipBetaBump:
        type: boolean
        description: Skip version bump (beta)
      skipThunderbird:
        type: boolean
        description: Skip building Thunderbird
      skipK9Mail:
        type: boolean
        description: Skip building K-9 Mail
      skipTests:
        type: boolean
        description: Skip running tests
      skipGooglePlay:
        type: boolean
        description: Skip Google Play publish
      draftGooglePlay:
        type: boolean
        description: Leave Play Store version in draft state
      skipFtp:
        type: boolean
        description: Skip FTP upload
      uploadFtpStage:
        type: boolean
        description: Upload to FTP stage instead of prod

permissions:
  contents: read

jobs:
  get_environment:
    name: Determine Release Environment
    runs-on: ubuntu-latest
    outputs:
      releaseEnv: ${{ steps.releaseEnv.outputs.result }}
    steps:
      - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
        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 }}
      releaseDate: ${{ steps.dump.outputs.releaseDate }}
      releaseType: ${{ vars.RELEASE_TYPE }}
    steps:
      - name: Show Environment
        uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
        id: dump
        env:
          matrixInclude: ${{ vars.MATRIX_INCLUDE }}
          releaseType: ${{ vars.RELEASE_TYPE }}
          pointRelease: ${{ inputs.pointRelease }}
          skipBetaBump: ${{ inputs.skipBetaBump }}
          skipThunderbird: ${{ inputs.skipThunderbird }}
          skipK9Mail: ${{ inputs.skipK9Mail }}
          skipTests: ${{ inputs.skipTests }}
          skipGooglePlay: ${{ inputs.skipGooglePlay }}
          draftGooglePlay: ${{ inputs.draftGooglePlay }}
          skipFtp: ${{ inputs.skipFtp }}
          uploadFtpStage: ${{ inputs.uploadFtpStage }}
        with:
          script: |
            const NOW = new Date();
            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("releaseDate", NOW.toString());
            core.setOutput("matrixInclude", matrixFull);

            await core.summary
              .addRaw(`Beginning a <b>${process.env.releaseType}</b> 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 },
                  { data: item.packageFormat },
                  { data: item.releaseTarget?.replace(/\|/g, ", ") || "artifact only" },
                  { data: item.playTargetTrack || "none" },
                ])
              ])
              .write();

              if (process.env.pointRelease == "true" && process.env.releaseType == "release") {
                await core.summary.addList(["Point release: minor versionName will be bumped"]).write();
              }
              if (process.env.skipBetaBump == "true" && process.env.releaseType == "beta") {
                await core.summary.addList(["Beta bump is being skipped"]).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.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();
              }
              if (process.env.skipFtp == "true") {
                await core.summary.addList(["FTP upload is being skipped"]).write();
              } else if (process.env.uploadFtpStage == "true") {
                await core.summary.addList(["FTP upload destination is stage (ftp.stage.mozaws.net)"]).write();
              } else if (process.env.uploadFtpStage == "false") {
                await core.summary.addList(["FTP upload destination is prod (ftp.mozilla.org)"]).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@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
        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@3c45d89acd032c84b955b54c8a9001833ac91d17 # 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 ]
    environment: ${{ needs.get_environment.outputs.releaseEnv }}
    strategy:
      max-parallel: 1
      matrix:
        include: "${{ fromJSON(needs.dump_config.outputs.matrixInclude) }}"
    outputs:
      k9mail_sha: ${{ steps.commit.outputs.k9mail_sha }}
      thunderbird_sha: ${{ steps.commit.outputs.thunderbird_sha }}
      k9mail_github_notes: ${{ steps.append_contributors.outputs.k9mail_github_notes }}
      thunderbird_github_notes: ${{ steps.append_contributors.outputs.thunderbird_github_notes }}
      old_version_code: ${{ steps.new_version_code.outputs.old_version_code }}
      new_version_code: ${{ steps.new_version_code.outputs.new_version_code }}
    steps:
      - name: App Token Generate
        uses: actions/create-github-app-token@64ab729a6294000f5aa4a4bc0b73d1f32bc45510 # v3.3.0
        if: ${{ vars.BOT_CLIENT_ID }}
        id: app-token
        with:
          client-id: ${{ vars.BOT_CLIENT_ID }}
          private-key: ${{ secrets.BOT_PRIVATE_KEY }}

      - name: Checkout repository
        if: ${{ contains(matrix.releaseTarget, 'github') || needs.dump_config.outputs.releaseType == 'daily' }}
        uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
        with:
          fetch-depth: 0
          token: ${{ steps.app-token.outputs.token || github.token }}

      - name: Setup Gradle environment
        if: ${{ contains(matrix.releaseTarget, 'github') || needs.dump_config.outputs.releaseType == 'daily' }}
        uses: ./.github/actions/setup-gradle
        with:
          disable-cache: "${{ contains(fromJSON('[\"beta\", \"release\"]'), needs.dump_config.outputs.releaseType) }}"
          add-job-summary: never

      - name: Get application info
        id: appinfo
        shell: bash
        if: ${{ contains(matrix.releaseTarget, 'github') || needs.dump_config.outputs.releaseType == 'daily' }}
        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${PACKAGE_FLAVOR^}${RELEASE_TYPE^} -PoutputFile=${GITHUB_OUTPUT}

      - name: Determine new version code
        id: new_version_code
        if: ${{ contains(matrix.releaseTarget, 'github') || needs.dump_config.outputs.releaseType == 'daily' }}
        shell: bash
        env:
          APP_NAME: ${{ matrix.appName }}
          OLD_VERSION_CODE: ${{ steps.appinfo.outputs.VERSION_CODE }}
          RELEASE_TYPE: ${{ vars.RELEASE_TYPE }}
          RELEASE_DATE: ${{ needs.dump_config.outputs.releaseDate }}
        run: |
          date_version_code() {
            # Daily uses date-based calculation for version code in yDDDHHmm format
            # y: Years since 2023 (e.g. 2024 is 1)
            # DDD: Day of the year (3 digits, zero-padded)
            # HH: Hour of the day (2 digits, 00-23)
            # mm: Minute of the hour (2 digits)
            datetime="$1"
            year=$(( $(date -d "${datetime}" +"%Y") - 2023 ))
            day=$(date -d "${datetime}" +"%j")
            time=$(date -d "${datetime}" +"%H%M")
            echo "${year}${day}${time}"
          }
          # Android version codes max out at 2100000000. If we accidentally reach that number then
          # all we can do is start with a new application id.
          MAX_VERSION_CODE="2100000000"
          if [[ "$RELEASE_TYPE" = "beta" || "$RELEASE_TYPE" = "release" ]]; then
            NEW_VERSION_CODE=$(($OLD_VERSION_CODE + 1))
          elif [[ "$RELEASE_TYPE" = "daily" ]]; then
            NEW_VERSION_CODE=$(date_version_code "$RELEASE_DATE")
          fi
          if [[ "$NEW_VERSION_CODE" -gt "$MAX_VERSION_CODE" ]]; then
            echo "New version code ${NEW_VERSION_CODE} is greater than ${MAX_VERSION_CODE}"
            exit 1
          fi
          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 "old_version_code=${OLD_VERSION_CODE}" | tee -a $GITHUB_OUTPUT
          echo "new_version_code=${NEW_VERSION_CODE}" | tee -a $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: Bump version name (point release)
        id: bump_version_name
        if: ${{ inputs.pointRelease && contains(matrix.releaseTarget, 'github') && vars.RELEASE_TYPE == 'release' }}
        shell: bash
        env:
          APP_NAME: ${{ matrix.appName }}
          OLD_VERSION_NAME: ${{ steps.appinfo.outputs.VERSION_NAME }}
        run: |
          NEW_VERSION_NAME=$(echo "$OLD_VERSION_NAME" | awk -F '.' '{print $1"."$2+1}')
          sed "s/versionName = \"$OLD_VERSION_NAME\"/versionName = \"$NEW_VERSION_NAME\"/" 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 "VERSION_NAME=${NEW_VERSION_NAME}" >> $GITHUB_OUTPUT

          cat $GITHUB_OUTPUT

      - name: Render Release Notes
        id: render_notes
        if: "${{ contains(matrix.releaseTarget, 'github') && contains(fromJSON('[\"beta\", \"release\"]'), needs.dump_config.outputs.releaseType) }}"
        shell: bash
        env:
          APP_NAME: ${{ matrix.appName }}
          APPLICATION_ID: ${{ steps.appinfo.outputs.APPLICATION_ID }}
          APPLICATION_LABEL: ${{ steps.appinfo.outputs.APPLICATION_LABEL }}
          VERSION_CODE: ${{ steps.new_version_code.outputs.new_version_code }}
          FULL_VERSION_NAME: ${{ steps.bump_version_name.outputs.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/ci/render-notes.py ${APPLICATION_ID} ${FULL_VERSION_NAME} ${VERSION_CODE} ${GITHUB_NOTES_FILE}

          echo "${APP_NAME}_github_notes<<EOF" >> $GITHUB_OUTPUT
          cat $GITHUB_NOTES_FILE >> $GITHUB_OUTPUT
          echo "EOF" >> $GITHUB_OUTPUT

          echo "<h2>${APPLICATION_LABEL} ${FULL_VERSION_NAME} Release Notes (${VERSION_CODE})</h2>" | 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') && contains(fromJSON('[\"beta\", \"release\"]'), needs.dump_config.outputs.releaseType) }}"
        shell: bash
        env:
          APPLICATION_ID: ${{ steps.appinfo.outputs.APPLICATION_ID }}
          VERSION_CODE: ${{ steps.new_version_code.outputs.new_version_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') && contains(fromJSON('[\"beta\", \"release\"]'), needs.dump_config.outputs.releaseType) }}"
        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.bump_version_name.outputs.VERSION_NAME || steps.appinfo.outputs.VERSION_NAME }}${{ steps.bump_version_suffix.outputs.SUFFIX || steps.appinfo.outputs.VERSION_NAME_SUFFIX }}
          RELEASE_TYPE: ${{ vars.RELEASE_TYPE }}
          APP_SLUG: ${{ steps.app-token.outputs.app-slug || 'github-actions'}}
          APP_USER_ID: ${{ vars.BOT_USER_ID || '41898282' }}
        run: |
          git config --global user.name "${APP_SLUG}"
          git config --global user.email "${APP_USER_ID}+${APP_SLUG}[bot]@users.noreply.github.com"

          # We need the metadata to point to the right application for the release commit
          set -x
          git pull
          ls -l metadata
          rm metadata
          ln -sf app-metadata/${APPLICATION_ID} metadata
          ls -l metadata

          # Add changelogs, build version changes and metadata symlink
          git add ./app-metadata/${APPLICATION_ID}/en-US/changelogs/*
          CHANGELOG_DIR="./app-${APP_NAME}/src/${RELEASE_TYPE}/res/raw"
          git add "${CHANGELOG_DIR}/changelog_index.json" "${CHANGELOG_DIR}"/changelog_release_*.json
          git add ./app-${APP_NAME}/build.gradle.kts
          git add metadata

          # Ready to commit. There should be no race conditions with max-parallel set to 1.
          git status
          git commit -m "Release: ${APPLICATION_LABEL} ${FULL_VERSION_NAME}"
          git diff HEAD~1 HEAD
          git log -n 5
          git push
          set +x

          echo "${APP_NAME}_sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
          echo "sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT

      - name: Append Contributors
        id: append_contributors
        if: "${{ contains(matrix.releaseTarget, 'github') && contains(fromJSON('[\"beta\", \"release\"]'), needs.dump_config.outputs.releaseType) }}"
        shell: bash
        env:
          APP_NAME: ${{ matrix.appName }}
          RELEASE_TYPE: ${{ vars.RELEASE_TYPE }}
          APPLICATION_LABEL: ${{ steps.appinfo.outputs.APPLICATION_LABEL }}
          VERSION_CODE: ${{ steps.new_version_code.outputs.new_version_code }}
          FULL_VERSION_NAME: ${{ steps.bump_version_name.outputs.VERSION_NAME || steps.appinfo.outputs.VERSION_NAME }}${{ steps.bump_version_suffix.outputs.SUFFIX || steps.appinfo.outputs.VERSION_NAME_SUFFIX }}
          K9MAIL_GITHUB_NOTES: ${{ steps.render_notes.outputs.k9mail_github_notes }}
          THUNDERBIRD_GITHUB_NOTES: ${{ steps.render_notes.outputs.thunderbird_github_notes }}
          GITHUB_TOKEN: ${{ github.token }}
        run: |
          case "${APP_NAME}:${RELEASE_TYPE}" in
            thunderbird:beta) TAG_REGEX='^THUNDERBIRD_[0-9]+_0b[0-9]+$'
                              GITHUB_NOTES=$THUNDERBIRD_GITHUB_NOTES
                              ;;
            thunderbird:*) TAG_REGEX='^THUNDERBIRD_[0-9]+_[0-9]+$'
                           GITHUB_NOTES=$THUNDERBIRD_GITHUB_NOTES
                           ;;
            k9mail:*) TAG_REGEX='^K9MAIL_[0-9]+_[0-9]+$'
                      GITHUB_NOTES=$K9MAIL_GITHUB_NOTES
                      ;;
          esac

          TAG_NAME="${APP_NAME^^}_${FULL_VERSION_NAME//./_}"
          NOTES_FILE="$(mktemp -d)/github_notes.txt"
          CONTRIBUTORS_FILE="$(mktemp -d)/contributors.txt"
          echo "${GITHUB_NOTES}" >> "${NOTES_FILE}"

          python ./scripts/ci/contributor_list.py \
            --regex "$TAG_REGEX" \
            --head $(git rev-parse HEAD) \
            --changelog-head "$TAG_NAME" \
          "$CONTRIBUTORS_FILE"

          cat "${CONTRIBUTORS_FILE}" >> "${NOTES_FILE}"

          echo "${APP_NAME}_github_notes<<EOF" >> $GITHUB_OUTPUT
          cat $NOTES_FILE >> $GITHUB_OUTPUT
          echo "EOF" >> $GITHUB_OUTPUT

          echo "<h2>${APPLICATION_LABEL} ${FULL_VERSION_NAME} Contributors (${VERSION_CODE})</h2>" | tee -a $GITHUB_STEP_SUMMARY
          echo -e "\n\n\`\`\`" | tee -a $GITHUB_STEP_SUMMARY
          cat $CONTRIBUTORS_FILE | tee -a $GITHUB_STEP_SUMMARY
          echo -e "\`\`\`" | tee -a $GITHUB_STEP_SUMMARY

      - name: Summary
        if: ${{ contains(matrix.releaseTarget, 'github') || needs.dump_config.outputs.releaseType == 'daily' }}
        uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
        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.new_version_code.outputs.old_version_code }}
          newFullVersion: ${{ steps.bump_version_name.outputs.VERSION_NAME || steps.appinfo.outputs.VERSION_NAME }}${{ steps.bump_version_suffix.outputs.SUFFIX || steps.appinfo.outputs.VERSION_NAME_SUFFIX }}
          newVersionCode: ${{ steps.new_version_code.outputs.new_version_code }}
          releaseType: ${{ vars.RELEASE_TYPE }}
        with:
          script: |
            let env = process.env;
            console.log(env);
            if (process.env.releaseType == "daily") {
              await core.summary
                .addRaw(`Version for ${env.applicationId} changed from ${env.oldFullVersion} (${env.oldVersionCode}) to ${env.newFullVersion} (${env.newVersionCode}) (uncommitted) and will build from `)
                .addLink(process.env.GITHUB_SHA, `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/commit/${process.env.GITHUB_SHA}`)
                .addEOL()
                .write();
            } else {
              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 }}
          RELEASE_TYPE: ${{ vars.RELEASE_TYPE }}
          APP_NAME: ${{ matrix.appName }}
        run: |
          APP_SHA_NAME=${APP_NAME^^}_SHA
          APP_SHA="${!APP_SHA_NAME:-$GITHUB_SHA}"
          echo "app_sha=${APP_SHA}" >> $GITHUB_OUTPUT
          cat $GITHUB_OUTPUT

      - name: Checkout repository
        uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
        with:
          ref: ${{ steps.sha.outputs.app_sha }}

      - name: Setup Gradle environment
        uses: ./.github/actions/setup-gradle
        with:
          disable-cache: "${{ contains(fromJSON('[\"beta\", \"release\"]'), needs.dump_config.outputs.releaseType) }}"
          add-job-summary: on-failure

      - name: Set Version Code for Daily
        if: ${{ needs.dump_config.outputs.releaseType == 'daily' }}
        shell: bash
        env:
          APP_NAME: ${{ matrix.appName }}
          OLD_VERSION_CODE: ${{ needs.release_commit.outputs.old_version_code }}
          NEW_VERSION_CODE: ${{ needs.release_commit.outputs.new_version_code }}
        run: |
          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

      - 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="assemble${PACKAGE_FLAVOR^}Release"
          fi

          echo "BUILDING: :app-${APP_NAME}:${BUILD_COMMAND}"
          ./gradlew clean :app-${APP_NAME}:${BUILD_COMMAND} --no-build-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/${PACKAGE_FLAVOR}/release"
            OUT_FILE="app-${APP_NAME}-${PACKAGE_FLAVOR}-release-unsigned.apk"
            UPLOAD_FILE="${APP_NAME}-${PACKAGE_FLAVOR}-${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@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
        env:
          UPLOAD_PATH: "uploads"
        with:
          name: unsigned-${{ matrix.appName }}-${{ matrix.packageFormat }}-${{ matrix.packageFlavor }}
          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 }}
    env:
      RELEASE_TYPE: ${{ needs.dump_config.outputs.releaseType }}
    steps:
      - uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
        with:
          name: unsigned-${{ matrix.appName }}-${{ matrix.packageFormat }}-${{ matrix.packageFlavor }}
          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 }}
        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@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
        with:
          name: signed-${{ matrix.appName }}-${{ matrix.packageFormat }}-${{ matrix.packageFlavor }}
          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: "${{ contains(fromJSON('[\"beta\", \"release\"]'), needs.dump_config.outputs.releaseType) }}"
    runs-on: ubuntu-latest
    environment: notify_matrix
    steps:
      - uses: kewisch/action-matrix-notify@3c45d89acd032c84b955b54c8a9001833ac91d17 # 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: "${{ contains(fromJSON('[\"beta\", \"release\"]'), needs.dump_config.outputs.releaseType) }}"
    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_DATE: ${{ needs.dump_config.outputs.releaseDate }}
      RELEASE_TYPE: ${{ needs.dump_config.outputs.releaseType }}
      APP_NAME: ${{ matrix.appName }}
      PACKAGE_FLAVOR: ${{ matrix.packageFlavor}}
      PACKAGE_FORMAT: ${{ matrix.packageFormat }}
    permissions:
      id-token: 'write'  # For GCS publishing (ftp.mo)
    steps:
      - 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 }}
          RELEASE_TYPE: ${{ env.RELEASE_TYPE }}
        run: |
          app_sha_name=${APP_NAME^^}_SHA
          app_sha="${!app_sha_name:-$GITHUB_SHA}"
          echo "app_sha=${app_sha}" >> $GITHUB_OUTPUT

          if [[ "$RELEASE_TYPE" = "beta" || "$RELEASE_TYPE" = "release" ]]; then
            app_relnotes_name=${APP_NAME^^}_GITHUB_NOTES
            echo "app_github_notes<<EOF" >> $GITHUB_OUTPUT
            echo "${!app_relnotes_name}" >> $GITHUB_OUTPUT
            echo "EOF" >> $GITHUB_OUTPUT
          fi

          cat $GITHUB_OUTPUT

      - name: Checkout repository
        uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
        with:
          ref: ${{ steps.shanotes.outputs.app_sha }}

      - name: Download Artifacts
        uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
        with:
          name: signed-${{ matrix.appName }}-${{ matrix.packageFormat }}-${{ matrix.packageFlavor }}
          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@64ab729a6294000f5aa4a4bc0b73d1f32bc45510  # v3.3.0
        if: ${{ contains(matrix.releaseTarget, 'github') && vars.BOT_CLIENT_ID }}
        id: app-token
        with:
          client-id: ${{ vars.BOT_CLIENT_ID }}
          private-key: ${{ secrets.BOT_PRIVATE_KEY }}

      - name: Publish to GitHub Releases
        id: publish_gh
        if: ${{ contains(matrix.releaseTarget, 'github') }}
        uses: softprops/action-gh-release@efb35369e0ad2afab669f228072c1b0d510eae64  # v3.0.3
        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 }}
          VERSION_NAME: ${{ steps.pkginfo.outputs.VERSION_NAME }}
          APPLICATION_ID: ${{ steps.pkginfo.outputs.APPLICATION_ID }}
          REPO: ${{ github.repository }}
          APP_SHA: ${{ steps.shanotes.outputs.app_sha }}
          RELEASE_TYPE: ${{ env.RELEASE_TYPE }}
          APP_NAME: ${{ matrix.appName }}
        run: |
          # r0adkll/upload-google-play expects the release notes in a different structure
          mkdir whatsnew
          if [[ "$RELEASE_TYPE" = "beta" || "$RELEASE_TYPE" = "release" ]]; then
            FILEPATH=app-metadata/${APPLICATION_ID}/en-US/changelogs/${VERSION_CODE}.txt
            wget --header="Accept: application/vnd.github.v3.raw" -O whatsnew/whatsnew-en-US https://api.github.com/repos/${REPO}/contents/${FILEPATH}?ref=${APP_SHA}
          elif [[ "$RELEASE_TYPE" = "daily" ]]; then
            COMMIT_URL="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/commit/${GITHUB_SHA}"
            WORKFLOW_URL="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}"
            sed 's/^[[:space:]]*//' > whatsnew/whatsnew-en-US <<EOF
              ${APP_NAME^} ${VERSION_NAME} (${VERSION_CODE}) Daily Build

              The Daily version is an unstable testing and development platform, make sure you back up important data regularly!
              If you find any issues, get in touch at https://github.com/thunderbird/thunderbird-android/issues/

              - Built from GitHub commit: ${COMMIT_URL}
              - Built with GitHub workflow: ${WORKFLOW_URL}
          EOF
          fi

      - name: Publish to Google Play
        id: publish_play
        uses: r0adkll/upload-google-play@e738b9dd8f2476ea806d921b64aacd24f34515a5 # v1.1.5
        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 }} (${{ steps.pkginfo.outputs.VERSION_CODE }})
          status: completed
          changesNotSentForReview: ${{ inputs.draftGooglePlay }}
          whatsNewDirectory: whatsnew
          releaseFiles: |
            uploads/${{ steps.rename.outputs.PKG_FILE }}

      - name: Generate source tar
        id: generate_tar
        if: ${{ !inputs.skipFtp && contains(matrix.releaseTarget, 'ftp') && matrix.packageFormat == 'apk' }}
        shell: bash
        env:
          APP_SHA: ${{ steps.shanotes.outputs.app_sha }}
          TAG_NAME: ${{ steps.pkginfo.outputs.TAG_NAME }}
        run: |
          FTP_TAR_FILENAME=$(echo "${TAG_NAME,,}.tar.gz" | sed 's/_/-/g')
          git archive --format=tar.gz --output=uploads/${FTP_TAR_FILENAME} ${APP_SHA}
          ls -l uploads
          echo "FTP_TAR_FILENAME=${FTP_TAR_FILENAME}" | tee -a $GITHUB_OUTPUT

      - name: Prepare for FTP Uploads
        id: prepare_ftp
        if: ${{ !inputs.skipFtp && contains(matrix.releaseTarget, 'ftp') && matrix.packageFormat == 'apk' }}
        shell: bash
        env:
          RELEASE_TYPE: ${{ env.RELEASE_TYPE }}
          VERSION_NAME: ${{ steps.pkginfo.outputs.VERSION_NAME }}
          UPLOAD_FTP_STAGE: ${{ inputs.uploadFtpStage }}
          PKG_FILE: ${{ steps.rename.outputs.PKG_FILE }}
          FTP_TAR_FILENAME: ${{ steps.generate_tar.outputs.FTP_TAR_FILENAME }}
        run: |
          GCP_ENV=prod
          GCP_PROJECT=moz-fx-productdelivery-pr-38b5
          FTP_URL_PREFIX="https://ftp.mozilla.org/pub/thunderbird-mobile/android"
          if [[ "$UPLOAD_FTP_STAGE" = "true" ]]; then
            GCP_ENV=stage
            GCP_PROJECT=moz-fx-productdelivery-no-7d6a
            FTP_URL_PREFIX="https://ftp.stage.mozaws.net/pub/thunderbird-mobile/android"
          fi
          SERVICE_ACCOUNT="thunderbird-team-${GCP_ENV}@${GCP_PROJECT}.iam.gserviceaccount.com"
          WORKLOAD_IDENTITY_PROVIDER="projects/324168772199/locations/global/workloadIdentityPools/github-actions/providers/github-actions"

          FTP_SUBDIR="releases"
          FTP_LOCAL_PATH="ftp/${FTP_SUBDIR}/${VERSION_NAME}"
          FTP_DESTINATION="${GCP_PROJECT}-productdelivery/pub/thunderbird-mobile/android/${FTP_SUBDIR}/${VERSION_NAME}"
          FTP_URL_PATH="${FTP_URL_PREFIX}/${FTP_SUBDIR}/${VERSION_NAME}/"
          if [[ "$RELEASE_TYPE" = "daily" ]]; then
            YEAR=$(date -d "${RELEASE_DATE}" +"%Y")
            MONTH=$(date -d "${RELEASE_DATE}" +%m)
            DAY=$(date -d "${RELEASE_DATE}" +"%d")
            HOUR=$(date -d "${RELEASE_DATE}" +"%H")
            MIN=$(date -d "${RELEASE_DATE}" +"%M")
            SEC=$(date -d "${RELEASE_DATE}" +"%S")
            DATETIME_SUBDIR="${YEAR}/${MONTH}/${YEAR}-${MONTH}-${DAY}-${HOUR}-${MIN}-${SEC}-main"
            FTP_SUBDIR="nightly"
            FTP_LOCAL_PATH="ftp/${FTP_SUBDIR}/${DATETIME_SUBDIR}"
            FTP_LOCAL_PATH_NIGHTLY_LATEST="ftp/${FTP_SUBDIR}/latest-main"
            FTP_DESTINATION="${GCP_PROJECT}-productdelivery/pub/thunderbird-mobile/android/${FTP_SUBDIR}/${DATETIME_SUBDIR}"
            FTP_DESTINATION_NIGHTLY_LATEST="${GCP_PROJECT}-productdelivery/pub/thunderbird-mobile/android/${FTP_SUBDIR}/latest-main"
            FTP_URL_PATH="${FTP_URL_PREFIX}/${FTP_SUBDIR}/${DATETIME_SUBDIR}/"
            FTP_URL_PATH_NIGHTLY_LATEST="${FTP_URL_PREFIX}/${FTP_SUBDIR}/latest-main/"
          fi

          mkdir -p "${FTP_LOCAL_PATH}"
          cp "uploads/${PKG_FILE}" "${FTP_LOCAL_PATH}"
          cp "uploads/${FTP_TAR_FILENAME}" "${FTP_LOCAL_PATH}"
          echo "$(pwd)/${FTP_LOCAL_PATH}/" | sed -e "s/\.\///"
          ls "${FTP_LOCAL_PATH}"
          if [[ "$RELEASE_TYPE" = "daily" ]]; then
            mkdir -p "${FTP_LOCAL_PATH_NIGHTLY_LATEST}"
            cp "uploads/${PKG_FILE}" "${FTP_LOCAL_PATH_NIGHTLY_LATEST}"
            cp "uploads/${FTP_TAR_FILENAME}" "${FTP_LOCAL_PATH_NIGHTLY_LATEST}"
            echo "$(pwd)/${FTP_LOCAL_PATH}/" | sed -e "s/\.\///"
            ls "${FTP_LOCAL_PATH}"
          fi

          echo "SERVICE_ACCOUNT=${SERVICE_ACCOUNT}" | tee -a $GITHUB_OUTPUT
          echo "WORKLOAD_IDENTITY_PROVIDER=${WORKLOAD_IDENTITY_PROVIDER}" | tee -a $GITHUB_OUTPUT
          echo "FTP_LOCAL_PATH=${FTP_LOCAL_PATH}" | tee -a $GITHUB_OUTPUT
          echo "FTP_LOCAL_PATH_NIGHTLY_LATEST=${FTP_LOCAL_PATH_NIGHTLY_LATEST}" | tee -a $GITHUB_OUTPUT
          echo "FTP_DESTINATION=${FTP_DESTINATION}" | tee -a $GITHUB_OUTPUT
          echo "FTP_DESTINATION_NIGHTLY_LATEST=${FTP_DESTINATION_NIGHTLY_LATEST}" | tee -a $GITHUB_OUTPUT
          echo "FTP_URL_PATH=${FTP_URL_PATH}" | tee -a $GITHUB_OUTPUT
          echo "FTP_URL_PATH_NIGHTLY_LATEST=${FTP_URL_PATH_NIGHTLY_LATEST}" | tee -a $GITHUB_OUTPUT

      - name: Auth to GCS for FTP
        if: ${{ !inputs.skipFtp && contains(matrix.releaseTarget, 'ftp') && matrix.packageFormat == 'apk' }}
        uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # v3.0.0
        with:
          service_account: ${{ steps.prepare_ftp.outputs.SERVICE_ACCOUNT }}
          workload_identity_provider: ${{ steps.prepare_ftp.outputs.WORKLOAD_IDENTITY_PROVIDER }}
          gzip: false

      - name: GCS Upload of APK Package to FTP
        if: ${{ !inputs.skipFtp && contains(matrix.releaseTarget, 'ftp') && matrix.packageFormat == 'apk' }}
        uses: google-github-actions/upload-cloud-storage@6397bd7208e18d13ba2619ee21b9873edc94427a # v3.0.0
        with:
          path: ${{ steps.prepare_ftp.outputs.FTP_LOCAL_PATH }}/${{ steps.rename.outputs.PKG_FILE }}
          destination: ${{ steps.prepare_ftp.outputs.FTP_DESTINATION }}
          gzip: false

      - name: GCS Upload of APK Package to FTP Nightly Latest
        if: ${{ !inputs.skipFtp && contains(matrix.releaseTarget, 'ftp') && matrix.packageFormat == 'apk' && needs.dump_config.outputs.releaseType == 'daily'}}
        uses: google-github-actions/upload-cloud-storage@6397bd7208e18d13ba2619ee21b9873edc94427a # v3.0.0
        with:
          path: ${{ steps.prepare_ftp.outputs.FTP_LOCAL_PATH_NIGHTLY_LATEST }}/${{ steps.rename.outputs.PKG_FILE }}
          destination: ${{ steps.prepare_ftp.outputs.FTP_DESTINATION_NIGHTLY_LATEST }}
          gzip: false

      - name: GCS Upload of Source Tar to FTP
        if: ${{ !inputs.skipFtp && contains(matrix.releaseTarget, 'ftp') && matrix.packageFormat == 'apk' }}
        uses: google-github-actions/upload-cloud-storage@6397bd7208e18d13ba2619ee21b9873edc94427a # v3.0.0
        with:
          path: ${{ steps.prepare_ftp.outputs.FTP_LOCAL_PATH }}/${{ steps.generate_tar.outputs.FTP_TAR_FILENAME }}
          destination: ${{ steps.prepare_ftp.outputs.FTP_DESTINATION }}
          gzip: false

      - name: GCS Upload of Source Tar to FTP Nightly Latest
        if: ${{ !inputs.skipFtp && contains(matrix.releaseTarget, 'ftp') && matrix.packageFormat == 'apk' && needs.dump_config.outputs.releaseType == 'daily'}}
        uses: google-github-actions/upload-cloud-storage@6397bd7208e18d13ba2619ee21b9873edc94427a # v3.0.0
        with:
          path: ${{ steps.prepare_ftp.outputs.FTP_LOCAL_PATH_NIGHTLY_LATEST }}/${{ steps.generate_tar.outputs.FTP_TAR_FILENAME }}
          destination: ${{ steps.prepare_ftp.outputs.FTP_DESTINATION_NIGHTLY_LATEST }}
          gzip: false

      - name: Set up Cloud SDK for pruning
        if: ${{ !inputs.skipFtp && contains(matrix.releaseTarget, 'ftp') && matrix.packageFormat == 'apk' && needs.dump_config.outputs.releaseType == 'daily' }}
        uses: google-github-actions/setup-gcloud@aa5489c8933f4cc7a4f7d45035b3b1440c9c10db # v3.0.1

      - name: Prune FTP Nightly Latest
        continue-on-error: true
        if: ${{ !inputs.skipFtp && contains(matrix.releaseTarget, 'ftp') && matrix.packageFormat == 'apk' && needs.dump_config.outputs.releaseType == 'daily' }}
        shell: bash
        env:
          FTP_DESTINATION_NIGHTLY_LATEST: ${{ steps.prepare_ftp.outputs.FTP_DESTINATION_NIGHTLY_LATEST }}
          PKG_FILE: ${{ steps.rename.outputs.PKG_FILE }}
          FTP_TAR_FILENAME: ${{ steps.generate_tar.outputs.FTP_TAR_FILENAME }}
        run: |
          set -x
          gcloud storage ls "gs://${FTP_DESTINATION_NIGHTLY_LATEST}/" \
            | grep -vF "${PKG_FILE}" \
            | grep -vF "${FTP_TAR_FILENAME}" \
            | xargs -r gcloud storage rm

      - name: Summary
        uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
        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 }}
          releaseType: ${{ env.RELEASE_TYPE }}
          appSha: ${{ steps.shanotes.outputs.app_sha }}
          appName: ${{ matrix.appName }}
          skipGooglePlay: ${{ inputs.skipGooglePlay }}
          ftpUrlPath: ${{ steps.prepare_ftp.outputs.FTP_URL_PATH }}
          ftpUrlPathNightlyLatest: ${{ steps.prepare_ftp.outputs.FTP_URL_PATH_NIGHTLY_LATEST }}
          skipFtp: ${{ inputs.skipFtp }}
          packageFormat: ${{ matrix.packageFormat }}
        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 <b>${process.env.playTargetTrack}</b> track on Google Play`, true).write();
            }

            if (process.env.skipFtp != "true" && process.env.releaseTarget.includes("ftp") && process.env.packageFormat == 'apk') {
              await core.summary
                .addRaw(`Published to FTP at`)
                .addEOL()
                .addLink(process.env.ftpUrlPath, process.env.ftpUrlPath)
                .addEOL();

              if (process.env.releaseType == 'daily') {
                await core.summary
                  .addLink(process.env.ftpUrlPathNightlyLatest, process.env.ftpUrlPathNightlyLatest)
                  .addEOL();
              }
              await core.summary.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@f86bce177498d6fdd3bda4113d7d93266cfc3fca
        id: last_status
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}

      - name: Info
        uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
        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@3c45d89acd032c84b955b54c8a9001833ac91d17 # 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@3c45d89acd032c84b955b54c8a9001833ac91d17 # 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') && contains(fromJSON('[\"beta\", \"release\"]'), needs.dump_config.outputs.releaseType) }}"
        uses: kewisch/action-matrix-notify@3c45d89acd032c84b955b54c8a9001833ac91d17 # 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@3c45d89acd032c84b955b54c8a9001833ac91d17 # 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@3c45d89acd032c84b955b54c8a9001833ac91d17 # 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@3c45d89acd032c84b955b54c8a9001833ac91d17 # 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

[Dauer der Verarbeitung: 0.9 Sekunden, vorverarbeitet 2026-10-24]