从零搭建一个 Tauri NextJS Android 项目

Tauri 简介

Tauri 是一个轻量、高性能的跨平台应用开发框架,允许使用 Web 技术(如 HTML、JavaScript、CSS)构建 UI,同时以 Rust、Swift 或 Kotlin 编写后端逻辑,生成适用于主流桌面和移动平台的本地应用程序。

目前 Tauri 已经发布 V2 版本,支持包括 Android、iOS 在内的移动平台。

Tauri 的主要优势在于构建体积小、技术选型灵活,以及基于 Rust 构建所带来的高性能与可靠性:

  • 轻量:Tauri 没有像 Electron 那样将完整的 Chromium 打包进应用中,而是使用系统原生的 WebView,因此生成的应用体积非常小。
  • 灵活的前端框架选择:它不依赖特定的前端技术栈,开发者可以自由选择 React、Vue、Svelte、Next.js 等任意框架。
  • 高性能与安全性:底层采用 Rust 编写,具备出色的运行性能和更强的内存安全保障。

当然,Tauri 也有一些需要注意的限制:

  • 平台兼容性差异:由于依赖系统 WebView,开发者需要手动处理各平台之间在 WebView 实现上的差异,特别是在 Android 和旧版 Windows 上。
  • Rust 学习成本:虽然可以仅编写前端,但如果希望更深入地控制应用行为、扩展系统功能或编写后端逻辑,仍然需要具备一定的 Rust 编程能力。

可以参考 Tauri 官方提供的前置条件部分来完成初步的项目环境搭建。

项目初始化

Tauri 官方有提供有 cli 来帮助创建项目,具体可以查看官方的创建项目

这里以 pnpm 为例。

$ pnpm create tauri-app
✔ Project name · tauri-android-tutorial
✔ Identifier · com.example.app
✔ Choose which language to use for your frontend · TypeScript / JavaScript - (pnpm, yarn, npm, deno, bun)
✔ Choose your package manager · pnpm
✔ Choose your UI template · Vanilla
✔ Choose your UI flavor · TypeScript

运行 pnpm create tauri-app 命令后,会进入交互式初始化流程,依次配置即可。

配置项说明:

  1. Project name
    项目名称,将作为项目目录名和默认包名。这里填写 tauri-android-tutorial
  2. Identifier
    应用标识符,建议使用反转域名格式(如 com.example.app),未来用于 Android 包名等场景。
  3. Frontend language
    前端开发语言,推荐选择 TypeScript / JavaScript,后续我们会引入 Next.js。
  4. Package manager
    包管理器选择,本文统一使用 pnpm,也可根据个人偏好选择 yarnnpm
  5. UI template
    初始前端模板,选择 Vanilla 意味着不使用任何框架(如 React/Vue),方便我们后续自行集成 Next.js。
  6. UI flavor
    选择使用的语言风格,推荐使用 TypeScript,更有利于类型安全和开发效率。

配置好后,命令行会输出内容来告诉我们接下来该怎么做。

进入项目文件夹,安装依赖,初始化 Android 配置,如果是桌面开发就使用 pnpm tauri dev 如果是 Android 开发就使用 pnpm tauri android dev

Template created! To get started run:
  cd tauri-android-tutorial
  pnpm install
  pnpm tauri android init

For Desktop development, run:
  pnpm tauri dev

For Android development, run:
  pnpm tauri android dev

Tauri 虽然也支持 iOS,但由于苹果官方政策限制,iOS 应用开发必须在 macOS 上进行,并依赖 Xcode 工具链。因此,如果你当前使用的是 Windows 或 Linux 开发环境,暂时无法进行 iOS 构建和调试。

尝试构建

完成依赖安装和 Android 初始化之后,就可以尝试构建 Android 应用了。

在项目根目录下运行 pnpm tauri android build

首次执行构建命令时,系统可能会下载缺失的构建依赖,因此耗时较长是正常的,请耐心等待。

构建完成后,命令行会输出构建产物的路径信息,例如:

...
    Finished 1 APK at:
        E:\tauri-android-tutorial\src-tauri\gen/android\app/build/outputs/apk/universal/release/app-universal-release-unsigned.apk

    Finished 1 AAB at:
        E:\tauri-android-tutorial\src-tauri\gen/android\app/build/outputs/bundle/universalRelease/app-universal-release.aab

