diff --git a/.changes/mobile.md b/.changes/mobile.md new file mode 100644 index 0000000..1c38ac3 --- /dev/null +++ b/.changes/mobile.md @@ -0,0 +1,5 @@ +--- +action: patch +--- + +Added initial Android & iOS support. This only includes building (=== running `tauri android|ios build`), installing dependencies and uploading to stores must be done manually. diff --git a/.github/fixtures/example-with-tauri-v2/src-tauri/Cargo.toml b/.github/fixtures/example-with-tauri-v2/src-tauri/Cargo.toml index e72e538..1e75a1f 100644 --- a/.github/fixtures/example-with-tauri-v2/src-tauri/Cargo.toml +++ b/.github/fixtures/example-with-tauri-v2/src-tauri/Cargo.toml @@ -7,14 +7,23 @@ license = "" repository = "" edition = "2021" -[dependencies] -serde_json = "1" -serde = { version = "1", features = ["derive"] } -tauri = { version = "2", features = [] } +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[lib] +# The `_lib` suffix may seem redundant but it is necessary +# to make the lib name unique and wouldn't conflict with the bin name. +# This seems to be only an issue on Windows, see https://github.com/rust-lang/cargo/issues/8519 +name = "test_app_lib" +crate-type = ["staticlib", "cdylib", "rlib"] [build-dependencies] tauri-build = { version = "2", features = [] } +[dependencies] +tauri = { version = "2", features = [] } +serde = { version = "1", features = ["derive"] } +serde_json = "1" + [profile.dev] opt-level = 0 lto = false diff --git a/.github/fixtures/example-with-tauri-v2/src-tauri/src/lib.rs b/.github/fixtures/example-with-tauri-v2/src-tauri/src/lib.rs new file mode 100644 index 0000000..3b48e91 --- /dev/null +++ b/.github/fixtures/example-with-tauri-v2/src-tauri/src/lib.rs @@ -0,0 +1,6 @@ +#[cfg_attr(mobile, tauri::mobile_entry_point)] +pub fn run() { + tauri::Builder::default() + .run(tauri::generate_context!()) + .expect("error while running tauri application"); +} diff --git a/.github/fixtures/example-with-tauri-v2/src-tauri/src/main.rs b/.github/fixtures/example-with-tauri-v2/src-tauri/src/main.rs index d387909..fee34e1 100644 --- a/.github/fixtures/example-with-tauri-v2/src-tauri/src/main.rs +++ b/.github/fixtures/example-with-tauri-v2/src-tauri/src/main.rs @@ -1,7 +1,5 @@ #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] fn main() { - tauri::Builder::default() - .run(tauri::generate_context!()) - .expect("error while running tauri application"); + test_app_lib::run() } diff --git a/.github/workflows/test-action.yml b/.github/workflows/test-action.yml index 96489ef..730acf6 100644 --- a/.github/workflows/test-action.yml +++ b/.github/workflows/test-action.yml @@ -27,6 +27,13 @@ jobs: args: '--verbose --debug' - platform: 'windows-11-arm' args: '--verbose --debug' + - platform: 'ubuntu-latest' + args: '--verbose --debug' + mobile: 'android' + # can't test without an apple account + #- platform: 'macos-26' + # args: '--verbose --debug' + # mobile: 'ios' runs-on: ${{ matrix.platform }} steps: @@ -46,16 +53,61 @@ jobs: - name: install example dependencies run: | echo "skipped" - # disabled to test cli fallback install + # disabled on desktop to test cli fallback install #cd ./.github/fixtures/example-with-tauri-v2 # pnpm install # rust - - name: install Rust stable + - name: install Rust stable (desktop) + if: matrix.mobile == '' uses: dtolnay/rust-toolchain@stable with: targets: aarch64-apple-darwin,x86_64-apple-darwin + - name: install Rust stable (android) + if: matrix.mobile == 'android' + uses: dtolnay/rust-toolchain@stable + with: + targets: aarch64-linux-android,armv7-linux-androideabi,i686-linux-android,x86_64-linux-android + + - name: install Rust stable (ios) + if: matrix.mobile == 'ios' + uses: dtolnay/rust-toolchain@stable + with: + targets: aarch64-apple-ios + + - name: Set up JDK + if: matrix.mobile == 'android' + uses: actions/setup-java@v5 + with: + java-version: '21' # Maybe 17 + distribution: 'temurin' + + - name: Setup Android SDK + if: matrix.mobile == 'android' + uses: android-actions/setup-android@v3 + + - name: Setup Android NDK + if: matrix.mobile == 'android' + uses: nttld/setup-ndk@v1 + id: setup-ndk + with: + ndk-version: r29 + link-to-sdk: true + + - uses: maxim-lobanov/setup-xcode@v1 + if: matrix.mobile == 'ios' + with: + xcode-version: 26.1 + + - name: setup Apple API key + if: matrix.mobile == 'ios' + run: | + APPLE_API_KEY_PATH="$RUNNER_TEMP/AuthKey_${{ secrets.APPLE_API_KEY_ID }}.p8" + echo "${{ secrets.APPLE_API_KEY }}" > $APPLE_API_KEY_PATH + echo "APPLE_API_KEY_PATH=$APPLE_API_KEY_PATH" >> $GITHUB_ENV + echo "API_PRIVATE_KEYS_DIR=$RUNNER_TEMP" >> $GITHUB_ENV + - uses: Swatinem/rust-cache@v2 with: cache-on-failure: true @@ -69,12 +121,19 @@ jobs: sudo apt-get update sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev + - name: init mobile project + if: matrix.mobile != '' + run: | + cd ./.github/fixtures/example-with-tauri-v2 + pnpm dlx @tauri-apps/cli ${{ matrix.mobile }} init + - name: Preconfigured Tauri Project uses: ./ env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Updater signature is exposed here to make sure it works in PR's TAURI_SIGNING_PRIVATE_KEY: dW50cnVzdGVkIGNvbW1lbnQ6IHJzaWduIGVuY3J5cHRlZCBzZWNyZXQga2V5ClJXUlRZMEl5YTBGV3JiTy9lRDZVd3NkL0RoQ1htZmExNDd3RmJaNmRMT1ZGVjczWTBKZ0FBQkFBQUFBQUFBQUFBQUlBQUFBQWdMekUzVkE4K0tWQ1hjeGt1Vkx2QnRUR3pzQjVuV0ZpM2czWXNkRm9hVUxrVnB6TUN3K1NheHJMREhQbUVWVFZRK3NIL1VsMDBHNW5ET1EzQno0UStSb21nRW4vZlpTaXIwZFh5ZmRlL1lSN0dKcHdyOUVPclVvdzFhVkxDVnZrbHM2T1o4Tk1NWEU9Cg== + NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }} with: projectPath: ./.github/fixtures/example-with-tauri-v2 tagName: ${{ !github.event.pull_request.head.repo.fork && 'example-with-tauri-v__VERSION__' || '' }} @@ -86,5 +145,6 @@ jobs: uploadPlainBinary: true uploadWorkflowArtifacts: true uploadUpdaterSignatures: false + mobile: ${{ matrix.mobile }} # workflowArtifactNamePattern: '[name]_${{ github.sha }}_[platform]_[arch]_[bundle]' # releaseAssetNamePattern: '[name]x[version]x[platform]x[arch]x[mode]x[setup]x[ext]' diff --git a/README.md b/README.md index b5f0d66..0985b1f 100644 --- a/README.md +++ b/README.md @@ -244,6 +244,24 @@ jobs: # # default: true uploadUpdaterSignatures: true + + # EXPERIMENTAL - Whether to build for mobile or desktop. + # + # Effectively changes the build command from `${tauriScript} build` + # to `${tauriScript} android build` / ` ${tauriScript}ios build` + # + # Note that you have to install system dependencies (Xcode, SDKs, etc) yourself. + # Furthermore, the action does not upload the app to the App Store or Play Store. + # The .apk and .ipa files can be uploaded to the release or as workflow artifcats, but + # plain .ipa files are generally useless so uploading them to a release is not recommended. + # + # - Can be set to "android" to build for Android. This works on all runners but other + # required actions typically only work on Ubuntu so we recommend Ubuntu as well. + # - Can be set to "ios` to build for iOS. This only works on macOS runners. + # - Any other value will be ignored. + # + # default: unset + mobile: '' ``` ## Outputs @@ -287,6 +305,8 @@ jobs: - `[bundle]` is likely only useful for `workflowArtifactNamePattern` and _not_ for `releaseAssetNamePattern` because of its conflict with `[ext]`. +- The action's iOS and Android support is considered experimental, please report any issues or feedback you may have in this repository. + ## Partners diff --git a/action.yml b/action.yml index 681fc46..ef8a788 100644 --- a/action.yml +++ b/action.yml @@ -61,6 +61,8 @@ inputs: generateReleaseNotes: description: "Whether to use GitHub's Release Notes API to generate the release title and body. If `releaseName` is set, it will overwrite the generated title. If `releaseBody` is set, it will be pre-pended to the automatically generated notes. This action is not responsible for the generated content." default: 'false' + mobile: + description: 'EXPERIMENTAL - Whether to build for mobile or desktop. Can be "android" or "ios" or unset for desktop builds.' outputs: releaseId: description: 'The ID of the created release' diff --git a/dist/index.js b/dist/index.js index f8bfcec..5d9b97f 100644 --- a/dist/index.js +++ b/dist/index.js @@ -84695,7 +84695,6 @@ async function getRunner() { async function buildProject() { const runner = await getRunner(); - const debug = inputs/* parsedArgs */.HD['debug']; const targetPath = inputs/* parsedArgs */.HD['target']; const configArg = inputs/* parsedArgs */.HD['config']; const profile = inputs/* parsedRunnerArgs */.Nl['profile']; @@ -84713,13 +84712,24 @@ async function buildProject() { wixLanguage: info.wixLanguage, rpmRelease: info.rpmRelease, }; - await runner.execTauriCommand(['build'], inputs/* rawArgs */.ay, inputs/* projectPath */.DZ, targetInfo.platform === 'macos' + let command = ['build']; + if (inputs/* isAndroid */.m0) + command = ['android', 'build']; + if (inputs/* isIOS */.un) + command = ['ios', 'build']; + await runner.execTauriCommand(command, inputs/* rawArgs */.ay, inputs/* projectPath */.DZ, targetInfo.platform === 'macos' ? { TAURI_BUNDLER_DMG_IGNORE_CI: process.env.TAURI_BUNDLER_DMG_IGNORE_CI ?? 'true', } : undefined, inputs/* retryAttempts */.z); const workspacePath = (0,utils/* getWorkspaceDir */.Lw)(app.tauriPath) ?? app.tauriPath; - const artifactsPath = (0,external_node_path_.join)((0,utils/* getTargetDir */.d)(workspacePath, info.tauriPath, !!targetPath), targetPath ?? '', profile ? profile : debug ? 'debug' : 'release'); + let artifactsPath = (0,external_node_path_.join)((0,utils/* getTargetDir */.d)(workspacePath, info.tauriPath, !!targetPath), targetPath ?? '', profile ? profile : inputs/* isDebug */._o ? 'debug' : 'release'); + if (inputs/* isAndroid */.m0) { + artifactsPath = (0,external_node_path_.join)(info.tauriPath, 'gen/android/app/build/outputs/'); + } + if (inputs/* isIOS */.un) { + artifactsPath = (0,external_node_path_.join)(info.tauriPath, 'gen/apple/build/'); + } let artifacts = []; let arch = targetInfo.arch; if (targetInfo.platform === 'macos') { @@ -84733,7 +84743,6 @@ async function buildProject() { (0,utils/* createArtifact */.Dg)({ path: (0,external_node_path_.join)(artifactsPath, `bundle/dmg/${app.name}_${app.version}_${arch}.dmg`), name: app.name, - debug, platform: targetInfo.platform, arch, bundle: 'dmg', // could be 'dmg' or 'app' depending on the usecase @@ -84742,7 +84751,6 @@ async function buildProject() { (0,utils/* createArtifact */.Dg)({ path: (0,external_node_path_.join)(artifactsPath, `bundle/macos/${app.name}.app`), name: app.name, - debug, platform: targetInfo.platform, arch, bundle: 'app', @@ -84751,7 +84759,6 @@ async function buildProject() { (0,utils/* createArtifact */.Dg)({ path: (0,external_node_path_.join)(artifactsPath, `bundle/macos/${app.name}.app.tar.gz`), name: app.name, - debug, platform: targetInfo.platform, arch, bundle: 'app', @@ -84760,7 +84767,6 @@ async function buildProject() { (0,utils/* createArtifact */.Dg)({ path: (0,external_node_path_.join)(artifactsPath, `bundle/macos/${app.name}.app.tar.gz.sig`), name: app.name, - debug, platform: targetInfo.platform, arch, bundle: 'app', @@ -84796,7 +84802,6 @@ async function buildProject() { winArtifacts.push((0,utils/* createArtifact */.Dg)({ path: (0,external_node_path_.join)(artifactsPath, `bundle/msi/${app.name}_${app.version}_${arch}_${lang}.msi`), name: app.name, - debug, platform: targetInfo.platform, arch, bundle: 'msi', @@ -84804,7 +84809,6 @@ async function buildProject() { }), (0,utils/* createArtifact */.Dg)({ path: (0,external_node_path_.join)(artifactsPath, `bundle/msi/${app.name}_${app.version}_${arch}_${lang}.msi.sig`), name: app.name, - debug, platform: targetInfo.platform, arch, bundle: 'msi', @@ -84812,7 +84816,6 @@ async function buildProject() { }), (0,utils/* createArtifact */.Dg)({ path: (0,external_node_path_.join)(artifactsPath, `bundle/msi/${app.name}_${app.version}_${arch}_${lang}.msi.zip`), name: app.name, - debug, platform: targetInfo.platform, arch, bundle: 'msi', @@ -84820,7 +84823,6 @@ async function buildProject() { }), (0,utils/* createArtifact */.Dg)({ path: (0,external_node_path_.join)(artifactsPath, `bundle/msi/${app.name}_${app.version}_${arch}_${lang}.msi.zip.sig`), name: app.name, - debug, platform: targetInfo.platform, arch, bundle: 'msi', @@ -84830,7 +84832,6 @@ async function buildProject() { winArtifacts.push((0,utils/* createArtifact */.Dg)({ path: (0,external_node_path_.join)(artifactsPath, `bundle/nsis/${app.name}_${app.version}_${arch}-setup.exe`), name: app.name, - debug, platform: targetInfo.platform, arch, bundle: 'nsis', @@ -84838,7 +84839,6 @@ async function buildProject() { }), (0,utils/* createArtifact */.Dg)({ path: (0,external_node_path_.join)(artifactsPath, `bundle/nsis/${app.name}_${app.version}_${arch}-setup.exe.sig`), name: app.name, - debug, platform: targetInfo.platform, arch, bundle: 'nsis', @@ -84846,7 +84846,6 @@ async function buildProject() { }), (0,utils/* createArtifact */.Dg)({ path: (0,external_node_path_.join)(artifactsPath, `bundle/nsis/${app.name}_${app.version}_${arch}-setup.nsis.zip`), name: app.name, - debug, platform: targetInfo.platform, arch, bundle: 'nsis', @@ -84854,7 +84853,6 @@ async function buildProject() { }), (0,utils/* createArtifact */.Dg)({ path: (0,external_node_path_.join)(artifactsPath, `bundle/nsis/${app.name}_${app.version}_${arch}-setup.nsis.zip.sig`), name: app.name, - debug, platform: targetInfo.platform, arch, bundle: 'nsis', @@ -84862,7 +84860,7 @@ async function buildProject() { })); artifacts = winArtifacts; } - else { + else if (targetInfo.platform === 'linux') { const debianArch = arch === 'x64' || arch === 'x86_64' ? 'amd64' : arch === 'x32' || arch === 'i686' @@ -84894,7 +84892,6 @@ async function buildProject() { (0,utils/* createArtifact */.Dg)({ path: (0,external_node_path_.join)(artifactsPath, `bundle/deb/${app.name}_${app.version}_${debianArch}.deb`), name: app.name, - debug, platform: targetInfo.platform, arch: debianArch, bundle: 'deb', @@ -84903,7 +84900,6 @@ async function buildProject() { (0,utils/* createArtifact */.Dg)({ path: (0,external_node_path_.join)(artifactsPath, `bundle/deb/${app.name}_${app.version}_${debianArch}.deb.sig`), name: app.name, - debug, platform: targetInfo.platform, arch: debianArch, bundle: 'deb', @@ -84912,7 +84908,6 @@ async function buildProject() { (0,utils/* createArtifact */.Dg)({ path: (0,external_node_path_.join)(artifactsPath, `bundle/rpm/${app.name}-${app.version}-${app.rpmRelease}.${rpmArch}.rpm`), name: app.name, - debug, platform: targetInfo.platform, arch: rpmArch, bundle: 'rpm', @@ -84921,7 +84916,6 @@ async function buildProject() { (0,utils/* createArtifact */.Dg)({ path: (0,external_node_path_.join)(artifactsPath, `bundle/rpm/${app.name}-${app.version}-${app.rpmRelease}.${rpmArch}.rpm.sig`), name: app.name, - debug, platform: targetInfo.platform, arch: rpmArch, bundle: 'rpm', @@ -84930,7 +84924,6 @@ async function buildProject() { (0,utils/* createArtifact */.Dg)({ path: (0,external_node_path_.join)(artifactsPath, `bundle/appimage/${app.name}_${app.version}_${appImageArch}.AppImage`), name: app.name, - debug, platform: targetInfo.platform, arch: appImageArch, bundle: 'appimage', @@ -84939,7 +84932,6 @@ async function buildProject() { (0,utils/* createArtifact */.Dg)({ path: (0,external_node_path_.join)(artifactsPath, `bundle/appimage/${app.name}_${app.version}_${appImageArch}.AppImage.sig`), name: app.name, - debug, platform: targetInfo.platform, arch: appImageArch, bundle: 'appimage', @@ -84948,7 +84940,6 @@ async function buildProject() { (0,utils/* createArtifact */.Dg)({ path: (0,external_node_path_.join)(artifactsPath, `bundle/appimage/${app.name}_${app.version}_${appImageArch}.AppImage.tar.gz`), name: app.name, - debug, platform: targetInfo.platform, arch: appImageArch, bundle: 'appimage', @@ -84957,7 +84948,6 @@ async function buildProject() { (0,utils/* createArtifact */.Dg)({ path: (0,external_node_path_.join)(artifactsPath, `bundle/appimage/${app.name}_${app.version}_${appImageArch}.AppImage.tar.gz.sig`), name: app.name, - debug, platform: targetInfo.platform, arch: appImageArch, bundle: 'appimage', @@ -84965,13 +84955,165 @@ async function buildProject() { }), ]; } + else if (targetInfo.platform === 'android') { + const debug = inputs/* isDebug */._o ? 'debug' : 'release'; + // TODO: detect (un)signed beforehand + if (!inputs/* isDebug */._o) { + // unsigned release apks + artifacts.push((0,utils/* createArtifact */.Dg)({ + path: (0,external_node_path_.join)(artifactsPath, `apk/universal/release/app-universal-unsigend.apk`), + name: app.name, + platform: targetInfo.platform, + arch: 'universal', + bundle: 'apk', + version: app.version, + }), (0,utils/* createArtifact */.Dg)({ + path: (0,external_node_path_.join)(artifactsPath, `apk/arm64/release/app-arm64-unsigend.apk`), + name: app.name, + platform: targetInfo.platform, + arch: 'arm64', + bundle: 'apk', + version: app.version, + }), (0,utils/* createArtifact */.Dg)({ + path: (0,external_node_path_.join)(artifactsPath, `apk/arm/release/app-arm-unsigend.apk`), + name: app.name, + platform: targetInfo.platform, + arch: 'universal', + bundle: 'apk', + version: app.version, + }), (0,utils/* createArtifact */.Dg)({ + path: (0,external_node_path_.join)(artifactsPath, `apk/x86_64/release/app-x86_64-unsigend.apk`), + name: app.name, + platform: targetInfo.platform, + arch: 'arm', + bundle: 'apk', + version: app.version, + }), (0,utils/* createArtifact */.Dg)({ + path: (0,external_node_path_.join)(artifactsPath, `apk/x86/release/app-x86-unsigend.apk`), + name: app.name, + platform: targetInfo.platform, + arch: 'x86', + bundle: 'apk', + version: app.version, + })); + } + artifacts.push( + // signed release apks and debug apks + (0,utils/* createArtifact */.Dg)({ + path: (0,external_node_path_.join)(artifactsPath, `apk/universal/${debug}/app-universal-${debug}.apk`), + name: app.name, + platform: targetInfo.platform, + arch: 'universal', + bundle: 'apk', + version: app.version, + }), (0,utils/* createArtifact */.Dg)({ + path: (0,external_node_path_.join)(artifactsPath, `apk/arm64/${debug}/app-arm64-${debug}.apk`), + name: app.name, + platform: targetInfo.platform, + arch: 'arm64', + bundle: 'apk', + version: app.version, + }), (0,utils/* createArtifact */.Dg)({ + path: (0,external_node_path_.join)(artifactsPath, `apk/arm/${debug}/app-arm-${debug}.apk`), + name: app.name, + platform: targetInfo.platform, + arch: 'universal', + bundle: 'apk', + version: app.version, + }), (0,utils/* createArtifact */.Dg)({ + path: (0,external_node_path_.join)(artifactsPath, `apk/x86_64/${debug}/app-x86_64-${debug}.apk`), + name: app.name, + platform: targetInfo.platform, + arch: 'arm', + bundle: 'apk', + version: app.version, + }), (0,utils/* createArtifact */.Dg)({ + path: (0,external_node_path_.join)(artifactsPath, `apk/x86/${debug}/app-x86-${debug}.apk`), + name: app.name, + platform: targetInfo.platform, + arch: 'x86', + bundle: 'apk', + version: app.version, + }), + // + // aabs + // + (0,utils/* createArtifact */.Dg)({ + path: (0,external_node_path_.join)(artifactsPath, `/bundle/universal${debug}/app-universal-${debug}.aab`), + name: app.name, + platform: targetInfo.platform, + arch: 'universal', + bundle: 'aab', + version: app.version, + }), (0,utils/* createArtifact */.Dg)({ + path: (0,external_node_path_.join)(artifactsPath, `/bundle/arm64${debug}/app-arm64-${debug}.aab`), + name: app.name, + platform: targetInfo.platform, + arch: 'arm64', + bundle: 'aab', + version: app.version, + }), (0,utils/* createArtifact */.Dg)({ + path: (0,external_node_path_.join)(artifactsPath, `/bundle/arm${debug}/app-arm-${debug}.aab`), + name: app.name, + platform: targetInfo.platform, + arch: 'arm', + bundle: 'aab', + version: app.version, + }), (0,utils/* createArtifact */.Dg)({ + path: (0,external_node_path_.join)(artifactsPath, `/bundle/x86_64${debug}/app-x86_64-${debug}.aab`), + name: app.name, + platform: targetInfo.platform, + arch: 'x86_64', + bundle: 'aab', + version: app.version, + }), (0,utils/* createArtifact */.Dg)({ + path: (0,external_node_path_.join)(artifactsPath, `/bundle/x86${debug}/app-x86-${debug}.aab`), + name: app.name, + platform: targetInfo.platform, + arch: 'x86', + bundle: 'aab', + version: app.version, + })); + } + else if (targetInfo.platform === 'ios') { + // TODO: Confirm that info.name is correct. + artifacts = [ + (0,utils/* createArtifact */.Dg)({ + path: (0,external_node_path_.join)(artifactsPath, `x86_64/${app.name}.ipa`), + name: app.name, + platform: targetInfo.platform, + arch: 'x86_64', + bundle: 'ipa', + version: app.version, + }), + (0,utils/* createArtifact */.Dg)({ + path: (0,external_node_path_.join)(artifactsPath, `arm64/${app.name}.ipa`), + name: app.name, + platform: targetInfo.platform, + arch: 'arm64', + bundle: 'ipa', + version: app.version, + }), + (0,utils/* createArtifact */.Dg)({ + path: (0,external_node_path_.join)(artifactsPath, `arm64-sim/${app.name}.ipa`), + name: app.name, + platform: targetInfo.platform, + arch: 'arm64-sim', + bundle: 'ipa', + version: app.version, + }), + ]; + } + else { + // eslint-disable-next-line @typescript-eslint/restrict-template-expressions + console.error(`Unhandled target platform: "${targetInfo.platform}"`); + } if (inputs/* uploadPlainBinary */.pm) { const ext = targetInfo.platform === 'windows' ? '.exe' : ''; artifacts.push((0,utils/* createArtifact */.Dg)({ path: (0,external_node_path_.join)(artifactsPath, `${app.mainBinaryName}${ext}`), name: 'binary', // app.mainBinaryName, bundle: 'bin', - debug, platform: targetInfo.platform, arch, version: app.version, @@ -85129,6 +85271,9 @@ __nccwpck_require__.a(module, async (__webpack_handle_async_dependencies__, __we async function run() { try { + if (_inputs__WEBPACK_IMPORTED_MODULE_5__/* .isIOS */ .un && process.platform !== 'darwin') { + throw new Error('Building for iOS is only supported on macOS runners.'); + } // inputs that won't be changed are in ./inputs let tagName = _actions_core__WEBPACK_IMPORTED_MODULE_2__.getInput('tagName').replace('refs/tags/', ''); let releaseId = Number(_actions_core__WEBPACK_IMPORTED_MODULE_2__.getInput('releaseId')); @@ -85242,7 +85387,10 @@ __nccwpck_require__.d(__webpack_exports__, { yK: () => (/* binding */ generateReleaseNotes), qu: () => (/* binding */ githubBaseUrl), Sv: () => (/* binding */ includeUpdaterJson), + m0: () => (/* binding */ isAndroid), + _o: () => (/* binding */ isDebug), Hd: () => (/* binding */ isGitea), + un: () => (/* binding */ isIOS), eC: () => (/* binding */ owner), HD: () => (/* binding */ parsedArgs), Nl: () => (/* binding */ parsedRunnerArgs), @@ -85357,6 +85505,9 @@ const shouldUploadWorkflowArtifacts = core.getBooleanInput('uploadWorkflowArtifa const workflowArtifactNamePattern = core.getInput('workflowArtifactNamePattern') || '[platform]-[arch]-[bundle]'; const uploadUpdaterSignatures = core.getBooleanInput('uploadUpdaterSignatures'); const updaterJsonPreferNsis = core.getBooleanInput('updaterJsonPreferNsis'); +const isAndroid = core.getInput('mobile').toLowerCase() === 'android'; +const isIOS = core.getInput('mobile').toLowerCase() === 'ios'; +const isDebug = parsedArgs['debug']; /***/ }), @@ -85673,7 +85824,6 @@ async function uploadVersionJSON(version, notes, tagName, releaseId, artifacts, const artifact = (0,_utils__WEBPACK_IMPORTED_MODULE_5__/* .createArtifact */ .Dg)({ path: versionFile, name: versionFilename, - debug: false, platform: targetInfo.platform, arch: '', bundle: '', @@ -96121,7 +96271,7 @@ function ghAssetName(artifact, releaseAssetNamePattern) { .replace(/[^a-zA-Z0-9_-]/g, '.') .replace(/\.\./g, '.'); } -function createArtifact({ path, name, debug, platform, arch, bundle, version, }) { +function createArtifact({ path, name, platform, arch, bundle, version, }) { const baseName = (0,external_node_path_.basename)(path); const exts = extensions.filter((s) => baseName.includes(s)); const ext = exts[0] || (0,external_node_path_.extname)(path); @@ -96133,7 +96283,7 @@ function createArtifact({ path, name, debug, platform, arch, bundle, version, }) return { path, name, - mode: debug ? 'debug' : 'release', + mode: inputs/* isDebug */._o ? 'debug' : 'release', platform: platform === 'macos' ? 'darwin' : platform, arch, bundle, @@ -96427,11 +96577,22 @@ function getInfo(targetInfo, configFlag) { } function getTargetInfo(targetPath) { let arch = process.arch; - let platform = process.platform === 'win32' - ? 'windows' - : process.platform === 'darwin' - ? 'macos' - : 'linux'; + let platform; + if (inputs/* isAndroid */.m0) { + platform = 'android'; + } + else if (inputs/* isIOS */.un) { + platform = 'ios'; + } + else if (process.platform === 'win32') { + platform = 'windows'; + } + else if (process.platform === 'darwin') { + platform = 'macos'; + } + else { + platform = 'linux'; + } if (targetPath) { if (targetPath.includes('windows')) { platform = 'windows'; diff --git a/src/build.ts b/src/build.ts index ecc40f5..41c35fb 100644 --- a/src/build.ts +++ b/src/build.ts @@ -3,6 +3,9 @@ import { join } from 'node:path'; import { getRunner } from './runner'; import { + isAndroid, + isDebug, + isIOS, parsedArgs, parsedRunnerArgs, projectPath, @@ -23,7 +26,6 @@ import type { Artifact } from './types'; export async function buildProject(): Promise { const runner = await getRunner(); - const debug = parsedArgs['debug'] as boolean; const targetPath = parsedArgs['target'] as string | undefined; const configArg = parsedArgs['config'] as string | undefined; const profile = parsedRunnerArgs['profile'] as string | undefined; @@ -46,8 +48,12 @@ export async function buildProject(): Promise { rpmRelease: info.rpmRelease, }; + let command = ['build']; + if (isAndroid) command = ['android', 'build']; + if (isIOS) command = ['ios', 'build']; + await runner.execTauriCommand( - ['build'], + command, rawArgs, projectPath, targetInfo.platform === 'macos' @@ -61,11 +67,17 @@ export async function buildProject(): Promise { const workspacePath = getWorkspaceDir(app.tauriPath) ?? app.tauriPath; - const artifactsPath = join( + let artifactsPath = join( getTargetDir(workspacePath, info.tauriPath, !!targetPath), targetPath ?? '', - profile ? profile : debug ? 'debug' : 'release', + profile ? profile : isDebug ? 'debug' : 'release', ); + if (isAndroid) { + artifactsPath = join(info.tauriPath, 'gen/android/app/build/outputs/'); + } + if (isIOS) { + artifactsPath = join(info.tauriPath, 'gen/apple/build/'); + } let artifacts: Artifact[] = []; @@ -85,7 +97,6 @@ export async function buildProject(): Promise { `bundle/dmg/${app.name}_${app.version}_${arch}.dmg`, ), name: app.name, - debug, platform: targetInfo.platform, arch, bundle: 'dmg', // could be 'dmg' or 'app' depending on the usecase @@ -94,7 +105,6 @@ export async function buildProject(): Promise { createArtifact({ path: join(artifactsPath, `bundle/macos/${app.name}.app`), name: app.name, - debug, platform: targetInfo.platform, arch, bundle: 'app', @@ -103,7 +113,6 @@ export async function buildProject(): Promise { createArtifact({ path: join(artifactsPath, `bundle/macos/${app.name}.app.tar.gz`), name: app.name, - debug, platform: targetInfo.platform, arch, bundle: 'app', @@ -112,7 +121,6 @@ export async function buildProject(): Promise { createArtifact({ path: join(artifactsPath, `bundle/macos/${app.name}.app.tar.gz.sig`), name: app.name, - debug, platform: targetInfo.platform, arch, bundle: 'app', @@ -150,7 +158,6 @@ export async function buildProject(): Promise { `bundle/msi/${app.name}_${app.version}_${arch}_${lang}.msi`, ), name: app.name, - debug, platform: targetInfo.platform, arch, bundle: 'msi', @@ -162,7 +169,6 @@ export async function buildProject(): Promise { `bundle/msi/${app.name}_${app.version}_${arch}_${lang}.msi.sig`, ), name: app.name, - debug, platform: targetInfo.platform, arch, bundle: 'msi', @@ -174,7 +180,6 @@ export async function buildProject(): Promise { `bundle/msi/${app.name}_${app.version}_${arch}_${lang}.msi.zip`, ), name: app.name, - debug, platform: targetInfo.platform, arch, bundle: 'msi', @@ -186,7 +191,6 @@ export async function buildProject(): Promise { `bundle/msi/${app.name}_${app.version}_${arch}_${lang}.msi.zip.sig`, ), name: app.name, - debug, platform: targetInfo.platform, arch, bundle: 'msi', @@ -202,7 +206,6 @@ export async function buildProject(): Promise { `bundle/nsis/${app.name}_${app.version}_${arch}-setup.exe`, ), name: app.name, - debug, platform: targetInfo.platform, arch, bundle: 'nsis', @@ -214,7 +217,6 @@ export async function buildProject(): Promise { `bundle/nsis/${app.name}_${app.version}_${arch}-setup.exe.sig`, ), name: app.name, - debug, platform: targetInfo.platform, arch, bundle: 'nsis', @@ -226,7 +228,6 @@ export async function buildProject(): Promise { `bundle/nsis/${app.name}_${app.version}_${arch}-setup.nsis.zip`, ), name: app.name, - debug, platform: targetInfo.platform, arch, bundle: 'nsis', @@ -238,7 +239,6 @@ export async function buildProject(): Promise { `bundle/nsis/${app.name}_${app.version}_${arch}-setup.nsis.zip.sig`, ), name: app.name, - debug, platform: targetInfo.platform, arch, bundle: 'nsis', @@ -247,7 +247,7 @@ export async function buildProject(): Promise { ); artifacts = winArtifacts; - } else { + } else if (targetInfo.platform === 'linux') { const debianArch = arch === 'x64' || arch === 'x86_64' ? 'amd64' @@ -286,7 +286,6 @@ export async function buildProject(): Promise { `bundle/deb/${app.name}_${app.version}_${debianArch}.deb`, ), name: app.name, - debug, platform: targetInfo.platform, arch: debianArch, bundle: 'deb', @@ -298,7 +297,6 @@ export async function buildProject(): Promise { `bundle/deb/${app.name}_${app.version}_${debianArch}.deb.sig`, ), name: app.name, - debug, platform: targetInfo.platform, arch: debianArch, bundle: 'deb', @@ -310,7 +308,6 @@ export async function buildProject(): Promise { `bundle/rpm/${app.name}-${app.version}-${app.rpmRelease}.${rpmArch}.rpm`, ), name: app.name, - debug, platform: targetInfo.platform, arch: rpmArch, bundle: 'rpm', @@ -322,7 +319,6 @@ export async function buildProject(): Promise { `bundle/rpm/${app.name}-${app.version}-${app.rpmRelease}.${rpmArch}.rpm.sig`, ), name: app.name, - debug, platform: targetInfo.platform, arch: rpmArch, bundle: 'rpm', @@ -334,7 +330,6 @@ export async function buildProject(): Promise { `bundle/appimage/${app.name}_${app.version}_${appImageArch}.AppImage`, ), name: app.name, - debug, platform: targetInfo.platform, arch: appImageArch, bundle: 'appimage', @@ -346,7 +341,6 @@ export async function buildProject(): Promise { `bundle/appimage/${app.name}_${app.version}_${appImageArch}.AppImage.sig`, ), name: app.name, - debug, platform: targetInfo.platform, arch: appImageArch, bundle: 'appimage', @@ -358,7 +352,6 @@ export async function buildProject(): Promise { `bundle/appimage/${app.name}_${app.version}_${appImageArch}.AppImage.tar.gz`, ), name: app.name, - debug, platform: targetInfo.platform, arch: appImageArch, bundle: 'appimage', @@ -370,13 +363,201 @@ export async function buildProject(): Promise { `bundle/appimage/${app.name}_${app.version}_${appImageArch}.AppImage.tar.gz.sig`, ), name: app.name, - debug, platform: targetInfo.platform, arch: appImageArch, bundle: 'appimage', version: app.version, }), ]; + } else if (targetInfo.platform === 'android') { + const debug = isDebug ? 'debug' : 'release'; + + // TODO: detect (un)signed beforehand + + if (!isDebug) { + // unsigned release apks + artifacts.push( + createArtifact({ + path: join( + artifactsPath, + `apk/universal/release/app-universal-unsigend.apk`, + ), + name: app.name, + platform: targetInfo.platform, + arch: 'universal', + bundle: 'apk', + version: app.version, + }), + createArtifact({ + path: join(artifactsPath, `apk/arm64/release/app-arm64-unsigend.apk`), + name: app.name, + platform: targetInfo.platform, + arch: 'arm64', + bundle: 'apk', + version: app.version, + }), + createArtifact({ + path: join(artifactsPath, `apk/arm/release/app-arm-unsigend.apk`), + name: app.name, + platform: targetInfo.platform, + arch: 'universal', + bundle: 'apk', + version: app.version, + }), + createArtifact({ + path: join( + artifactsPath, + `apk/x86_64/release/app-x86_64-unsigend.apk`, + ), + name: app.name, + platform: targetInfo.platform, + arch: 'arm', + bundle: 'apk', + version: app.version, + }), + createArtifact({ + path: join(artifactsPath, `apk/x86/release/app-x86-unsigend.apk`), + name: app.name, + platform: targetInfo.platform, + arch: 'x86', + bundle: 'apk', + version: app.version, + }), + ); + } + + artifacts.push( + // signed release apks and debug apks + createArtifact({ + path: join( + artifactsPath, + `apk/universal/${debug}/app-universal-${debug}.apk`, + ), + name: app.name, + platform: targetInfo.platform, + arch: 'universal', + bundle: 'apk', + version: app.version, + }), + createArtifact({ + path: join(artifactsPath, `apk/arm64/${debug}/app-arm64-${debug}.apk`), + name: app.name, + platform: targetInfo.platform, + arch: 'arm64', + bundle: 'apk', + version: app.version, + }), + createArtifact({ + path: join(artifactsPath, `apk/arm/${debug}/app-arm-${debug}.apk`), + name: app.name, + platform: targetInfo.platform, + arch: 'universal', + bundle: 'apk', + version: app.version, + }), + createArtifact({ + path: join( + artifactsPath, + `apk/x86_64/${debug}/app-x86_64-${debug}.apk`, + ), + name: app.name, + platform: targetInfo.platform, + arch: 'arm', + bundle: 'apk', + version: app.version, + }), + createArtifact({ + path: join(artifactsPath, `apk/x86/${debug}/app-x86-${debug}.apk`), + name: app.name, + platform: targetInfo.platform, + arch: 'x86', + bundle: 'apk', + version: app.version, + }), + // + // aabs + // + createArtifact({ + path: join( + artifactsPath, + `/bundle/universal${debug}/app-universal-${debug}.aab`, + ), + name: app.name, + platform: targetInfo.platform, + arch: 'universal', + bundle: 'aab', + version: app.version, + }), + createArtifact({ + path: join( + artifactsPath, + `/bundle/arm64${debug}/app-arm64-${debug}.aab`, + ), + name: app.name, + platform: targetInfo.platform, + arch: 'arm64', + bundle: 'aab', + version: app.version, + }), + createArtifact({ + path: join(artifactsPath, `/bundle/arm${debug}/app-arm-${debug}.aab`), + name: app.name, + platform: targetInfo.platform, + arch: 'arm', + bundle: 'aab', + version: app.version, + }), + createArtifact({ + path: join( + artifactsPath, + `/bundle/x86_64${debug}/app-x86_64-${debug}.aab`, + ), + name: app.name, + platform: targetInfo.platform, + arch: 'x86_64', + bundle: 'aab', + version: app.version, + }), + createArtifact({ + path: join(artifactsPath, `/bundle/x86${debug}/app-x86-${debug}.aab`), + name: app.name, + platform: targetInfo.platform, + arch: 'x86', + bundle: 'aab', + version: app.version, + }), + ); + } else if (targetInfo.platform === 'ios') { + // TODO: Confirm that info.name is correct. + artifacts = [ + createArtifact({ + path: join(artifactsPath, `x86_64/${app.name}.ipa`), + name: app.name, + platform: targetInfo.platform, + arch: 'x86_64', + bundle: 'ipa', + version: app.version, + }), + createArtifact({ + path: join(artifactsPath, `arm64/${app.name}.ipa`), + name: app.name, + platform: targetInfo.platform, + arch: 'arm64', + bundle: 'ipa', + version: app.version, + }), + createArtifact({ + path: join(artifactsPath, `arm64-sim/${app.name}.ipa`), + name: app.name, + platform: targetInfo.platform, + arch: 'arm64-sim', + bundle: 'ipa', + version: app.version, + }), + ]; + } else { + // eslint-disable-next-line @typescript-eslint/restrict-template-expressions + console.error(`Unhandled target platform: "${targetInfo.platform}"`); } if (uploadPlainBinary) { @@ -386,7 +567,7 @@ export async function buildProject(): Promise { path: join(artifactsPath, `${app.mainBinaryName}${ext}`), name: 'binary', // app.mainBinaryName, bundle: 'bin', - debug, + platform: targetInfo.platform, arch, version: app.version, diff --git a/src/index.ts b/src/index.ts index 6833fc1..bb64b5f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,6 +7,7 @@ import { buildProject } from './build'; import { getOrCreateRelease } from './create-release'; import { includeUpdaterJson, + isIOS, parsedArgs, retryAttempts, shouldUploadWorkflowArtifacts, @@ -20,6 +21,10 @@ import type { Artifact } from './types'; async function run(): Promise { try { + if (isIOS && process.platform !== 'darwin') { + throw new Error('Building for iOS is only supported on macOS runners.'); + } + // inputs that won't be changed are in ./inputs let tagName = core.getInput('tagName').replace('refs/tags/', ''); let releaseId = Number(core.getInput('releaseId')); diff --git a/src/inputs.ts b/src/inputs.ts index 06e996a..7a06ddc 100644 --- a/src/inputs.ts +++ b/src/inputs.ts @@ -84,3 +84,7 @@ export const uploadUpdaterSignatures = core.getBooleanInput( export const updaterJsonPreferNsis = core.getBooleanInput( 'updaterJsonPreferNsis', ); + +export const isAndroid = core.getInput('mobile').toLowerCase() === 'android'; +export const isIOS = core.getInput('mobile').toLowerCase() === 'ios'; +export const isDebug = parsedArgs['debug'] as boolean; diff --git a/src/upload-version-json.ts b/src/upload-version-json.ts index 384b2b8..c63420b 100644 --- a/src/upload-version-json.ts +++ b/src/upload-version-json.ts @@ -328,7 +328,6 @@ export async function uploadVersionJSON( const artifact = createArtifact({ path: versionFile, name: versionFilename, - debug: false, platform: targetInfo.platform, arch: '', bundle: '', diff --git a/src/utils.ts b/src/utils.ts index c815ba2..3a8a975 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -24,7 +24,7 @@ import type { } from './types'; import { GitHub } from '@actions/github/lib/utils'; import { findUpSync } from 'find-up-simple'; -import { owner, projectPath, repo } from './inputs'; +import { isAndroid, isDebug, isIOS, owner, projectPath, repo } from './inputs'; /*** constants ***/ export const extensions = [ @@ -139,7 +139,6 @@ export function ghAssetName( export function createArtifact({ path, name, - debug, platform, arch, bundle, @@ -147,7 +146,6 @@ export function createArtifact({ }: { path: string; name: string; - debug: boolean; platform: TargetPlatform; arch: string; bundle: string; @@ -167,7 +165,7 @@ export function createArtifact({ return { path, name, - mode: debug ? 'debug' : 'release', + mode: isDebug ? 'debug' : 'release', platform: platform === 'macos' ? 'darwin' : platform, arch, bundle, @@ -546,12 +544,18 @@ export function getInfo(targetInfo?: TargetInfo, configFlag?: string): Info { export function getTargetInfo(targetPath?: string): TargetInfo { let arch: string = process.arch; - let platform: TargetPlatform = - process.platform === 'win32' - ? 'windows' - : process.platform === 'darwin' - ? 'macos' - : 'linux'; + let platform: TargetPlatform; + if (isAndroid) { + platform = 'android'; + } else if (isIOS) { + platform = 'ios'; + } else if (process.platform === 'win32') { + platform = 'windows'; + } else if (process.platform === 'darwin') { + platform = 'macos'; + } else { + platform = 'linux'; + } if (targetPath) { if (targetPath.includes('windows')) {