发布于 2026-01-06 6 阅读
0

如何在 React Native 中管理预发布和生产环境?导入 "GeneratedDotEnv.m" // 由 BuildDotenvConfig.ruby 在构建过程中生成

如何在 React Native 中管理测试环境和生产环境

import "GeneratedDotEnv.m" // 由 BuildDotenvConfig.ruby 在构建过程中写入

作为一名 React Native 开发者,你的生活即将变得更加轻松。至少我的生活是这样,自从我学会了如何在 React Native 中管理测试环境和生产环境之后,一切都变得简单多了。这并不会改变你的 React Native 应用的外观、体验或销售方式,但它能为你省去大量的开发工作。

我最初将这篇文章发布在around25 的博客上。

如果只需要修改一两项配置,那修改起来简直轻而易举。切换API host到生产环境配置,搞定!但如果需要修改很多东西API host,比如设置支付网关keyhost设置通知.plist文件、更新崩溃日志key和分析数据key,事情就会变得复杂得多,也更容易出错。每次构建都要手动完成这些操作,我敢保证你肯定会漏掉一些东西。

为了避免麻烦,我们将使用环境变量。

为了将环境变量暴露给React Native react-native,我将使用react-native-config库。这样做可以将配置变量与代码隔离开来。这些变量在不同环境下很可能不同,而代码本身则保持不变。

首先,我要把这个库添加到项目中。在我写这篇文章的时候,最新版本react-native-config0.11.7。然后,我需要在root项目的 `<path>` 目录下创建 3 个环境变量文件:一个用于本地环境,简单地命名为 `<local_environment_name>` .env,另外两个分别命名为 `<local_environment_name>` 和 ` .env.staging<local_environment_name> .env.production`。



// .env
IS_PRODUCTION=false
API_HOST=https://api.staging.foobar.com

// .env.staging
IS_PRODUCTION=false
API_HOST=https://api.staging.foobar.com

// .env.production
IS_PRODUCTION=true
API_HOST=https://api.foobar.com


Enter fullscreen mode Exit fullscreen mode

目前,我只API host在配置中保留了它,以及一个IS_PRODUCTION标志。我们稍后会用到它。显然,两者https://api.staging.foobar.com都是https://api.foobar.com虚构的🙂。

现在,为了在我的 React Native 应用中使用这些变量,我的配置文件通常是这样的:



// src/config/index.js

import env from 'react-native-config'

const config = {
  api: {
    host: env.API_HOST,
    timeout: 20000
  }
};

const API_HOST = config.api.host;

export {
  API_HOST
}

export default config


Enter fullscreen mode Exit fullscreen mode

就这些。默认情况下,react-native-config程序会从.env文件中读取数据,但也可以通过以下命令从任何文件读取数据并运行项目:



$ ENVFILE=.env.staging react-native run-ios  


Enter fullscreen mode Exit fullscreen mode

目前一切顺利,但我希望构建过程尽可能自动化。因此,手动指定.env文件是不可接受的。接下来,我们将着手避免这种情况。

iOS 设置

在 iOS 平台上,我将利用Scheme的概念。Xcode Scheme 定义了一组要构建的目标以及构建时要使用的配置。默认 Scheme 可以在 Xcode 的菜单栏中找到:



Product -> Scheme -> FooBar


Enter fullscreen mode Exit fullscreen mode

其中FooBar是我的应用程序的名称。

显示 iOS 产品方案

我打算复制默认方案,并创建两个新的方案:一个用于测试环境,一个用于生产环境。我们将使用默认方案作为本地环境。您可以通过以下步骤操作:



Product -> Scheme -> Edit Scheme -> Duplicate Scheme


Enter fullscreen mode Exit fullscreen mode

Xcode 2

我打算把它们分别命名为FooBar.stagingFooBar.production。命名在我们稍后编写自动构建脚本时非常重要。

我将选中FooBar.staging并返回Edit Scheme,以确保我的方案加载了.env我想要的文件。在这里,Build -> Pre-actions我添加了一个执行此操作的脚本。

添加预构建脚本

添加预构建脚本第 2 部分

我正在经历完全相同的过程FooBar.production

现在,iOS 的配置都搞定了。每次构建应用时,只要选择一个 scheme,就能加载正确的环境变量。搞定!接下来我们来看看 Android。

Android 设置

对于 Android,我们有构建类型。在android/app/build.gradle`<build-type>` 标签下,buildTypes我们有两种默认构建类型:发布版调试版



buildTypes {
    release {
        minifyEnabled enableProguardInReleaseBuilds
        proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
        signingConfig signingConfigs.release
    }
    debug {
        debuggable true
    }
}



Enter fullscreen mode Exit fullscreen mode

与之前一样,默认情况下将使用本地环境变量。

对于新的构建类型,请添加以下代码行:



stagingrelease {
    initWith release
}
productionrelease {
    initWith release
}



Enter fullscreen mode Exit fullscreen mode

这些都将用于创建发布版本,因此请务必在继续操作之前生成签名密钥

命名在这一步非常重要。首先,构建变体名称中必须包含“release”标记,才能应用常规的发布构建行为,详情请参见此处。其次,我最初将名称设置为releaseStagingreleaseProduction.env时遇到了问题,因为没有加载正确的文件。我在网上搜索后找到了问题和解决方案,请参见此处

现在,在同一个文件的最顶部android/app/build.gradle,添加以下内容:



project.ext.envConfigFiles = [
        debug: ".env",
        release: ".env",
        stagingrelease: ".env.staging",
        productionrelease: ".env.production"
]