可以看见,有 APK 和 AAB 两种格式,APK 是 Android 安装包,可直接安装到真机或模拟器上,用于调试与内部测试,AAB 则是 Android App Bundle,适用于发布到 Google Play 商店,由 Google 进行按需拆包与分发。

APK 签名

在完成构建之后,可以尝试将构建的 APK 文件安装到手机上。

然后就会看见安装失败,这是因为没有签名导致的。

这是因为 Android 系统要求所有 APK 必须先使用证书进行数字签名,然后才能安装到设备上或进行更新。

所以我们需要对构建产物进行签名。

需要注意,Google Play 以第一次上传的签名为准,要求后续上传的文件签名一致。

生成签名

keytool 是 Java 数据证书管理工具。

可以在 Android Studio 安装目录下的 /jbr/bin/ 目录中找到 keytool.exe

keytool -genkey -v -keystore 自定义的数据文件名称 -storetype JKS -keyalg RSA -keysize 2048 -validity 10000 -alias 自定义的证书别名

之后按照提示进行输入即可。

如果是要上架应用商店的,要如实填写。否则可能会导致因信息不对从而上架失败。

输入完毕,确认后,工具会要求设置密码。记住密码,后面会用到。

完成以上操作后,在当前工作目录下会生成一个 自定义的数据文件名称.keystore 文件。

构建时签名

创建 src-tauri/gen/android/keystore.properties 文件。

文件内容如下:

storePassword=数据文件密码
keyPassword=证书密码
keyAlias=自定义的证书别名
storeFile=自定义的数据文件名称.keystore

需要注意,在 windows 下,storeFile 路径需要 C:\\Program Files\\Android 这样的格式。

随后找到 src-tauri/gen/android/app/build.gradle.kts 文件。

增加以下内容:

import java.io.FileInputStream

// ...
signingConfigs {
  create("release") {
      val keystorePropertiesFile = rootProject.file("keystore.properties")
      val keystoreProperties = Properties()
      if (keystorePropertiesFile.exists()) {
          keystoreProperties.load(FileInputStream(keystorePropertiesFile))
      }
      keyAlias = keystoreProperties["keyAlias"] as String
      keyPassword = keystoreProperties["keyPassword"] as String
      storeFile = File(keystoreProperties["storeFile"] as String)
      storePassword = keystoreProperties["storePassword"] as String
  }
}
// ...

// ...
signingConfig = signingConfigs.getByName("release")

image-dxacrphd.png

配置完毕之后,重新打包,APK 安装成功。

集成 NextJS

本文使用的是 NextJS 的 App Router。

Tauri 要求前端资源必须是可导出的静态 HTML,因此我们需要使用 NextJS 提供的静态导出功能。

执行 pnpm add next@latest react@latest react-dom@latest 安装 NextJS,因为使用了 TS,因此需要还需要执行 pnpm add -D @types/react @types/react-dom 命令来安装 React 的类型定义。

接着修改 package.jsonscripts 部分。

  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint",
    "tauri": "tauri"
  },

创建并修改 next.config.ts 文件。

import type { NextConfig } from "next"

const nextConfig: NextConfig = {
  output: "export",
  images: {
    unoptimized: true
  }
}

export default nextConfig

接着创建 src/app 目录,和 src/app/layout.tsxsrc/app/page.tsx 两个文件。

// src/app/layout.tsx
export default function rootLayout({ children }: React.PropsWithChildren) {
  return (
    <html>
      <head></head>
      <body>
        {children}
      </body>
    </html>
  )
}

// src/app/page.tsx
export default function Home() {
  return (
    <div>
      Home
    </div>
  )
}

修改 tauri.conf.json 文件中的 build 部分。

  "build": {
    "beforeDevCommand": "pnpm dev",
    "devUrl": "http://localhost:3000",
    "beforeBuildCommand": "pnpm build",
    "frontendDist": "../out"
  },

随后执行 pnpm tauri dev 来运行项目。

多语言配置

NextJS 官方是有多语言支持的,但要求是通过 NextJS 运行项目,而 Tauri 要求前端资源必须是可导出的静态 HTML,因此需要我们手动实现多语言功能。

安装依赖 pnpm add i18next i18next-resources-to-backend react-i18next

多语言配置代码

// i18n.ts
import { createInstance, KeyPrefix } from 'i18next'
import resourcesToBackend from 'i18next-resources-to-backend'
import { initReactI18next } from 'react-i18next/initReactI18next'

