@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.
Files changed (75) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/CREDENTIALS.md +79 -0
  3. package/LICENSE +21 -0
  4. package/README.md +112 -0
  5. package/SECURITY.md +44 -0
  6. package/dist/cli.d.ts +3 -0
  7. package/dist/cli.d.ts.map +1 -0
  8. package/dist/cli.js +326 -0
  9. package/dist/cli.js.map +1 -0
  10. package/dist/config.d.ts +27 -0
  11. package/dist/config.d.ts.map +1 -0
  12. package/dist/config.js +229 -0
  13. package/dist/config.js.map +1 -0
  14. package/dist/credentials.d.ts +27 -0
  15. package/dist/credentials.d.ts.map +1 -0
  16. package/dist/credentials.js +317 -0
  17. package/dist/credentials.js.map +1 -0
  18. package/dist/detect.d.ts +7 -0
  19. package/dist/detect.d.ts.map +1 -0
  20. package/dist/detect.js +326 -0
  21. package/dist/detect.js.map +1 -0
  22. package/dist/fs.d.ts +15 -0
  23. package/dist/fs.d.ts.map +1 -0
  24. package/dist/fs.js +79 -0
  25. package/dist/fs.js.map +1 -0
  26. package/dist/index.d.ts +7 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +6 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/install.d.ts +5 -0
  31. package/dist/install.d.ts.map +1 -0
  32. package/dist/install.js +338 -0
  33. package/dist/install.js.map +1 -0
  34. package/dist/logging.d.ts +15 -0
  35. package/dist/logging.d.ts.map +1 -0
  36. package/dist/logging.js +84 -0
  37. package/dist/logging.js.map +1 -0
  38. package/dist/patch.d.ts +39 -0
  39. package/dist/patch.d.ts.map +1 -0
  40. package/dist/patch.js +352 -0
  41. package/dist/patch.js.map +1 -0
  42. package/dist/paths.d.ts +14 -0
  43. package/dist/paths.d.ts.map +1 -0
  44. package/dist/paths.js +65 -0
  45. package/dist/paths.js.map +1 -0
  46. package/dist/process.d.ts +26 -0
  47. package/dist/process.d.ts.map +1 -0
  48. package/dist/process.js +43 -0
  49. package/dist/process.js.map +1 -0
  50. package/dist/prompt.d.ts +9 -0
  51. package/dist/prompt.d.ts.map +1 -0
  52. package/dist/prompt.js +70 -0
  53. package/dist/prompt.js.map +1 -0
  54. package/dist/types.d.ts +153 -0
  55. package/dist/types.d.ts.map +1 -0
  56. package/dist/types.js +2 -0
  57. package/dist/types.js.map +1 -0
  58. package/docs/ARCHITECTURE.md +25 -0
  59. package/docs/MIGRATION.md +28 -0
  60. package/docs/PUBLISHING.md +128 -0
  61. package/docs/SUPPORT.md +17 -0
  62. package/install.mjs +6 -0
  63. package/package.json +73 -0
  64. package/schema/release.config.schema.json +209 -0
  65. package/template/android/keystore.properties.example +7 -0
  66. package/template/android/proguard-rules.rn-local-release.pro +28 -0
  67. package/template/docs/LOCAL_RELEASE.md +85 -0
  68. package/template/fastlane/.env.example +20 -0
  69. package/template/fastlane/Appfile +10 -0
  70. package/template/fastlane/Fastfile +203 -0
  71. package/template/fastlane/lib/rn_local_release.rb +700 -0
  72. package/template/ios/exportOptions.plist +18 -0
  73. package/template/scripts/print-android-signing-fingerprints.sh +62 -0
  74. package/template/secrets/.gitignore +3 -0
  75. package/template/secrets/README.md +12 -0
