@ahmedsamirelsaka/react-native-local-release 2.0.0-rc.1
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.
- package/CHANGELOG.md +33 -0
- package/CREDENTIALS.md +79 -0
- package/LICENSE +21 -0
- package/README.md +112 -0
- package/SECURITY.md +44 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +326 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +27 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +229 -0
- package/dist/config.js.map +1 -0
- package/dist/credentials.d.ts +27 -0
- package/dist/credentials.d.ts.map +1 -0
- package/dist/credentials.js +317 -0
- package/dist/credentials.js.map +1 -0
- package/dist/detect.d.ts +7 -0
- package/dist/detect.d.ts.map +1 -0
- package/dist/detect.js +326 -0
- package/dist/detect.js.map +1 -0
- package/dist/fs.d.ts +15 -0
- package/dist/fs.d.ts.map +1 -0
- package/dist/fs.js +79 -0
- package/dist/fs.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/install.d.ts +5 -0
- package/dist/install.d.ts.map +1 -0
- package/dist/install.js +338 -0
- package/dist/install.js.map +1 -0
- package/dist/logging.d.ts +15 -0
- package/dist/logging.d.ts.map +1 -0
- package/dist/logging.js +84 -0
- package/dist/logging.js.map +1 -0
- package/dist/patch.d.ts +39 -0
- package/dist/patch.d.ts.map +1 -0
- package/dist/patch.js +352 -0
- package/dist/patch.js.map +1 -0
- package/dist/paths.d.ts +14 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +65 -0
- package/dist/paths.js.map +1 -0
- package/dist/process.d.ts +26 -0
- package/dist/process.d.ts.map +1 -0
- package/dist/process.js +43 -0
- package/dist/process.js.map +1 -0
- package/dist/prompt.d.ts +9 -0
- package/dist/prompt.d.ts.map +1 -0
- package/dist/prompt.js +70 -0
- package/dist/prompt.js.map +1 -0
- package/dist/types.d.ts +153 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/docs/ARCHITECTURE.md +25 -0
- package/docs/MIGRATION.md +28 -0
- package/docs/PUBLISHING.md +128 -0
- package/docs/SUPPORT.md +17 -0
- package/install.mjs +6 -0
- package/package.json +73 -0
- package/schema/release.config.schema.json +209 -0
- package/template/android/keystore.properties.example +7 -0
- package/template/android/proguard-rules.rn-local-release.pro +28 -0
- package/template/docs/LOCAL_RELEASE.md +85 -0
- package/template/fastlane/.env.example +20 -0
- package/template/fastlane/Appfile +10 -0
- package/template/fastlane/Fastfile +203 -0
- package/template/fastlane/lib/rn_local_release.rb +700 -0
- package/template/ios/exportOptions.plist +18 -0
- package/template/scripts/print-android-signing-fingerprints.sh +62 -0
- package/template/secrets/.gitignore +3 -0
- package/template/secrets/README.md +12 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Base React Native R8 keep rules (conservative).
|
|
2
|
+
# Do not replace project-specific SDK keeps; append project rules after this file.
|
|
3
|
+
|
|
4
|
+
-keepattributes Signature
|
|
5
|
+
-keepattributes *Annotation*
|
|
6
|
+
-keepattributes InnerClasses
|
|
7
|
+
-keepattributes EnclosingMethod
|
|
8
|
+
-keepattributes SourceFile,LineNumberTable
|
|
9
|
+
-keepattributes Exceptions
|
|
10
|
+
|
|
11
|
+
-keep class com.facebook.react.** { *; }
|
|
12
|
+
-keep class com.facebook.hermes.** { *; }
|
|
13
|
+
-keep class com.facebook.jni.** { *; }
|
|
14
|
+
-keep class com.facebook.soloader.** { *; }
|
|
15
|
+
-keep class com.facebook.yoga.** { *; }
|
|
16
|
+
-keep class * extends com.facebook.react.bridge.NativeModule { *; }
|
|
17
|
+
-keep class * extends com.facebook.react.bridge.JavaScriptModule { *; }
|
|
18
|
+
-keepclassmembers class * {
|
|
19
|
+
@com.facebook.react.uimanager.annotations.ReactProp <methods>;
|
|
20
|
+
}
|
|
21
|
+
-keepclassmembers class * {
|
|
22
|
+
@com.facebook.react.uimanager.annotations.ReactPropGroup <methods>;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
-keep class com.facebook.react.turbomodule.** { *; }
|
|
26
|
+
-keep class com.facebook.react.fabric.** { *; }
|
|
27
|
+
|
|
28
|
+
-dontwarn com.facebook.**
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Local Fastlane release (laptop CI/CD)
|
|
2
|
+
|
|
3
|
+
This project uses **[@ahmedsamirelsaka/react-native-local-release](https://www.npmjs.com/package/@ahmedsamirelsaka/react-native-local-release)** v2:
|
|
4
|
+
a security-first local Fastlane kit for bare React Native iOS + Android.
|
|
5
|
+
|
|
6
|
+
## Split of concerns
|
|
7
|
+
|
|
8
|
+
| Kind | Where | Examples |
|
|
9
|
+
|------|-------|----------|
|
|
10
|
+
| Tracked metadata | `fastlane/release.config.json` | package name, team ID, scheme, tracks, version sources, logging, archives |
|
|
11
|
+
| Managed install state | `.rn-local-release/manifest.json` | hashes of managed files (non-secret) |
|
|
12
|
+
| Local credentials | Keychain/vault (default macOS) or `fastlane/.env` + `secrets/` | ASC `.p8`, Play JSON, keystore passwords |
|
|
13
|
+
| Crash logs | `logs/` (gitignored, redacted) | installer / Fastlane failures |
|
|
14
|
+
| Optional upload archive | `versions/` (gitignored, opt-in) | copies of uploaded IPA/AAB |
|
|
15
|
+
|
|
16
|
+
Never paste private keys, passwords, or JSON credentials into Git, chat, docs, or screenshots.
|
|
17
|
+
|
|
18
|
+
How to create credentials: see [RELEASE_CREDENTIALS.md](./RELEASE_CREDENTIALS.md).
|
|
19
|
+
|
|
20
|
+
## Install / reinstall
|
|
21
|
+
|
|
22
|
+
Prefer an **exact npm version** (do not run floating `github:` HEAD in daily workflows):
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npx @ahmedsamirelsaka/react-native-local-release@2.0.0 install --project .
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Useful flags:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
--dry-run
|
|
32
|
+
--team-id YOURTEAMID
|
|
33
|
+
--package-name com.example.app
|
|
34
|
+
--ios-scheme YourScheme
|
|
35
|
+
--fixed-ios-build 1
|
|
36
|
+
--store-uploaded-versions / --no-store-uploaded-versions
|
|
37
|
+
--setup-credentials / --skip-credentials
|
|
38
|
+
--credentials-only
|
|
39
|
+
--non-interactive
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Interactive install asks:
|
|
43
|
+
|
|
44
|
+
1. Configure Apple / Play / keystore now?
|
|
45
|
+
2. Store uploaded IPA/AAB copies under `versions/`?
|
|
46
|
+
|
|
47
|
+
Reinstall merges existing `release.config.json` instead of wiping custom tracks/gates.
|
|
48
|
+
|
|
49
|
+
## Prerequisites
|
|
50
|
+
|
|
51
|
+
- macOS with Xcode (iOS)
|
|
52
|
+
- Android SDK / JDK for Gradle
|
|
53
|
+
- Node 18+, Yarn/npm/pnpm
|
|
54
|
+
- Ruby compatible with Fastlane (`bundle install`)
|
|
55
|
+
- ASC API key + Play service account + upload keystore
|
|
56
|
+
|
|
57
|
+
## Daily commands
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
bundle install
|
|
61
|
+
yarn release:doctor
|
|
62
|
+
yarn release:credentials
|
|
63
|
+
yarn release:compile # typecheck + IPA + AAB (no bump, no upload)
|
|
64
|
+
yarn release:upload # upload last compile
|
|
65
|
+
yarn release:beta # bump + compile + upload
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Promote to production requires confirmation:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
RN_LOCAL_RELEASE_YES=1 yarn release:ios:promote version:x.y.z build:n
|
|
72
|
+
RN_LOCAL_RELEASE_YES=1 yarn release:android:promote version_code:n
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Uninstall
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
npx @ahmedsamirelsaka/react-native-local-release@2.0.0 uninstall --project . --yes
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Troubleshooting
|
|
82
|
+
|
|
83
|
+
- Check redacted files under `logs/`
|
|
84
|
+
- Run `rn-local-release audit --project .` if credentials look tracked
|
|
85
|
+
- Partial dual-upload failures leave `build/upload-journal.json` describing which store succeeded
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Local Fastlane credentials — copy to fastlane/.env and fill in.
|
|
2
|
+
# Never commit real values. Paths may be absolute or relative to the repo root.
|
|
3
|
+
|
|
4
|
+
# --- Apple App Store Connect API key ---
|
|
5
|
+
# Create at: App Store Connect → Users and Access → Integrations → App Store Connect API
|
|
6
|
+
ASC_KEY_ID=
|
|
7
|
+
ASC_ISSUER_ID=
|
|
8
|
+
ASC_KEY_FILEPATH=secrets/AuthKey_XXXXXXXXXX.p8
|
|
9
|
+
# Optional Apple ID email (only needed for interactive Apple ID auth fallback)
|
|
10
|
+
# APPLE_ID=you@example.com
|
|
11
|
+
|
|
12
|
+
# --- Google Play service account ---
|
|
13
|
+
# JSON key downloaded after creating a Play Console service account
|
|
14
|
+
PLAY_JSON_KEY_PATH=secrets/play-store-service-account.json
|
|
15
|
+
|
|
16
|
+
# --- Android upload keystore ---
|
|
17
|
+
MYAPP_UPLOAD_STORE_FILE=keystore.jks
|
|
18
|
+
MYAPP_UPLOAD_KEY_ALIAS=key0
|
|
19
|
+
MYAPP_UPLOAD_STORE_PASSWORD=
|
|
20
|
+
MYAPP_UPLOAD_KEY_PASSWORD=
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Generated by react-native-local-release. Non-secret identifiers only.
|
|
2
|
+
# Credential paths come from environment / fastlane/.env — never hardcode secrets.
|
|
3
|
+
|
|
4
|
+
app_identifier("{{bundleIdentifier}}")
|
|
5
|
+
package_name("{{packageName}}")
|
|
6
|
+
team_id("{{teamId}}")
|
|
7
|
+
|
|
8
|
+
apple_id(ENV["APPLE_ID"]) if ENV["APPLE_ID"] && !ENV["APPLE_ID"].empty?
|
|
9
|
+
|
|
10
|
+
json_key_file(ENV["PLAY_JSON_KEY_PATH"]) if ENV["PLAY_JSON_KEY_PATH"] && !ENV["PLAY_JSON_KEY_PATH"].empty?
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
opt_out_usage
|
|
4
|
+
default_platform(:ios)
|
|
5
|
+
|
|
6
|
+
import("./lib/rn_local_release.rb")
|
|
7
|
+
|
|
8
|
+
desc "Validate config paths and print current versions (no secrets required)"
|
|
9
|
+
lane :doctor do
|
|
10
|
+
doctor!
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
desc "Validate Apple and Google upload credentials"
|
|
14
|
+
lane :credentials do
|
|
15
|
+
load_asc_api_key
|
|
16
|
+
UI.success("App Store Connect API key loaded successfully.")
|
|
17
|
+
validate_play_store_json_key(json_key: play_json_key_path)
|
|
18
|
+
UI.success("Play Store JSON key validated successfully.")
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
desc "Run shared JS verification"
|
|
22
|
+
lane :verify do
|
|
23
|
+
run_verify!
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
desc "Bump / set marketing version + Android versionCode (and iOS build per strategy)"
|
|
27
|
+
lane :bump_version do |options|
|
|
28
|
+
bumped = bump_versions!(options)
|
|
29
|
+
UI.message(
|
|
30
|
+
"Next store upload will use #{bumped[:version]} " \
|
|
31
|
+
"(Android #{bumped[:android_build]}, iOS #{bumped[:ios_build]})"
|
|
32
|
+
)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
desc "Alias of bump_version for prepare-before-build workflows"
|
|
36
|
+
lane :prepare do |options|
|
|
37
|
+
bumped = bump_versions!(options)
|
|
38
|
+
UI.message(
|
|
39
|
+
"Prepared #{bumped[:version]} " \
|
|
40
|
+
"(Android #{bumped[:android_build]}, iOS #{bumped[:ios_build]})"
|
|
41
|
+
)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
desc "Typecheck and compile iOS IPA + Android AAB without uploading"
|
|
45
|
+
lane :compile do
|
|
46
|
+
compile_all!
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
desc "Build iOS IPA and Android AAB without uploading"
|
|
50
|
+
lane :build_all do
|
|
51
|
+
compile_all!
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
desc "Upload previously compiled IPA + AAB using release-manifest.json"
|
|
55
|
+
lane :upload_all do |options|
|
|
56
|
+
upload_all_resumable!(options)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
desc "Bump (unless skipped), compile both platforms, then upload TestFlight + Play beta track"
|
|
60
|
+
lane :beta_all do |options|
|
|
61
|
+
unless skip_flag?(options, :skip_bump)
|
|
62
|
+
bumped = bump_versions!(options)
|
|
63
|
+
UI.important(
|
|
64
|
+
"Version is now #{bumped[:version]} " \
|
|
65
|
+
"(Android #{bumped[:android_build]}, iOS #{bumped[:ios_build]}). Compiling, then uploading."
|
|
66
|
+
)
|
|
67
|
+
else
|
|
68
|
+
UI.important(
|
|
69
|
+
"Using current version #{current_marketing_version} " \
|
|
70
|
+
"(Android #{current_android_build}, iOS #{current_ios_build}). " \
|
|
71
|
+
"Compiling, then uploading."
|
|
72
|
+
)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
compile_all!
|
|
76
|
+
upload_all_resumable!(options)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
platform :ios do
|
|
80
|
+
desc "Validate App Store Connect API credentials"
|
|
81
|
+
lane :credentials do
|
|
82
|
+
load_asc_api_key
|
|
83
|
+
UI.success("App Store Connect API key loaded successfully.")
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
desc "Run lint, TypeScript, and tests"
|
|
87
|
+
lane :verify do
|
|
88
|
+
run_verify!
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
desc "Build a signed App Store IPA (no upload)"
|
|
92
|
+
lane :build do
|
|
93
|
+
compile_js!
|
|
94
|
+
build_ios_ipa!
|
|
95
|
+
write_platform_manifest!(:ios)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
desc "Compile IPA then upload to TestFlight"
|
|
99
|
+
lane :beta do |options|
|
|
100
|
+
bump_versions!(options) unless skip_flag?(options, :skip_bump)
|
|
101
|
+
compile_js!
|
|
102
|
+
build_ios_ipa!
|
|
103
|
+
write_platform_manifest!(:ios)
|
|
104
|
+
assert_manifest_artifacts!(:ios)
|
|
105
|
+
upload_ios!(options)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
desc "Upload an already-compiled IPA from the release manifest"
|
|
109
|
+
lane :upload do |options|
|
|
110
|
+
assert_manifest_artifacts!(:ios)
|
|
111
|
+
upload_ios!(options)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
desc "Submit an already-uploaded build for App Store review"
|
|
115
|
+
lane :promote do |options|
|
|
116
|
+
ensure_clean_git!
|
|
117
|
+
load_asc_api_key
|
|
118
|
+
app_version = require_option!(options, :version)
|
|
119
|
+
build_number = require_option!(options, :build)
|
|
120
|
+
confirm_promote!(:ios, {
|
|
121
|
+
"app" => config.dig("app", "bundleIdentifier"),
|
|
122
|
+
"version" => app_version,
|
|
123
|
+
"build" => build_number,
|
|
124
|
+
"action" => "submit_for_review"
|
|
125
|
+
})
|
|
126
|
+
UI.user_error!("Set RN_LOCAL_RELEASE_YES=1 to confirm production promote") unless ENV["RN_LOCAL_RELEASE_YES"] == "1" || ENV["CI"] == "true"
|
|
127
|
+
|
|
128
|
+
upload_to_app_store(
|
|
129
|
+
app_version: app_version,
|
|
130
|
+
build_number: build_number.to_s,
|
|
131
|
+
submit_for_review: true,
|
|
132
|
+
automatic_release: false,
|
|
133
|
+
force: true,
|
|
134
|
+
skip_binary_upload: true,
|
|
135
|
+
skip_metadata: true,
|
|
136
|
+
skip_screenshots: true,
|
|
137
|
+
precheck_include_in_app_purchases: false
|
|
138
|
+
)
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
platform :android do
|
|
143
|
+
desc "Validate Google Play service-account credentials"
|
|
144
|
+
lane :credentials do
|
|
145
|
+
validate_play_store_json_key(json_key: play_json_key_path)
|
|
146
|
+
UI.success("Play Store JSON key validated successfully.")
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
desc "Run lint, TypeScript, and tests"
|
|
150
|
+
lane :verify do
|
|
151
|
+
run_verify!
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
desc "Build a signed Play App Bundle (no upload)"
|
|
155
|
+
lane :build do
|
|
156
|
+
compile_js!
|
|
157
|
+
build_android_aab!
|
|
158
|
+
write_platform_manifest!(:android)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
desc "Compile AAB then upload to the configured Play beta track"
|
|
162
|
+
lane :beta do |options|
|
|
163
|
+
bump_versions!(options) unless skip_flag?(options, :skip_bump)
|
|
164
|
+
compile_js!
|
|
165
|
+
build_android_aab!
|
|
166
|
+
write_platform_manifest!(:android)
|
|
167
|
+
assert_manifest_artifacts!(:android)
|
|
168
|
+
upload_android!
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
desc "Upload an already-compiled AAB from the release manifest"
|
|
172
|
+
lane :upload do
|
|
173
|
+
assert_manifest_artifacts!(:android)
|
|
174
|
+
upload_android!
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
desc "Promote a specific versionCode from beta track to production"
|
|
178
|
+
lane :promote do |options|
|
|
179
|
+
ensure_clean_git!
|
|
180
|
+
version_code = require_option!(options, :version_code)
|
|
181
|
+
confirm_promote!(:android, {
|
|
182
|
+
"package" => config.dig("app", "packageName"),
|
|
183
|
+
"versionCode" => version_code,
|
|
184
|
+
"fromTrack" => config.dig("android", "playTrackBeta"),
|
|
185
|
+
"toTrack" => config.dig("android", "playTrackPromote")
|
|
186
|
+
})
|
|
187
|
+
UI.user_error!("Set RN_LOCAL_RELEASE_YES=1 to confirm production promote") unless ENV["RN_LOCAL_RELEASE_YES"] == "1" || ENV["CI"] == "true"
|
|
188
|
+
|
|
189
|
+
upload_to_play_store(
|
|
190
|
+
package_name: config.dig("app", "packageName"),
|
|
191
|
+
track: config.dig("android", "playTrackBeta"),
|
|
192
|
+
track_promote_to: config.dig("android", "playTrackPromote"),
|
|
193
|
+
version_code: version_code.to_i,
|
|
194
|
+
json_key: play_json_key_path,
|
|
195
|
+
skip_upload_apk: true,
|
|
196
|
+
skip_upload_aab: true,
|
|
197
|
+
skip_upload_metadata: true,
|
|
198
|
+
skip_upload_images: true,
|
|
199
|
+
skip_upload_screenshots: true,
|
|
200
|
+
skip_upload_changelogs: true
|
|
201
|
+
)
|
|
202
|
+
end
|
|
203
|
+
end
|