/**
 * 支持语言列表
 */
export const SUPPORT_LANGUAGES = ['en', 'de', 'fr', 'es', 'it', 'nl', 'pt', 'ar', 'ja'] as const satisfies string[]
export type SUPPORT_LANGUAGES = typeof SUPPORT_LANGUAGES

/**
 * 支持的语言
 */
export type SUPPORT_LANGUAGE = SUPPORT_LANGUAGES[number]

/**
 * 语言对应全称
 */
export const SUPPORT_LANGUAGES_FULL_NAME = {
  en: 'English',
  de: 'Deutsch',
  fr: 'Français',
  es: 'Español',
  it: 'Italiano',
  nl: 'Netherlands',
  pt: 'Português',
  ar: 'عربى',
  ja: 'Japan',
} as const satisfies Record<SUPPORT_LANGUAGE, string>
export type SUPPORT_LANGUAGES_FULL_NAME = typeof SUPPORT_LANGUAGES_FULL_NAME

/**
 * 右到左方向排列语言列表
 */
export const RTL_LANGUAGES: Array<SUPPORT_LANGUAGE> = ['ar']

/**
 * 语言资源命名空间列表
 */
export const LANGUAGE_NAMESPACES = ['common', 'home'] as const satisfies string[]
export type LANGUAGE_NAMESPACES = typeof LANGUAGE_NAMESPACES

/**
 * 语言资源命名空间
 */
export type LANGUAGE_NAMESPACE = LANGUAGE_NAMESPACES[number]

/**
 * 默认语言
 */
export const DEFAULT_LANGUAGE: SUPPORT_LANGUAGE = 'en'

/**
 * 默认命名空间
 */
export const DEFAULT_NAMESPACE: LANGUAGE_NAMESPACE = 'common'

export type withLocaleProps = { locale: SUPPORT_LANGUAGE }

const initI18next = async (lng: SUPPORT_LANGUAGE, ns: LANGUAGE_NAMESPACE) => {
  const i18nInstance = createInstance()

  await i18nInstance
    .use(initReactI18next)
    .use(
      resourcesToBackend<SUPPORT_LANGUAGE, LANGUAGE_NAMESPACE>(
        (lng: SUPPORT_LANGUAGE, ns: LANGUAGE_NAMESPACE) => import(`#/locales/${lng}/${ns}.json`),
      ),
    )
    .init({
      supportedLngs: SUPPORT_LANGUAGES,
      fallbackLng: DEFAULT_LANGUAGE,
      lng,
      fallbackNS: DEFAULT_NAMESPACE,
      defaultNS: DEFAULT_NAMESPACE,
      ns,
    })

  return i18nInstance
}

export async function createTranslation(
  lng: SUPPORT_LANGUAGE,
  ns: LANGUAGE_NAMESPACE,
  options?: KeyPrefix<LANGUAGE_NAMESPACE>,
) {
  const i18nextInstance = await initI18next(lng, ns)
  return {
    t: i18nextInstance.getFixedT<LANGUAGE_NAMESPACE, string>(lng, Array.isArray(ns) ? ns[0] : ns, options),
    i18n: i18nextInstance,
  }
}

在页面中使用

// src/app/[locale]/page.tsx
import style from './page.module.scss'
import { createTranslation, DEFAULT_LANGUAGE, SUPPORT_LANGUAGES, type withLocaleProps } from '@/lib/i18n'

export async function generateStaticParams() {
  return SUPPORT_LANGUAGES.map((locale) => ({ locale }))
}

export default async function Home({ params }: { params: Promise<withLocaleProps> }) {
  const locale = (await params).locale ?? DEFAULT_LANGUAGE
  const { t } = await createTranslation(locale, 'home')
  return (
    <div className={style.container}>
      <h1>{t('home-title')}</h1>
    </div>
  )
}

使用 github Action 进行自动化构建

完成以上步骤后,一个简单的 Tauri Android 项目就搭建好了。

在开发完毕后手动构建就行。

但每次开发后手动构建有点影响效率。

因此我们需要自动化构建。

我们可以使用 github 提供的 Action。

