fastlane-plugin-react_native_release 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +118 -45
- data/lib/fastlane/plugin/react_native_release/actions/accept_android_sdk_licenses.rb +46 -0
- data/lib/fastlane/plugin/react_native_release/actions/add_app_var.rb +109 -0
- data/lib/fastlane/plugin/react_native_release/actions/create_fastlane_session.rb +75 -0
- data/lib/fastlane/plugin/react_native_release/actions/decrypt_android_keystore.rb +131 -0
- data/lib/fastlane/plugin/react_native_release/actions/decrypt_app_vars.rb +106 -0
- data/lib/fastlane/plugin/react_native_release/actions/decrypt_fastlane_vars.rb +64 -0
- data/lib/fastlane/plugin/react_native_release/actions/decrypt_google_play_credentials.rb +62 -0
- data/lib/fastlane/plugin/react_native_release/actions/encrypt_app_vars.rb +110 -0
- data/lib/fastlane/plugin/react_native_release/actions/encrypt_fastlane_vars.rb +61 -0
- data/lib/fastlane/plugin/react_native_release/actions/encrypt_google_play_credentials.rb +64 -0
- data/lib/fastlane/plugin/react_native_release/actions/generate_android_keystore.rb +137 -0
- data/lib/fastlane/plugin/react_native_release/actions/{react_native_release_action.rb → react_native_release.rb} +33 -31
- data/lib/fastlane/plugin/react_native_release/actions/read_fastlane_session.rb +55 -0
- data/lib/fastlane/plugin/react_native_release/helper/react_native_release_helper.rb +14 -5
- data/lib/fastlane/plugin/react_native_release/version.rb +1 -1
- metadata +16 -6
- data/lib/fastlane/plugin/react_native_release/actions/create_fastlane_session_action.rb +0 -63
- data/lib/fastlane/plugin/react_native_release/actions/read_fastlane_session_action.rb +0 -67
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 85c358216aed69c8872ccca434dd6840958d56f9cd0d123d01df7aa623c5bbba
|
4
|
+
data.tar.gz: e76aeb88e60329c64251caf091e7806a570e985d7e915c8a96a88b1783cd4e9d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d752e0955a2fa7c73e2f8a38db76cd2e8da383e203ebe743c2ed0b0bf5716812018f9d8c13311d0832eb6d59b1ce36d8e0800c8eb318b34cdc0bf799cf8b9eef
|
7
|
+
data.tar.gz: 2bfc9c7a783887edcf8ad103ac6b17c535f2ca48ab1f7dec40698644a1f6d1c37687d99fb193b5e619a11ba90fb9dd61aa956398b718c62856dfa590cbba15cb
|
data/README.md
CHANGED
@@ -1,86 +1,159 @@
|
|
1
|
-
# React Native Release
|
1
|
+
# React Native Release
|
2
2
|
|
3
3
|
[](https://rubygems.org/gems/fastlane-plugin-react_native_release)
|
4
4
|
|
5
|
-
|
5
|
+
Simplify releases for your React Native apps. This plugin contains many actions to help you with your release workflow.
|
6
6
|
|
7
|
-
|
7
|
+
It helps cut a new `beta` or `release` version of your app:
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
- Authenticates and stores an encrypted App Store Connect session that is later used for TestFlight and App Store operations
|
10
|
+
- Prompts the user for a `major/minor/patch` version bump
|
11
|
+
- Bumps the version of the iOS and Android app appropriately
|
12
|
+
- Creates a tag
|
13
|
+
- Handles hotfix releases
|
14
|
+
- Handles merging version bumps and hotfixes back to the appropriate branches
|
15
|
+
|
16
|
+
It encrypts, and on CI, decrypts values from a standalone "context" git repository:
|
17
|
+
|
18
|
+
- Android keystore file (including helping you generate one)
|
19
|
+
- Google Play Credentials to upload to the Play Store
|
20
|
+
- Fastlane config ENV variables
|
21
|
+
- App ENV variables (API_URL, feature flags, etc)
|
22
|
+
|
23
|
+
### Philosophies
|
24
|
+
|
25
|
+
**Use Fastlane Match**
|
26
|
+
|
27
|
+
Certificates and provisioning profiles should be created and managed by Fastlane Match
|
28
|
+
|
29
|
+
**Builds Run on CI**
|
30
|
+
|
31
|
+
We support local builds for projects, but they should only be used in emergency situations.
|
32
|
+
|
33
|
+
**CI uses a machine user**
|
34
|
+
|
35
|
+
This is a best practice.
|
36
|
+
|
37
|
+
**ENV vars are managed through React Native Release**
|
38
|
+
|
39
|
+
We do this for portability and ease of configuration. Outside of the ENV vars to configure CRYPTEX, you shouldn't have to add ENV vars to CI.
|
40
|
+
|
41
|
+
For App ENV vars, we provide some additional functionality via namespaces. Valid namespaces are `alpha`, `beta`, `release`, and empty (root). Root ENV vars are "global". Namespaced variables are merged into the Root ENV vars at build time via the `decrypt_app_vars` action. This allows you to easily overwrite ENV vars for specific types of builds, all without configuring separate targets and schemes in XCode.
|
42
|
+
|
43
|
+
The main branch and tagging flow looks like this (note this may be out of date. Revisit after workflow updates):
|
44
|
+
|
45
|
+

|
46
|
+
|
47
|
+
If a hotfix is required the flow looks like this (note this may be out of date. Revisit after workflow updates):
|
48
|
+
|
49
|
+

|
50
|
+
|
51
|
+
Here's what it looks like in action:
|
52
|
+

|
53
|
+
(this example uses `"release": "bundle exec fastlane run react_native_release"` as a yarn script)e
|
54
|
+
|
55
|
+
## Prerequisites
|
12
56
|
|
13
57
|
### Ensure your project confirms to Semantic Versioning
|
58
|
+
|
14
59
|
Projects using React Native Release should use Semantic Versioning. At the very least, you need major, minor, and patch numbers in your version.
|
15
60
|
|
16
61
|
**iOS**
|
17
62
|
Use `agvtool` to get and set a version across your project. From the `ios` directory, do the following:
|
18
|
-
|
19
|
-
|
63
|
+
|
64
|
+
- `agvtool what-marketing-version` to see your current version
|
65
|
+
- `agvtool new-marketing-version 0.1.0` to set a new version
|
20
66
|
|
21
67
|
**Android**
|
22
|
-
|
68
|
+
|
69
|
+
- Set `versionName` in `app/build.gradle`. (`versionName "0.1.0"`)
|
23
70
|
|
24
71
|
:exclamation: If you don't complete these steps, releases will fail. :exclamation:
|
25
72
|
|
26
|
-
|
73
|
+
## Installation
|
74
|
+
|
75
|
+
This project is a [_fastlane_](https://github.com/fastlane/fastlane) plugin. Since React Native projects contain both `iOS` and `Android` apps, we need to install the plugin in multiple places:
|
27
76
|
|
28
|
-
|
77
|
+
### Install plugin into fastlane folders
|
29
78
|
|
30
|
-
|
79
|
+
1. In the root of your project. This is used to run release commands over the entire project.
|
31
80
|
|
32
81
|
```bash
|
33
|
-
|
82
|
+
fastlane add_plugin react_native_release
|
34
83
|
```
|
35
84
|
|
36
|
-
|
85
|
+
2. In `./android/fastlane`
|
37
86
|
|
38
|
-
```
|
39
|
-
|
40
|
-
setup_circle_ci
|
41
|
-
read_fastlane_session
|
42
|
-
end
|
87
|
+
```bash
|
88
|
+
fastlane add_plugin react_native_release
|
43
89
|
```
|
44
90
|
|
45
|
-
|
91
|
+
3. In `./ios/fastlane`
|
46
92
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
| FASTLANE_ENV_USERNAME | String | The Apple Developer Account email to authenticate with 2FA and generate a session token for. |
|
51
|
-
| CRYPTEX_PASSWORD | String | The secret key used to encrypt/decrypt the `FASTLANE_SESSION` value. |
|
93
|
+
```bash
|
94
|
+
fastlane add_plugin react_native_release
|
95
|
+
```
|
52
96
|
|
97
|
+
### Add or update .env files
|
53
98
|
|
54
|
-
|
99
|
+
We leverage `.env` files in a number of different places.
|
55
100
|
|
56
|
-
|
101
|
+
`<root>/fastlane/.env`:
|
57
102
|
|
58
|
-
|
103
|
+
| KEY | TYPE | DESCRIPTION |
|
104
|
+
| ----------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
105
|
+
| CRYPTEX_GIT_URL | String | The repository where the hashed session token will be stored. **Ensure this repository before running the release script!** (You can leverage the same repository you use for Fastlane Match) |
|
106
|
+
| CRYPTEX_PASSWORD | String | The secret key used to encrypt/decrypt the `FASTLANE_SESSION` value. |
|
107
|
+
| CRYPTEX_SKIP_DOCS | Boolean | Force the underlying encryption plugin to skip README generation. |
|
59
108
|
|
60
|
-
|
109
|
+
`<root>/android/fastlane/.env`:
|
61
110
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
111
|
+
| KEY | TYPE | DESCRIPTION |
|
112
|
+
| -------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
113
|
+
| ANDROID_KEY_PASSWORD | String | The password generated from `generate_android_keystore` or a manual keystore command.` |
|
114
|
+
| ANDROID_KEY_ALIAS | String | The alias for the keystore |
|
115
|
+
| CRYPTEX_GIT_URL | String | The repository where the hashed session token will be stored. **Ensure this repository before running the release script!** (You can leverage the same repository you use for Fastlane Match) |
|
116
|
+
| CRYPTEX_PASSWORD | String | The secret key used to encrypt/decrypt the `FASTLANE_SESSION` value. |
|
117
|
+
| CRYPTEX_SKIP_DOCS | Boolean | Force the underlying encryption plugin to skip README generation. |
|
68
118
|
|
69
|
-
|
70
|
-

|
119
|
+
`<root>/ios/fastlane/.env`:
|
71
120
|
|
72
|
-
|
73
|
-
|
121
|
+
| KEY | TYPE | DESCRIPTION |
|
122
|
+
| ----------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
123
|
+
| MATCH_GIT_URL | String | The repository used for Fastlane match. Typically the same as CRYPTEX_GIT_URL |
|
124
|
+
| MATCH_PASSWORD | String | The password used to encrypt / decrypt Fastlane Match certs and profiles |
|
125
|
+
| GYM_WORKSPACE | String | The name of the workspace file (Myproject.xcworkspace) |
|
126
|
+
| GYM_SCHEME | String | The scheme to build within the workspace (Myproject) |
|
127
|
+
| GYM_OUTPUT_NAME | String | The name of the `.ipa` file to output (Myproject) |
|
128
|
+
| CRYPTEX_GIT_URL | String | The repository where the hashed session token will be stored. **Ensure this repository before running the release script!** (You can leverage the same repository you use for Fastlane Match) |
|
129
|
+
| CRYPTEX_PASSWORD | String | The secret key used to encrypt/decrypt the `FASTLANE_SESSION` value. |
|
130
|
+
| CRYPTEX_SKIP_DOCS | Boolean | Force the underlying encryption plugin to skip README generation. |
|
74
131
|
|
75
|
-
|
76
|
-

|
77
|
-
(this example uses `"release": "bundle exec fastlane run react_native_release"` as a yarn script)
|
132
|
+
**Note: In followup releases, we will add a `react-native-release init` script to generate these for you.**
|
78
133
|
|
79
|
-
|
134
|
+
### Setup CI
|
135
|
+
|
136
|
+
Step 1. If you've already setup Fastlane Match, skip this section. Each provider is different, but conceptually you'll want to:
|
137
|
+
|
138
|
+
- Create a machine user account on your source code provider (GitHub or BitBucket) and invite that account to both the mobile and certs repo.
|
139
|
+
- Enable CI on the main mobile repository by pressing "Follow" or "Build"
|
140
|
+
- In an Incognito tab, log into your CI provider with the machine user account.
|
141
|
+
- Add a user key instead of the default deploy key. This will allow CI to auth as the machine user and gain access to _both_ the mobile repo and the context repo.
|
80
142
|
|
81
|
-
|
143
|
+
Step 2. Add the following to your CI environment variables:
|
144
|
+
|
145
|
+
- CRYPTEX_GIT_URL=(your context repo ssh git url)
|
146
|
+
- CRYPTEX_PASSWORD=(your context repo password)
|
147
|
+
- CRYPTEX_VERBOSE=true
|
148
|
+
- CRYPTEX_DIGEST=sha256 (note: if you have a pre-existing setup, you should set this to md5 - see https://github.com/hjanuschka/fastlane-plugin-cryptex/pull/10)
|
149
|
+
|
150
|
+
### Configuring builds to upload to TestFlight and AppStore Connect on CI
|
151
|
+
|
152
|
+
To upload builds to TestFlight or AppStore Connect, CI will need to restore a previously generated session. While possible to use an Application Specific Password to upload builds, it will not have the additional permissions required for other TestFlight / App Store operations. As such, we require generating a session.
|
153
|
+
|
154
|
+
## Example
|
82
155
|
|
83
|
-
|
156
|
+
See /example to see how to use the plugin. (TODO)
|
84
157
|
|
85
158
|
## Run tests for this plugin
|
86
159
|
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'fastlane/action'
|
2
|
+
require 'fastlane/plugin/cryptex'
|
3
|
+
|
4
|
+
module Fastlane
|
5
|
+
module Actions
|
6
|
+
class AcceptAndroidSdkLicensesAction < Action
|
7
|
+
def self.run(params)
|
8
|
+
sh("yes | $ANDROID_HOME/tools/bin/sdkmanager --licenses || if [ $? -ne '141' ]; then exit $?; fi;")
|
9
|
+
end
|
10
|
+
|
11
|
+
#####################################################
|
12
|
+
# @!group Documentation
|
13
|
+
#####################################################
|
14
|
+
|
15
|
+
def self.description
|
16
|
+
"Accepts Android sdk licenses"
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.details
|
20
|
+
# Optional:
|
21
|
+
# this is your chance to provide a more detailed description of this action
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.available_options
|
25
|
+
[]
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.return_value
|
29
|
+
# If your method provides a return value, you can describe here what it does
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.details
|
33
|
+
# "Saves the current vars in android/fastlane/.env and ios/fastlane/.env"
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.authors
|
37
|
+
# So no one will ever forget your contribution to fastlane :) You are awesome btw!
|
38
|
+
["cball", "isaiahgrey93"]
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.is_supported?(platform)
|
42
|
+
[:ios, :android].include?(platform)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'fastlane/action'
|
2
|
+
require 'fastlane/plugin/cryptex'
|
3
|
+
|
4
|
+
module Fastlane
|
5
|
+
module Actions
|
6
|
+
class AddAppVarAction < Action
|
7
|
+
def self.run(params)
|
8
|
+
is_ci = ENV['CI'] === 'true'
|
9
|
+
namespace = params[:namespace]
|
10
|
+
key = params[:key]
|
11
|
+
value = params[:value]
|
12
|
+
cryptex_app_key = app_key_for(namespace)
|
13
|
+
existing_app_vars = {}
|
14
|
+
|
15
|
+
if !is_ci && !UI.confirm("This will add #{key}=#{value} to the #{cryptex_app_key} namespace in the encrypted context repo. Proceed?")
|
16
|
+
UI.abort_with_message!("Stepping away...")
|
17
|
+
end
|
18
|
+
|
19
|
+
begin
|
20
|
+
existing_app_vars = other_action.cryptex(
|
21
|
+
type: 'export_env',
|
22
|
+
key: cryptex_app_key,
|
23
|
+
)
|
24
|
+
rescue => ex
|
25
|
+
# If key doesn't exist cryptex will error
|
26
|
+
end
|
27
|
+
|
28
|
+
other_action.cryptex(
|
29
|
+
type: "import_env",
|
30
|
+
key: cryptex_app_key,
|
31
|
+
hash: existing_app_vars.merge({ key => value })
|
32
|
+
)
|
33
|
+
|
34
|
+
UI.success('Encrypted app ENV vars')
|
35
|
+
end
|
36
|
+
|
37
|
+
#####################################################
|
38
|
+
# @!group Documentation
|
39
|
+
#####################################################
|
40
|
+
|
41
|
+
def self.description
|
42
|
+
"Adds a single ENV var to the encrypted repository"
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.details
|
46
|
+
# Optional:
|
47
|
+
# this is your chance to provide a more detailed description of this action
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.available_options
|
51
|
+
[
|
52
|
+
FastlaneCore::ConfigItem.new(key: :namespace,
|
53
|
+
env_name: "FL_ADD_APP_VAR_NAMESPACE",
|
54
|
+
description: "What namespace should we use? (alpha, beta, release, ENTER = root)", # a short description of this parameter
|
55
|
+
type: String,
|
56
|
+
verify_block: lambda do |value|
|
57
|
+
unless Helper::ReactNativeReleaseHelper::VALID_NAMESPACES.include?(value)
|
58
|
+
UI.user_error!("Invalid namespace #{value}. Valid targets are #{Helper::ReactNativeReleaseHelper::VALID_NAMESPACES.join(', ')}")
|
59
|
+
next
|
60
|
+
end
|
61
|
+
end),
|
62
|
+
FastlaneCore::ConfigItem.new(key: :key,
|
63
|
+
env_name: "FL_ADD_APP_VAR_KEY",
|
64
|
+
description: "Enter the ENV name",
|
65
|
+
type: String),
|
66
|
+
FastlaneCore::ConfigItem.new(key: :value,
|
67
|
+
env_name: "FL_ADD_APP_VAR_VALUE",
|
68
|
+
description: "Enter the ENV value",
|
69
|
+
type: String),
|
70
|
+
]
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.return_value
|
74
|
+
# If your method provides a return value, you can describe here what it does
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.details
|
78
|
+
# "Saves the current vars in android/fastlane/.env and ios/fastlane/.env"
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.authors
|
82
|
+
# So no one will ever forget your contribution to fastlane :) You are awesome btw!
|
83
|
+
["cball", "isaiahgrey93"]
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.is_supported?(platform)
|
87
|
+
[:ios, :android].include?(platform)
|
88
|
+
end
|
89
|
+
|
90
|
+
# Returns a path for an env var. optionally namespaced
|
91
|
+
def self.env_path_for(namespace)
|
92
|
+
return default_env_path if namespace.strip.empty?
|
93
|
+
"#{default_env_path}.#{namespace}"
|
94
|
+
end
|
95
|
+
|
96
|
+
# Returns the app key for cryptex. optionally namespaced
|
97
|
+
def self.app_key_for(namespace)
|
98
|
+
default_app_key = Helper::ReactNativeReleaseHelper::APP_CRYPTEX_KEY
|
99
|
+
return default_app_key if namespace.strip.empty?
|
100
|
+
|
101
|
+
"#{namespace}_#{default_app_key}"
|
102
|
+
end
|
103
|
+
|
104
|
+
def self.default_env_path
|
105
|
+
Helper::ReactNativeReleaseHelper::APP_ENV_PATH
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'fastlane/action'
|
2
|
+
require 'fastlane/plugin/cryptex'
|
3
|
+
|
4
|
+
module Fastlane
|
5
|
+
module Actions
|
6
|
+
class CreateFastlaneSessionAction < Action
|
7
|
+
def self.run(params)
|
8
|
+
username = params[:username]
|
9
|
+
key = Helper::ReactNativeReleaseHelper::FASTLANE_SESSION_CRYPTEX_KEY
|
10
|
+
fastlane_key = Helper::ReactNativeReleaseHelper::FASTLANE_CRYPTEX_KEY
|
11
|
+
fastlane_session_cookie_path = "#{File.expand_path('~')}/.fastlane/spaceship/#{username}/cookie"
|
12
|
+
|
13
|
+
UI.message "Generating a new fastlane session."
|
14
|
+
UI.message "Please enter the 6 digit 2FA code if one is sent to your device otherwise the script will continue automatically."
|
15
|
+
|
16
|
+
sh("fastlane spaceauth -u #{username.shellescape}")
|
17
|
+
|
18
|
+
# store the session
|
19
|
+
other_action.cryptex(
|
20
|
+
type: "import",
|
21
|
+
in: fastlane_session_cookie_path,
|
22
|
+
key: key
|
23
|
+
)
|
24
|
+
|
25
|
+
# store the username that created the session in fastlane_vars
|
26
|
+
existing_fastlane_vars = other_action.cryptex(
|
27
|
+
type: 'export_env',
|
28
|
+
key: fastlane_key,
|
29
|
+
)
|
30
|
+
|
31
|
+
other_action.cryptex(
|
32
|
+
type: "import_env",
|
33
|
+
key: fastlane_key,
|
34
|
+
# PILOT_USERNAME needs to be set to the same username as the session above
|
35
|
+
hash: existing_fastlane_vars.merge({ 'PILOT_USERNAME' => username })
|
36
|
+
)
|
37
|
+
|
38
|
+
UI.success "Uploaded session for #{username} to #{key}."
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.description
|
42
|
+
"Simplify 2FA authentication for App Store Connect"
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.authors
|
46
|
+
["cball", "isaiahgrey93"]
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.return_value
|
50
|
+
# If your method provides a return value, you can describe here what it does
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.details
|
54
|
+
"Creates a cookie for authenticating with App Store connecting. Handles generating, encrypting, and storing the cookie."
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.available_options
|
58
|
+
[
|
59
|
+
FastlaneCore::ConfigItem.new(key: :username,
|
60
|
+
env_name: "FL_CREATE_FASTLANE_SESSION_USERNAME",
|
61
|
+
description: "Enter the Apple username to generate a App Store Connect session",
|
62
|
+
type: String)
|
63
|
+
]
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.is_supported?(platform)
|
67
|
+
# Adjust this if your plugin only works for a particular platform (iOS vs. Android, for example)
|
68
|
+
# See: https://docs.fastlane.tools/advanced/#control-configuration-by-lane-and-by-platform
|
69
|
+
#
|
70
|
+
[:ios, :android].include?(platform)
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|