name: Release Mobile on: push: tags: - "ios-v*" - "android-v*" workflow_dispatch: inputs: source_tag: description: "Existing tag: ios-v0.0.1-20261002 or android-v0.1.0-20261002." required: true type: string permissions: contents: read jobs: prepare: name: Validate mobile source tag runs-on: ubuntu-24.04 outputs: platform: ${{ steps.source.outputs.platform }} tag: ${{ steps.source.outputs.tag }} version: ${{ steps.source.outputs.version }} sha: ${{ steps.source.outputs.sha }} steps: - name: Checkout repository uses: actions/checkout@v5 with: fetch-depth: 0 persist-credentials: false - name: Resolve source tag id: source shell: python env: SOURCE_TAG: ${{ inputs.source_tag || github.ref_name }} run: | import datetime import os import re import subprocess tag = os.environ["SOURCE_TAG"] match = re.fullmatch(r"(ios|android)-(v[0-9]+\.[0-9]+\.[0-9]+-([0-9]{8}))", tag) if not match: raise ValueError("Use an existing ios-v-YYYYMMDD or android-v-YYYYMMDD tag") datetime.datetime.strptime(match[3], "%Y%m%d") sha = subprocess.check_output( ["git", "rev-parse", "--verify", f"refs/tags/{tag}^{{commit}}"], text=True).strip() with open(os.environ["GITHUB_OUTPUT"], "a") as stream: stream.write(f"platform={match[1]}\ntag={tag}\nversion={match[2]}\nsha={sha}\n") build-ios: name: Build iOS release needs: prepare if: needs.prepare.outputs.platform == 'ios' uses: ./.github/workflows/build-ios.yml with: source_tag: ${{ needs.prepare.outputs.tag }} build-android: name: Build Android release needs: prepare if: needs.prepare.outputs.platform == 'android' uses: ./.github/workflows/build-android.yml with: source_tag: ${{ needs.prepare.outputs.tag }} sign-android: name: Sign Android release needs: [prepare, build-android] if: needs.prepare.outputs.platform == 'android' uses: ./.github/workflows/sign-android.yml with: version: ${{ needs.build-android.outputs.version }} secrets: ANDROID_KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }} ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }} ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }} ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }} release: name: Publish mobile release needs: [prepare, build-ios, build-android, sign-android] if: ${{ !cancelled() && (needs.build-ios.result == 'success' || needs.sign-android.result == 'success') }} runs-on: ubuntu-24.04 permissions: contents: write concurrency: group: publish-release-metadata cancel-in-progress: false env: PLATFORM: ${{ needs.prepare.outputs.platform }} SOURCE_TAG: ${{ needs.prepare.outputs.tag }} SOURCE_SHA: ${{ needs.prepare.outputs.sha }} VERSION: ${{ needs.prepare.outputs.version }} BUILD_VERSION: ${{ needs.build-ios.outputs.version || needs.build-android.outputs.version }} steps: - name: Checkout publishing scripts uses: actions/checkout@v5 - name: Verify release manifest updates run: python3 -B -m unittest discover -s .github/scripts -p 'test_*.py' -v - name: Verify release version shell: bash run: | if [[ "${BUILD_VERSION}" != "${VERSION}" ]]; then echo "Source tag version ${VERSION} does not match the app version ${BUILD_VERSION}" >&2 exit 1 fi - name: Download mobile release artifact uses: actions/download-artifact@v8 with: name: crossdesk-${{ env.PLATFORM }}-arm64${{ env.PLATFORM == 'ios' && '-unsigned' || '' }}-${{ env.VERSION }} path: release - name: Prepare release notes shell: bash run: | if [[ "${PLATFORM}" == "ios" ]]; then PLATFORM_NAME="iOS" SIGNING_NOTE="The arm64 ZIP contains an unsigned app, source metadata, third-party sources and any generated dSYM. Sign the app before device installation." else PLATFORM_NAME="Android" SIGNING_NOTE="The arm64 Release APK is signed with the CrossDesk publishing key and can be installed directly. It includes source metadata, license documents and native build recipes." fi cat > "${RUNNER_TEMP}/mobile-release-notes.md" << EOF ${PLATFORM_NAME} ${VERSION} ${SIGNING_NOTE} Source: [${SOURCE_TAG}](https://github.com/${GITHUB_REPOSITORY}/tree/${SOURCE_SHA}). Clone this tag with recursive submodules to obtain the corresponding source. [Source and rebuild instructions](https://github.com/${GITHUB_REPOSITORY}/blob/${SOURCE_SHA}/apps/${PLATFORM}/README.md). EOF - name: Publish GitHub Release uses: softprops/action-gh-release@v2 with: tag_name: ${{ env.SOURCE_TAG }} # prepare requires an existing tag; this does not move its source commit. # Using the default branch lets GITHUB_TOKEN update older release metadata. target_commitish: ${{ github.event.repository.default_branch }} name: ${{ env.PLATFORM == 'ios' && 'iOS' || 'Android' }} ${{ env.VERSION }} draft: false prerelease: false make_latest: "false" files: release/* fail_on_unmatched_files: true generate_release_notes: false body_path: ${{ runner.temp }}/mobile-release-notes.md - name: Upload mobile artifact to download server uses: burnett01/rsync-deployments@5.2 with: switches: -avzr --progress path: release/* remote_path: /var/www/html/downloads/ remote_host: ${{ secrets.SERVER_HOST }} remote_user: ${{ secrets.SERVER_USER }} remote_key: ${{ secrets.SERVER_KEY }} - name: Download current version.json shell: bash run: | curl --fail --silent --show-error --retry 3 --max-time 30 \ -H 'Cache-Control: no-cache' \ https://version.crossdesk.cn/version.json -o current-version.json - name: Update mobile version and download URL shell: bash env: RELEASE_NAME: ${{ env.PLATFORM == 'ios' && 'iOS' || 'Android' }} ${{ env.VERSION }} run: | python3 .github/scripts/update_version_manifest.py \ --current current-version.json --output version.json --artifacts release \ --tag "${SOURCE_TAG}" --name "${RELEASE_NAME}" \ --notes "${RUNNER_TEMP}/mobile-release-notes.md" - name: Upload version.json to server uses: burnett01/rsync-deployments@5.2 with: switches: -avzr path: version.json remote_path: /var/www/html/version/ remote_host: ${{ secrets.SERVER_HOST }} remote_user: ${{ secrets.SERVER_USER }} remote_key: ${{ secrets.SERVER_KEY }} - name: Summarize release shell: bash run: | echo "Published [${SOURCE_TAG}](https://github.com/${GITHUB_REPOSITORY}/releases/tag/${SOURCE_TAG}), uploaded its ${PLATFORM} artifact to the download server, and updated version.json." >> "${GITHUB_STEP_SUMMARY}"