首先创建以下环境变量:

  • BASE64_JKS
    • Base64 编码后的签名文件
  • KEY_ALIAS
    • keystore.properties 文件中的 keyAlias
  • KEY_PASSWORD
    • keystore.properties 文件中的 keyPassword
  • NDK_VERSION
    • NDK 版本,最新的 LTS 版本是 r27c,最新稳定版是 r28b。
  • STORE_FILE
    • keystore.properties 文件中的 storeFile
    • 需要注意 linux 环境和 windows 环境中路径的差异。
  • STORE_PASSWORD
    • keystore.properties 文件中的 storePassword

以下是配置:

# .github/workflows/build.yml
name: Build APK
description: Build APK File

on:
  workflow_dispatch:
  push:
    tags:
      - v*

jobs:
  build-APK:
    permissions:
      contents: write
    strategy:
      fail-fast: false
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Install deps (ubuntu only)
        run: |
          sudo apt-get update
          sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf

      - name: Install pnpm
        uses: pnpm/action-setup@v4
        with:
          version: 10

      - name: Install Node
        uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'pnpm'

      - name: Install Rust
        uses: dtolnay/rust-toolchain@stable
        with:
          targets: "aarch64-linux-android x86_64-linux-android armv7-linux-androideabi i686-linux-android"

      - name: Install Android NDK
        id: ndk
        uses: nttld/setup-ndk@v1
        with:
          ndk-version: ${{ secrets.NDK_VERSION }}

      - name: Install front deps
        run: pnpm install

      - name: Rust cache
        uses: swatinem/rust-cache@v2
        with:
          workspaces: './src-tauri -> target'

      - name: Cache node_modules
        uses: actions/cache@v4
        with:
          path: '~/.pnpm-store'
          key: ${{ runner.os }}-node_modules-${{ hashFiles('**/pnpm-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-pnpm-store-

      - name: Set environment variables for Android NDK
        run: |
          echo "NDK_HOME=${{steps.ndk.outputs.ndk-path}}" >> $GITHUB_ENV
          echo "SDK_ROOT=$HOME/android-sdk" >> $GITHUB_ENV

      - name: Create keystore.properties and jks file
        env:
          storeFile: ${{ secrets.STORE_FILE }}
          storePassword: ${{ secrets.STORE_PASSWORD }}
          keyAlias: ${{ secrets.KEY_ALIAS }}
          keyPassword: ${{ secrets.KEY_PASSWORD }}
          jksBase64: ${{ secrets.BASE64_JKS }}
        run: |
          resolvedStoreFile="${storeFile/#\~/$HOME}"
          mkdir -p "$(dirname $"$resolvedStoreFile")"

          echo "storeFile=$resolvedStoreFile" > ${{github.workspace}}/src-tauri/gen/android/keystore.properties
          echo "storePassword=$storePassword" >> ${{github.workspace}}/src-tauri/gen/android/keystore.properties
          echo "keyAlias=$keyAlias" >> ${{github.workspace}}/src-tauri/gen/android/keystore.properties
          echo "keyPassword=$keyPassword" >> ${{github.workspace}}/src-tauri/gen/android/keystore.properties
          echo "$jksBase64" | base64 --decode > "$resolvedStoreFile"

      - name: Build App Bundle
        run: pnpm tauri android build

      - name: Upload Artifacts
        uses: actions/upload-artifact@v4
        with:
          name: App Bundle ${{ github.ref_name }}
          path: |
            ${{ github.workspace }}/src-tauri/gen/android/app/build/outputs/bundle/universalRelease/app-universal-release.aab
            ${{ github.workspace }}/src-tauri/gen/android/app/build/outputs/apk/universal/release/app-universal-release.apk

当发布一个 v 开头的 tag,如 v1.2.3 时,这个 Action会自动触发,并进行构建。

访问该 action 界面,在下方可以下载构建产物(前提是构建成功)。

使用 github Action 进行自动管理版本

每次都需要手动修改版本并打 tag 还是有点繁琐了。

所以需要将手动的版本管理改为自动版本管理。

所以需要再次编写一个Action。

# .github/workflows/release.yml
name: Release App Bundle
description: Release App Bundle

on:
  workflow_dispatch:
  push:
    branches:
      - main

permissions:
  contents: write
  pull-requests: write
  issues: write

jobs:
  release:
    runs-on: ubuntu-latest
    steps:
      - name: Release Please
        id: release
        uses: googleapis/release-please-action@v4
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
          config-file: release-please-config.json
          manifest-file: .release-please-manifest.json
// .release-please-manifest.json
{
  ".": "0.0.1"
}
// release-please-config.json
{
  "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json",
  "sequential-calls": true,
  "plugins": [
    "cargo-workspace"
  ],
  "pull-request-title-pattern": "release version: v1.9.0",
  "include-component-in-tag": false,
  "packages": {
    ".": {
      "changelog-path": "CHANGELOG.md",
      "release-type": "node",
      "bump-minor-pre-major": true,
      "bump-patch-for-minor-pre-major": true,
      "draft": false,
      "prerelease": false,
      "extra-files": [
        {
          "type": "json",
          "path": "src-tauri/tauri.conf.json",
          "jsonpath": "$.version"
        }
      ]
    }
  }
}

配置完毕后,当我们按照对应规范进行 commit 后,就会触发 Action 帮助我们进行版本管理。

具体 commit 规范可以参考这个网址

引入 Git Commit 规范

完成以上步骤后,当开发完毕进行 commit 时,Action 会根据 commit 规范来提取 commit 信息构建 CHANGELOG,但 commit 内容这依赖于开发者的自觉。

因此我们需要引入限制,保证 commit 规范。

安装对应依赖 pnpm add commitizen cz-custom izablecz-conventional-changelog husky -D

修改 package.json,在 scripts 部分新增 "prepare": "husky"

// .cz-config.json
{
  "types": [
    { "value": "feat", "name": "特性: 一个新的特性" },
    { "value": "fix", "name": "修复: 修复一个Bug" },
    { "value": "docs", "name": "文档: 变更的只有文档" },
    { "value": "style", "name": "格式: 空格, 分号等格式修复" },
    { "value": "refactor", "name": "重构: 代码重构,注意和特性、修复区分开" },
    { "value": "perf", "name": "性能: 提升性能" },
    { "value": "test", "name": "测试: 添加一个测试" },
    { "value": "revert", "name": "回滚: 代码回退" },
    { "value": "chore", "name": "工具:开发工具变动(构建、脚手架工具等)" },
    { "value": "merge", "name": "合并:合并代码" },
    { "value": "build", "name": "打包: 打包发布" },
    { "value": "ci", "name": "集成: 持续集成" },
    { "value": "release", "name": "发布: 发布新版本" },
    { "value": "other", "name": "其他: 其他改动,比如构建流程, 依赖管理" },
  ],
  "messages": {
    "type": "选择提交类型:",
    "customScope": "修改范围(可选):",
    "subject": "短说明:",
    "body": "长说明,使用\"|\"换行(可选):",
    "footer": "关联关闭的issue,例如:#31, #34(可选):",
    "confirmCommit": "确定提交说明?"
  },
  "allowCustomScopes": true,
  "allowBreakingChanges": ["feat"],
  "subjectLimit": 100
}
// commitlint.config.cjs
const czConfig = require("./.cz-config.json")

module.exports = {
  extends: ["@commitlint/config-conventional"],
  rules: {
    // type 类型定义
    "type-enum": [2, "always", czConfig.types.map((item) => item.value)],
    // subject 大小写不做校验
    // 自动部署的BUILD ROBOT的commit信息大写,以作区别
    "subject-case": [0],
  },
}

// .czrc
{
  "path": "cz-customizable",
  "config": ".cz-config.cjs"
}

# .husky/commit-msg
#!/bin/sh

# 定义颜色
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
NC='\033[0m' # 无色(重置颜色)

# 执行 commitlint 校验
if ! npx commitlint -e "$GIT_PARAMS"; then
  echo ""
  echo -e "${RED}🚫 提交信息校验失败!${NC}"
  echo -e "${YELLOW}请按照规范书写提交信息,例如:${NC}"
  echo -e "  ${CYAN}feat(front): 新增用户登录功能${NC}"
  echo -e "  ${CYAN}fix(styles): 修复登录页面样式问题${NC}"
  echo ""
  echo -e "${GREEN}👉 建议使用:npm run commit 以规范提交${NC}"
  exit 1
fi

修改 package.jsonscripts 部分,新增 "commit": "git-cz" 部分。

执行 pnpm commit 即可进入交互式 commit 流程。

需要 git 暂存区有暂存文件!

使用 github Action 进行自动化发布版本

将构建 APK 和版本管理两个 Action 合并。

当提交到主分支时,会触发版本管理步骤,会创建一个版本更改的 PR。

当合并版本更改 PR 后,会触发构建步骤,自动将构建产物上传到版本发布 release 中。

以下是具体配置:

name: Release App Bundle
description: Build App Bundle And Release

on:
  workflow_dispatch:
  push:
    branches:
      - main

permissions:
  contents: write
  pull-requests: write
  issues: write

jobs:
  release:
    environment: prerelease
    runs-on: ubuntu-latest
    outputs:
      release_created: ${{ steps.release.outputs.release_created }}
      tag_name: ${{ steps.release.outputs.tag_name }}
    steps:
      - name: Release Please
        id: release
        uses: googleapis/release-please-action@v4
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
          config-file: release-please-config.json
          manifest-file: .release-please-manifest.json

  build-APK:
    environment: prerelease
    needs: release
    if: ${{ needs.release.outputs.release_created == 'true' }}
    strategy:
      fail-fast: false
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Install deps (ubuntu only)
        run: |
          sudo apt-get update
          sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf

      - name: Install pnpm
        uses: pnpm/action-setup@v4
        with:
          version: 10

      - name: Install Node
        uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'pnpm'

      - name: Install Rust
        uses: dtolnay/rust-toolchain@stable
        with:
          targets: "aarch64-linux-android x86_64-linux-android armv7-linux-androideabi i686-linux-android"

      - name: Install Android NDK
        id: ndk
        uses: nttld/setup-ndk@v1
        with:
          ndk-version: ${{ secrets.NDK_VERSION }}

      - name: Install front deps
        run: pnpm install

      - name: Rust cache
        uses: swatinem/rust-cache@v2
        with:
          workspaces: './src-tauri -> target'

      - name: Cache node_modules
        uses: actions/cache@v4
        with:
          path: '~/.pnpm-store'
          key: ${{ runner.os }}-node_modules-${{ hashFiles('**/pnpm-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-pnpm-store-

      - name: Set environment variables for Android NDK
        run: |
          echo "NDK_HOME=${{steps.ndk.outputs.ndk-path}}" >> $GITHUB_ENV
          echo "SDK_ROOT=$HOME/android-sdk" >> $GITHUB_ENV

      - name: Create keystore.properties and jks file
        env:
          storeFile: ${{ secrets.STORE_FILE }}
          storePassword: ${{ secrets.STORE_PASSWORD }}
          keyAlias: ${{ secrets.KEY_ALIAS }}
          keyPassword: ${{ secrets.KEY_PASSWORD }}
          jksBase64: ${{ secrets.BASE64_JKS }}
        run: |
          resolvedStoreFile="${storeFile/#\~/$HOME}"
          mkdir -p "$(dirname $"$resolvedStoreFile")"

          echo "storeFile=$resolvedStoreFile" > ${{github.workspace}}/src-tauri/gen/android/keystore.properties
          echo "storePassword=$storePassword" >> ${{github.workspace}}/src-tauri/gen/android/keystore.properties
          echo "keyAlias=$keyAlias" >> ${{github.workspace}}/src-tauri/gen/android/keystore.properties
          echo "keyPassword=$keyPassword" >> ${{github.workspace}}/src-tauri/gen/android/keystore.properties
          echo "$jksBase64" | base64 --decode > "$resolvedStoreFile"

      - name: Build APK
        run: pnpm tauri android build --apk

      - name: Build AAB
        run: pnpm tauri android build --aab

      - name: Upload Artifacts
        uses: actions/upload-artifact@v4
        with:
          name: App Bundle ${{ github.ref_name }}
          path: |
            ${{ github.workspace }}/src-tauri/gen/android/app/build/outputs/bundle/universalRelease/app-universal-release.aab
            ${{ github.workspace }}/src-tauri/gen/android/app/build/outputs/apk/universal/release/app-universal-release.apk

      - name: Upload Release
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          gh release upload ${{ needs.release.outputs.tag_name }} \
          ${{ github.workspace }}/src-tauri/gen/android/app/build/outputs/bundle/universalRelease/app-universal-release.aab \
          ${{ github.workspace }}/src-tauri/gen/android/app/build/outputs/apk/universal/release/app-universal-release.apk \
          --clobber \
          --repo "$GITHUB_REPOSITORY"

完成截图

image-ujlsjxjz.png

image-mckvumzt.png


从零搭建一个 Tauri NextJS Android 项目
http://www.inksha.com/archives/cong-ling-da-jian-yi-ge-tauri-nextjs-android-xiang-mu
作者
inksha
发布于
2025年05月20日
许可协议