@@ -0,0 +1,153 @@
1
+ export type PackageManager = 'yarn' | 'npm' | 'pnpm';
2
+ export type IosBuildNumberStrategy = {
3
+ mode: 'increment';
4
+ } | {
5
+ mode: 'fixed';
6
+ value: number;
7
+ };
8
+ export type VersionSourceKind = 'packageJson' | 'androidGradle' | 'androidGradleKts' | 'iosPbxproj' | 'stringLiteral';
9
+ export type CredentialProviderKind = 'keychain' | 'file' | 'env';
10
+ export type ReleaseConfig = {
11
+ schemaVersion: number;
12
+ app: {
13
+ displayName: string;
14
+ packageName: string;
15
+ bundleIdentifier: string;
16
+ };
17
+ ios: {
18
+ enabled: boolean;
19
+ workspace: string;
20
+ project: string;
21
+ scheme: string;
22
+ teamId: string;
23
+ exportMethod: 'app-store' | 'ad-hoc' | 'enterprise' | 'development';
24
+ buildNumberStrategy: IosBuildNumberStrategy;
25
+ outputIpaName: string;
26
+ exportOptionsPath: string;
27
+ };
28
+ android: {
29
+ enabled: boolean;
30
+ module: string;
31
+ buildGradlePath: string;
32
+ dsl: 'groovy' | 'kotlin';
33
+ playTrackBeta: string;
34
+ playTrackPromote: string;
35
+ aabPath: string;
36
+ releaseStatus: string;
37
+ };
38
+ version: {
39
+ sources: Array<{
40
+ path: string;
41
+ kind: VersionSourceKind;
42
+ pattern?: string;
43
+ targets?: string[];
44
+ }>;
45
+ defaultBump: 'patch' | 'minor' | 'major';
46
+ extraFiles: Array<{
47
+ path: string;
48
+ kind: 'stringLiteral';
49
+ pattern?: string;
50
+ }>;
51
+ };
52
+ verify: {
53
+ install: string;
54
+ commands: string[];
55
+ };
56
+ build: {
57
+ iosOutputDir: string;
58
+ typecheckCommand: string;
59
+ maxAabSizeMb: number | null;
60
+ maxIpaSizeMb: number | null;
61
+ };
62
+ gates: {
63
+ requireCleanGitForPromote: boolean;
64
+ requireCompileBeforeUpload: boolean;
65
+ };
66
+ credentials: {
67
+ provider: CredentialProviderKind;
68
+ };
69
+ logging: {
70
+ enabled: boolean;
71
+ directory: string;
72
+ };
73
+ archives: {
74
+ storeUploadedVersions: boolean;
75
+ directory: string;
76
+ };
77
+ ruby: {
78
+ version: string;
79
+ };
80
+ };
81
+ export type InstallManifest = {
82
+ kitVersion: string;
83
+ installedAt: string;
84
+ projectRoot: string;
85
+ managedPaths: Array<{
86
+ path: string;
87
+ kind: 'created' | 'patched' | 'copied';
88
+ originalHash: string | null;
89
+ installedHash: string;
90
+ }>;
91
+ configHash: string;
92
+ };
93
+ export type CliOptions = {
94
+ command: string;
95
+ project: string;
96
+ dryRun: boolean;
97
+ nonInteractive: boolean;
98
+ force: boolean;
99
+ yes: boolean;
100
+ setupCredentials: boolean | null;
101
+ credentialsOnly: boolean;
102
+ purgeCredentials: boolean;
103
+ teamId?: string;
104
+ packageName?: string;
105
+ bundleIdentifier?: string;
106
+ displayName?: string;
107
+ iosWorkspace?: string;
108
+ iosScheme?: string;
109
+ fixedIosBuild?: string;
110
+ storeUploadedVersions?: boolean | null;
111
+ help: boolean;
112
+ version: boolean;
113
+ };
114
+ export type DetectionResult = {
115
+ root: string;
116
+ packageManager: PackageManager;
117
+ packageJsonVersion: string;
118
+ displayName: string;
119
+ installCommand: string;
120
+ runPrefix: string;
121
+ android: {
122
+ supported: boolean;
123
+ reason?: string;
124
+ dsl?: 'groovy' | 'kotlin';
125
+ buildGradlePath?: string;
126
+ applicationId?: string | null;
127
+ versionCode?: number | null;
128
+ versionName?: string | null;
129
+ aabPath?: string;
130
+ };
131
+ ios: {
132
+ supported: boolean;
133
+ reason?: string;
134
+ ambiguous?: boolean;
135
+ workspace?: string | null;
136
+ project?: string | null;
137
+ scheme?: string | null;
138
+ bundleIdentifier?: string | null;
139
+ teamId?: string | null;
140
+ marketingVersion?: string | null;
141
+ currentProjectVersion?: number | null;
142
+ exportOptionsPath?: string;
143
+ workspaces?: string[];
144
+ projects?: string[];
145
+ };
146
+ versionExtra: {
147
+ path: string;
148
+ kind: 'stringLiteral';
149
+ pattern: string;
150
+ } | null;
151
+ workspaceRoot: string | null;
152
+ };
153
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,25 @@
1
+ # Architecture (v2)
2
+
3
+ ```
4
+ src/
5
+ cli.ts command router + argv parsing
6
+ install.ts transactional install / uninstall
7
+ detect.ts Android/iOS/package-manager detectors
8
+ config.ts schema validate + migrate + merge
9
+ credentials.ts Keychain/vault/file providers
10
+ patch.ts marker-owned file adapters
11
+ process.ts argv-only spawn (no shell)
12
+ logging.ts redacted logs/ under project
13
+ paths.ts containment + ID validation
14
+ fs.ts secure modes 0600/0700
15
+ types.ts shared contracts
16
+
17
+ template/fastlane/
18
+ Fastfile
19
+ lib/rn_local_release.rb versioning, manifests, upload journal, archives
20
+ schema/release.config.schema.json
21
+ ```
22
+
23
+ Dependency direction: CLI → install/detect/config/credentials → fs/paths/process/logging.
24
+
25
+ Unsupported layouts fail before writes.
@@ -0,0 +1,28 @@
1
+ # Migrating from v1 to v2
2
+
3
+ 1. Install the new CLI:
4
+ ```bash
5
+ npm i -D @ahmedsamirelsaka/react-native-local-release@2.0.0
6
+ npx rn-local-release migrate --project .
7
+ ```
8
+ 2. Re-run install to refresh Fastlane templates (config is merged, not wiped):
9
+ ```bash
10
+ npx rn-local-release install --project . --skip-credentials
11
+ ```
12
+ 3. Move secrets to Keychain/vault (macOS):
13
+ ```bash
14
+ npx rn-local-release configure --project .
15
+ ```
16
+ 4. Confirm gitignore includes `logs/`, `versions/`, and the `# >>> rn-local-release` block.
17
+ 5. Optionally enable upload archives:
18
+ ```bash
19
+ # set archives.storeUploadedVersions=true in release.config.json
20
+ # or reinstall with --store-uploaded-versions
21
+ ```
22
+
23
+ ## Behavior changes to expect
24
+
25
+ - Shell-style verify commands with `;` / `|` / `$()` are rejected.
26
+ - Promote lanes require `RN_LOCAL_RELEASE_YES=1`.
27
+ - Install no longer changes ABI list or R8 fullMode.
28
+ - Kotlin DSL projects are supported via `android.dsl=kotlin`.
@@ -0,0 +1,128 @@
1
+ # Publishing to npm (`@ahmedsamirelsaka/react-native-local-release`)
2
+
3
+ CI and GitHub Releases already work. npm publish fails with **`ENEEDAUTH`** until **you** add registry credentials. This cannot be done by the CI bot alone.
4
+
5
+ Choose **Path A** (fastest) or **Path B** (OIDC, no long-lived token).
6
+
7
+ ---
8
+
9
+ ## Path A — Automation token (recommended for first publish)
10
+
11
+ ### 1. Create / sign in to npm
12
+
13
+ 1. Open https://www.npmjs.com/signup (or https://www.npmjs.com/login).
14
+ 2. Prefer username **`ahmedsamirelsaka`** so the scope `@ahmedsamirelsaka` matches the package name.
15
+ If you already use another username, either:
16
+ - change `package.json` `"name"` to `@YOUR_NPM_USER/react-native-local-release`, or
17
+ - create an npm **Organization** named `ahmedsamirelsaka` and add yourself as owner.
18
+
19
+ ### 2. Enable 2FA (required for publishing)
20
+
21
+ npm → **Account** → **Access Tokens** / Security → enable 2FA (auth-and-writes or auth-only + granular tokens).
22
+
23
+ ### 3. Create an Automation token
24
+
25
+ 1. https://www.npmjs.com/settings/~/tokens
26
+ (or Organization → Access Tokens if publishing under an org)
27
+ 2. **Generate New Token** → **Granular Access Token** (preferred) or classic **Automation**.
28
+ 3. Settings that work:
29
+ - **Type:** Automation (classic) **or** Granular with permission **Read and write** on packages
30
+ - **Packages:** allow `@ahmedsamirelsaka/react-native-local-release` (or entire `@ahmedsamirelsaka` scope)
31
+ - **Organizations:** your org if applicable
32
+ 4. Copy the token once (`npm_…`). Store it in a password manager.
33
+
34
+ ### 4. Add the token to GitHub
35
+
36
+ **Option 1 — Environment secret (matches the workflow):**
37
+
38
+ 1. https://github.com/AhmedSamirElsaka/react-native-local-release/settings/environments
39
+ 2. Open environment **`npm-publish`** (already created).
40
+ 3. **Environment secrets** → **Add secret**
41
+ - Name: `NPM_TOKEN`
42
+ - Value: paste the npm token
43
+
44
+ **Option 2 — Repository secret:**
45
+
46
+ 1. https://github.com/AhmedSamirElsaka/react-native-local-release/settings/secrets/actions
47
+ 2. **New repository secret** → `NPM_TOKEN` → paste token
48
+
49
+ > The workflow reads `${{ secrets.NPM_TOKEN }}`. Environment secrets are visible only to jobs that use `environment: npm-publish` (this one does).
50
+
51
+ ### 5. Re-run publish
52
+
53
+ Either:
54
+
55
+ - Actions → failed **Publish npm + GitHub Release assets** run → **Re-run failed jobs**
56
+
57
+ Or from a terminal:
58
+
59
+ ```bash
60
+ cd /Users/ahmedsamir/work/react-native-local-release
61
+ git push origin :refs/tags/v2.0.0-rc.1
62
+ git tag -f -a v2.0.0-rc.1 -m "2.0.0-rc.1"
63
+ git push origin v2.0.0-rc.1
64
+ ```
65
+
66
+ ### 6. Verify
67
+
68
+ ```bash
69
+ npm view @ahmedsamirelsaka/react-native-local-release version --tag next
70
+ npx @ahmedsamirelsaka/react-native-local-release@next --help
71
+ ```
72
+
73
+ RC is published under dist-tag **`next`** (not `latest`) until you cut a stable `2.0.0`.
74
+
75
+ ---
76
+
77
+ ## Path B — Trusted Publishing (OIDC, no `NPM_TOKEN`)
78
+
79
+ Best after the package exists once, or if npm allows first-time trusted publish for your account.
80
+
81
+ 1. Publish **once** with Path A (creates the package), **or** follow npm’s current “Trusted Publisher” UI for new packages.
82
+ 2. Open the package on npm → **Settings** → **Trusted Publisher** / **Provenance**:
83
+ - **Repository:** `AhmedSamirElsaka/react-native-local-release`
84
+ - **Workflow:** `publish.yml` (file under `.github/workflows/publish.yml`)
85
+ - **Environment:** `npm-publish`
86
+ 3. Remove `NPM_TOKEN` later if you want OIDC-only.
87
+ 4. Re-run the tag workflow. Job already has `permissions.id-token: write`.
88
+
89
+ Docs: https://docs.npmjs.com/trusted-publishers
90
+
91
+ ---
92
+
93
+ ## Optional: publish once from your laptop
94
+
95
+ ```bash
96
+ cd /Users/ahmedsamir/work/react-native-local-release
97
+ npm login # or: npm login --auth-type=web
98
+ npm whoami
99
+ npm test
100
+ npm publish --access public --tag next --provenance
101
+ ```
102
+
103
+ Use the same version already in `package.json` (`2.0.0-rc.1`). Do **not** re-publish the same version if CI already succeeded.
104
+
105
+ ---
106
+
107
+ ## Checklist
108
+
109
+ | Step | Status |
110
+ |------|--------|
111
+ | CI green on `main` | Done by repo |
112
+ | GitHub Release `v2.0.0-rc.1` + tarball | Done |
113
+ | npm account + `@ahmedsamirelsaka` scope | **You** |
114
+ | `NPM_TOKEN` on `npm-publish` env (or Trusted Publisher) | **You** |
115
+ | Re-run publish workflow | **You** |
116
+ | `npm view …@next` works | Confirm |
117
+
118
+ ---
119
+
120
+ ## After it works
121
+
122
+ Consumers:
123
+
124
+ ```bash
125
+ npx @ahmedsamirelsaka/react-native-local-release@next install --project .
126
+ # or pin exactly:
127
+ npm i -D @ahmedsamirelsaka/react-native-local-release@2.0.0-rc.1
128
+ ```
@@ -0,0 +1,17 @@
1
+ # Support policy
2
+
3
+ | Component | Support window |
4
+ |-----------|----------------|
5
+ | Toolkit 2.x | Current major |
6
+ | Toolkit 1.x | Security fixes until 2026-12-31 |
7
+ | React Native | Versions listed in CI fixtures (0.8x bare) |
8
+ | Fastlane | Compatible with `~> 2.234` |
9
+ | Node | 18, 20, 22 |
10
+
11
+ Unsupported (won't be fixed as product bugs):
12
+
13
+ - Expo managed / EAS
14
+ - Arbitrary custom native layouts without standard `android/app/build.gradle(.kts)` + `ios/*.xcworkspace`
15
+ - Remote CI orchestration (this toolkit is laptop-local by design)
16
+
17
+ When React Native, Xcode, Gradle, or Fastlane break detection, open an issue with fixture repro. Deprecation notices land in CHANGELOG before major bumps.
package/install.mjs ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * v1 entrypoint compatibility shim.
4
+ * Prefer the published `rn-local-release` binary (dist/cli.js).
5
+ */
6
+ import './dist/cli.js';
package/package.json ADDED
@@ -0,0 +1,73 @@
1
+ {
2
+ "name": "@ahmedsamirelsaka/react-native-local-release",
3
+ "version": "2.0.0-rc.1",
4
+ "description": "Security-first local Fastlane release toolkit for React Native iOS and Android",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": {
8
+ "name": "Ahmed Samir Elsaka",
9
+ "url": "https://github.com/AhmedSamirElsaka"
10
+ },
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "git+https://github.com/AhmedSamirElsaka/react-native-local-release.git"
14
+ },
15
+ "homepage": "https://github.com/AhmedSamirElsaka/react-native-local-release#readme",
16
+ "bugs": {
17
+ "url": "https://github.com/AhmedSamirElsaka/react-native-local-release/issues"
18
+ },
19
+ "keywords": [
20
+ "react-native",
21
+ "fastlane",
22
+ "ios",
23
+ "android",
24
+ "testflight",
25
+ "google-play",
26
+ "release-automation",
27
+ "cli",
28
+ "security"
29
+ ],
30
+ "files": [
31
+ "dist/",
32
+ "install.mjs",
33
+ "schema/",
34
+ "template/",
35
+ "docs/",
36
+ "README.md",
37
+ "CREDENTIALS.md",
38
+ "SECURITY.md",
39
+ "CHANGELOG.md",
40
+ "LICENSE"
41
+ ],
42
+ "bin": {
43
+ "rn-local-release": "./dist/cli.js"
44
+ },
45
+ "main": "./dist/index.js",
46
+ "types": "./dist/index.d.ts",
47
+ "exports": {
48
+ ".": {
49
+ "types": "./dist/index.d.ts",
50
+ "import": "./dist/index.js"
51
+ }
52
+ },
53
+ "scripts": {
54
+ "build": "tsc -p tsconfig.json",
55
+ "prepare": "npm run build",
56
+ "typecheck": "tsc -p tsconfig.json --noEmit",
57
+ "test": "npm run build && node --test test/*.test.mjs",
58
+ "pack:check": "npm pack --dry-run",
59
+ "lint": "tsc -p tsconfig.json --noEmit"
60
+ },
61
+ "engines": {
62
+ "node": ">=18"
63
+ },
64
+ "publishConfig": {
65
+ "access": "public",
66
+ "registry": "https://registry.npmjs.org",
67
+ "provenance": true
68
+ },
69
+ "devDependencies": {
70
+ "@types/node": "^22.15.29",
71
+ "typescript": "^5.8.3"
72
+ }
73
+ }
@@ -0,0 +1,209 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://github.com/AhmedSamirElsaka/react-native-local-release/blob/main/schema/release.config.schema.json",
4
+ "title": "React Native Local Release Config",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": [
8
+ "schemaVersion",
9
+ "app",
10
+ "ios",
11
+ "android",
12
+ "version",
13
+ "verify",
14
+ "build",
15
+ "gates",
16
+ "credentials",
17
+ "logging",
18
+ "archives"
19
+ ],
20
+ "properties": {
21
+ "schemaVersion": { "type": "integer", "minimum": 2 },
22
+ "app": {
23
+ "type": "object",
24
+ "additionalProperties": false,
25
+ "required": ["displayName", "packageName", "bundleIdentifier"],
26
+ "properties": {
27
+ "displayName": { "type": "string", "minLength": 1 },
28
+ "packageName": { "type": "string", "minLength": 1 },
29
+ "bundleIdentifier": { "type": "string", "minLength": 1 }
30
+ }
31
+ },
32
+ "ios": {
33
+ "type": "object",
34
+ "additionalProperties": false,
35
+ "required": [
36
+ "enabled",
37
+ "workspace",
38
+ "project",
39
+ "scheme",
40
+ "teamId",
41
+ "exportMethod",
42
+ "buildNumberStrategy",
43
+ "outputIpaName",
44
+ "exportOptionsPath"
45
+ ],
46
+ "properties": {
47
+ "enabled": { "type": "boolean" },
48
+ "workspace": { "type": "string" },
49
+ "project": { "type": "string" },
50
+ "scheme": { "type": "string" },
51
+ "teamId": { "type": "string", "minLength": 1 },
52
+ "exportMethod": {
53
+ "type": "string",
54
+ "enum": ["app-store", "ad-hoc", "enterprise", "development"]
55
+ },
56
+ "buildNumberStrategy": {
57
+ "type": "object",
58
+ "additionalProperties": false,
59
+ "required": ["mode"],
60
+ "properties": {
61
+ "mode": { "type": "string", "enum": ["increment", "fixed"] },
62
+ "value": { "type": "integer", "minimum": 1 }
63
+ }
64
+ },
65
+ "outputIpaName": { "type": "string" },
66
+ "exportOptionsPath": { "type": "string" }
67
+ }
68
+ },
69
+ "android": {
70
+ "type": "object",
71
+ "additionalProperties": false,
72
+ "required": [
73
+ "enabled",
74
+ "module",
75
+ "buildGradlePath",
76
+ "dsl",
77
+ "playTrackBeta",
78
+ "playTrackPromote",
79
+ "aabPath"
80
+ ],
81
+ "properties": {
82
+ "enabled": { "type": "boolean" },
83
+ "module": { "type": "string" },
84
+ "buildGradlePath": { "type": "string" },
85
+ "dsl": { "type": "string", "enum": ["groovy", "kotlin"] },
86
+ "playTrackBeta": { "type": "string" },
87
+ "playTrackPromote": { "type": "string" },
88
+ "aabPath": { "type": "string" },
89
+ "releaseStatus": { "type": "string" }
90
+ }
91
+ },
92
+ "version": {
93
+ "type": "object",
94
+ "additionalProperties": false,
95
+ "required": ["sources", "defaultBump", "extraFiles"],
96
+ "properties": {
97
+ "sources": {
98
+ "type": "array",
99
+ "minItems": 1,
100
+ "items": {
101
+ "type": "object",
102
+ "additionalProperties": false,
103
+ "required": ["path", "kind"],
104
+ "properties": {
105
+ "path": { "type": "string" },
106
+ "kind": {
107
+ "type": "string",
108
+ "enum": [
109
+ "packageJson",
110
+ "androidGradle",
111
+ "androidGradleKts",
112
+ "iosPbxproj",
113
+ "stringLiteral"
114
+ ]
115
+ },
116
+ "pattern": { "type": "string" },
117
+ "targets": {
118
+ "type": "array",
119
+ "items": { "type": "string" }
120
+ }
121
+ }
122
+ }
123
+ },
124
+ "defaultBump": { "type": "string", "enum": ["patch", "minor", "major"] },
125
+ "extraFiles": {
126
+ "type": "array",
127
+ "items": {
128
+ "type": "object",
129
+ "additionalProperties": false,
130
+ "required": ["path", "kind"],
131
+ "properties": {
132
+ "path": { "type": "string" },
133
+ "kind": { "type": "string", "enum": ["stringLiteral"] },
134
+ "pattern": { "type": "string" }
135
+ }
136
+ }
137
+ }
138
+ }
139
+ },
140
+ "verify": {
141
+ "type": "object",
142
+ "additionalProperties": false,
143
+ "required": ["install", "commands"],
144
+ "properties": {
145
+ "install": { "type": "string" },
146
+ "commands": {
147
+ "type": "array",
148
+ "items": { "type": "string" }
149
+ }
150
+ }
151
+ },
152
+ "build": {
153
+ "type": "object",
154
+ "additionalProperties": false,
155
+ "required": ["iosOutputDir", "typecheckCommand"],
156
+ "properties": {
157
+ "iosOutputDir": { "type": "string" },
158
+ "typecheckCommand": { "type": "string" },
159
+ "maxAabSizeMb": { "type": ["number", "null"] },
160
+ "maxIpaSizeMb": { "type": ["number", "null"] }
161
+ }
162
+ },
163
+ "gates": {
164
+ "type": "object",
165
+ "additionalProperties": false,
166
+ "required": ["requireCleanGitForPromote", "requireCompileBeforeUpload"],
167
+ "properties": {
168
+ "requireCleanGitForPromote": { "type": "boolean" },
169
+ "requireCompileBeforeUpload": { "type": "boolean" }
170
+ }
171
+ },
172
+ "credentials": {
173
+ "type": "object",
174
+ "additionalProperties": false,
175
+ "required": ["provider"],
176
+ "properties": {
177
+ "provider": {
178
+ "type": "string",
179
+ "enum": ["keychain", "file", "env"]
180
+ }
181
+ }
182
+ },
183
+ "logging": {
184
+ "type": "object",
185
+ "additionalProperties": false,
186
+ "required": ["enabled", "directory"],
187
+ "properties": {
188
+ "enabled": { "type": "boolean" },
189
+ "directory": { "type": "string" }
190
+ }
191
+ },
192
+ "archives": {
193
+ "type": "object",
194
+ "additionalProperties": false,
195
+ "required": ["storeUploadedVersions", "directory"],
196
+ "properties": {
197
+ "storeUploadedVersions": { "type": "boolean" },
198
+ "directory": { "type": "string" }
199
+ }
200
+ },
201
+ "ruby": {
202
+ "type": "object",
203
+ "additionalProperties": false,
204
+ "properties": {
205
+ "version": { "type": "string" }
206
+ }
207
+ }
208
+ }
209
+ }
@@ -0,0 +1,7 @@
1
+ # Copy to android/keystore.properties (gitignored) and fill in local values.
2
+ # Do not commit real passwords.
3
+
4
+ MYAPP_UPLOAD_STORE_FILE=keystore.jks
5
+ MYAPP_UPLOAD_KEY_ALIAS=key0
6
+ MYAPP_UPLOAD_STORE_PASSWORD=
7
+ MYAPP_UPLOAD_KEY_PASSWORD=