Enter fullscreen mode Exit fullscreen mode

使用 Proguard 构建发布版本时可能会遇到问题。解决方案在这里

要使用上述定义的构建类型之一运行应用程序,请View -> Tool Windows -> Build Variants在构建之前转到并选择变体(在新显示的窗口中):

Android 构建变体窗口

Android 部分也到此为止。接下来,我们将通过创建fastlane脚本进一步自动化构建流程。

我的目标是为这两个平台分别编写一行构建脚本。

额外内容:快速通道脚本

首先,我们将安装 fastlane并进行设置。fastlane init它必须在 iOS 或 Android 项目中运行,所以我们将在 ios 文件夹中运行它,然后将整个fastlane文件夹移动到项目的根目录。

在 Fastlane中fastlane/Fastfile,我正在 iOS 平台下创建一个 lane,用于将构建版本上传到 TestFlight。Fastlane 会自动加载我们在运行脚本时传递的 .env 文件。如果我们这样做:



$ fastlane ios beta --env=production


Enter fullscreen mode Exit fullscreen mode

它会加载.env.production文件。一切就绪。为了测试这一点,我将添加几行代码,只打印出环境变量和已加载的文件。



// fastlane/Fastfile

platform :ios do
  desc "Submit a new build to TestFlight"
  lane :beta do
    app_identifier = "com.app.identifier"

    api_environment = "staging"
    if ENV["IS_PRODUCTION"] == "true"
      api_environment = "production"
    end

    ENV["ENVFILE"]=".env.#{api_environment}"

    puts "API_HOST: #{ENV['API_HOST']}"
    puts "IS_PRODUCTION: #{ENV['IS_PRODUCTION']}"
    puts "ENVFILE: #{ENV['ENVFILE']}"
    end
  end



Enter fullscreen mode Exit fullscreen mode

如果一切正常,我们应该看到打印出来的结果:



[18:16:52]: Loading from './/../.env.production'
[18:16:52]: Driving the lane 'ios beta' 🚀
[18:16:52]: API_HOST: https://api.foobar.com
[18:16:52]: IS_PRODUCTION: true
[18:16:52]: ENVFILE: .env.production


Enter fullscreen mode Exit fullscreen mode

在创建新版本之前,我们可能需要增加版本号和/或版本号。请将以下几行添加到文件中:



increment_version_number(
    xcodeproj: './ios/FooBar.xcodeproj',
    bump_type: "patch",
    # bump_type: "minor",
    # bump_type: "major",
    # version_number: "1.0.0"
)
increment_build_number(
  xcodeproj: './ios/FooBar.xcodeproj',
  # build_number: '74'
)


Enter fullscreen mode Exit fullscreen mode

现在,要创建一个构建版本,请添加以下几行代码。请记住,我在文章开头提到过,方案的命名后面会很重要。原因如下:



gym(
    xcodeproj: './ios/FooBar.xcodeproj',
    scheme: "FooBar.#{api_environment}"
)



Enter fullscreen mode Exit fullscreen mode

最后,我希望我的脚本也能将构建结果上传到指定位置TestFlight,所以我这样做:



pilot(
    app_identifier: app_identifier,
    email: "itunesconnect_email",
    first_name: "itunesconnect_first_name",
    last_name: "itunesconnect_last_name",
    ipa: "./FooBar.ipa",
    distribute_external: true,
    skip_submission: true,
    skip_waiting_for_build_processing: false
)


Enter fullscreen mode Exit fullscreen mode

Android 的流程几乎相同,只是将 ` gymand`pilot操作替换为gradle`and` supply。流程大致如下:



platform :android do
  desc "Submit a new build to Google Play Console"

  lane :beta do
    app_identifier = "com.app.identifier"

    api_environment = "staging"
    if ENV["IS_PRODUCTION"] == "true"
      api_environment = "production"
    end
    ENV["ENVFILE"]=".env.#{api_environment}"

    puts "API_HOST: #{ENV['API_HOST']}"
    puts "IS_PRODUCTION: #{ENV['IS_PRODUCTION']}"
    puts "ENVFILE: #{ENV['ENVFILE']}"

    gradle_file = "./android/app/build.gradle"

    android_set_version_name(
      version_name: "1.0.0",
      gradle_file: gradle_file
    )

    android_set_version_code(
      gradle_file: gradle_file
    )

    gradle(
      project_dir: './android',
      task: 'assemble',
      build_type: 'release'
    )

    supply(
      json_key: 'google_play_console_key',
      track: 'beta',
      apk: './android/app/build/outputs/apk/release/app-release.apk',
      package_name: app_identifier
    )
  end
end


Enter fullscreen mode Exit fullscreen mode

关于 React Native 应用中的预发布环境和生产环境的最后思考

在离开之前,我想表达一下我对源代码控制最佳实践的看法。

我建议将真实值提交到.env(本地环境),但保留.env.staging虚拟.env.production/非敏感值,这些值仅在构建时在构建机器上替换。

因此,这三个文件在代码仓库中看起来会像这样:



// .env
IS_PRODUCTION=false
API_HOST=https://api.staging.foobar.com

// .env.staging
IS_PRODUCTION=false
API_HOST=api_host

// .env.production
IS_PRODUCTION=true
API_HOST=api_host


Enter fullscreen mode Exit fullscreen mode

完整的源代码可以在Github上找到

欢迎您告诉我您的想法,或者如果您对我上面提出的解决方案有任何建议,也请随时告诉我。

谢谢,

文章来源:https://dev.to/calintamas/how-to-manage-staging-and-production-environments-in-a-react-native-app-4naa