sdk-release-kit 0.2.0__tar.gz

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 (50) hide show
  1. sdk_release_kit-0.2.0/.claude-plugin/marketplace.json +20 -0
  2. sdk_release_kit-0.2.0/.gitignore +41 -0
  3. sdk_release_kit-0.2.0/AGENTS.md +68 -0
  4. sdk_release_kit-0.2.0/CLAUDE.md +1 -0
  5. sdk_release_kit-0.2.0/CONTRIBUTING.md +13 -0
  6. sdk_release_kit-0.2.0/LICENSE +21 -0
  7. sdk_release_kit-0.2.0/PKG-INFO +307 -0
  8. sdk_release_kit-0.2.0/README.md +277 -0
  9. sdk_release_kit-0.2.0/context7.json +16 -0
  10. sdk_release_kit-0.2.0/examples/custom.example.yaml +18 -0
  11. sdk_release_kit-0.2.0/examples/keys.example.yaml +18 -0
  12. sdk_release_kit-0.2.0/examples/outputs.example.yaml +19 -0
  13. sdk_release_kit-0.2.0/examples/repos.example.yaml +33 -0
  14. sdk_release_kit-0.2.0/llms.txt +24 -0
  15. sdk_release_kit-0.2.0/plugins/sdk-release-kit/.claude-plugin/icon.png +0 -0
  16. sdk_release_kit-0.2.0/plugins/sdk-release-kit/.claude-plugin/plugin.json +13 -0
  17. sdk_release_kit-0.2.0/plugins/sdk-release-kit/.mcp.json +8 -0
  18. sdk_release_kit-0.2.0/plugins/sdk-release-kit/skills/release-sdks/SKILL.md +25 -0
  19. sdk_release_kit-0.2.0/pyproject.toml +55 -0
  20. sdk_release_kit-0.2.0/recipes/sdks.yaml +222 -0
  21. sdk_release_kit-0.2.0/server.json +29 -0
  22. sdk_release_kit-0.2.0/src/sdk_release_kit/__init__.py +3 -0
  23. sdk_release_kit-0.2.0/src/sdk_release_kit/builders/__init__.py +24 -0
  24. sdk_release_kit-0.2.0/src/sdk_release_kit/builders/android.py +86 -0
  25. sdk_release_kit-0.2.0/src/sdk_release_kit/builders/base.py +217 -0
  26. sdk_release_kit-0.2.0/src/sdk_release_kit/builders/flutter.py +16 -0
  27. sdk_release_kit-0.2.0/src/sdk_release_kit/builders/generic.py +56 -0
  28. sdk_release_kit-0.2.0/src/sdk_release_kit/builders/ios.py +204 -0
  29. sdk_release_kit-0.2.0/src/sdk_release_kit/builders/reactnative.py +32 -0
  30. sdk_release_kit-0.2.0/src/sdk_release_kit/cli.py +198 -0
  31. sdk_release_kit-0.2.0/src/sdk_release_kit/mcp_server.py +240 -0
  32. sdk_release_kit-0.2.0/src/sdk_release_kit/models.py +306 -0
  33. sdk_release_kit-0.2.0/src/sdk_release_kit/paths.py +85 -0
  34. sdk_release_kit-0.2.0/src/sdk_release_kit/pipeline.py +446 -0
  35. sdk_release_kit-0.2.0/src/sdk_release_kit/recipes.py +110 -0
  36. sdk_release_kit-0.2.0/src/sdk_release_kit/runlog.py +62 -0
  37. sdk_release_kit-0.2.0/src/sdk_release_kit/server.py +331 -0
  38. sdk_release_kit-0.2.0/src/sdk_release_kit/store/__init__.py +29 -0
  39. sdk_release_kit-0.2.0/src/sdk_release_kit/store/_yaml.py +46 -0
  40. sdk_release_kit-0.2.0/src/sdk_release_kit/store/custom.py +34 -0
  41. sdk_release_kit-0.2.0/src/sdk_release_kit/store/keys.py +111 -0
  42. sdk_release_kit-0.2.0/src/sdk_release_kit/store/outputs.py +15 -0
  43. sdk_release_kit-0.2.0/src/sdk_release_kit/store/repos.py +15 -0
  44. sdk_release_kit-0.2.0/src/sdk_release_kit/uploaders/__init__.py +20 -0
  45. sdk_release_kit-0.2.0/src/sdk_release_kit/uploaders/onedrive.py +129 -0
  46. sdk_release_kit-0.2.0/src/sdk_release_kit/uploaders/play.py +108 -0
  47. sdk_release_kit-0.2.0/src/sdk_release_kit/uploaders/testflight.py +105 -0
  48. sdk_release_kit-0.2.0/web/app.js +305 -0
  49. sdk_release_kit-0.2.0/web/index.html +142 -0
  50. sdk_release_kit-0.2.0/web/styles.css +93 -0
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "sdk-release-kit",
3
+ "owner": {
4
+ "name": "Yahya",
5
+ "url": "https://github.com/yehyatt"
6
+ },
7
+ "metadata": {
8
+ "description": "Build and ship mobile SDK demo apps and packages from Claude Code."
9
+ },
10
+ "plugins": [
11
+ {
12
+ "name": "sdk-release-kit",
13
+ "source": "./plugins/sdk-release-kit",
14
+ "description": "MCP server and skill to build Android, iOS, Flutter and React Native SDK demos and packages and publish them to a shared folder, Google Play internal testing and TestFlight.",
15
+ "version": "0.2.0",
16
+ "category": "development",
17
+ "keywords": ["mobile", "sdk", "release", "android", "ios", "flutter", "react-native", "testflight", "google-play"]
18
+ }
19
+ ]
20
+ }
@@ -0,0 +1,41 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ .venv/
6
+ venv/
7
+ build/
8
+ dist/
9
+ .pytest_cache/
10
+ .ruff_cache/
11
+
12
+ # Local settings never belong in git (they live in ~/.config/sdk-release-kit/)
13
+ repos.yaml
14
+ outputs.yaml
15
+ keys.yaml
16
+ custom.yaml
17
+ recipes.d/
18
+ !examples/*.example.yaml
19
+
20
+ # Build output
21
+ staging/
22
+ runs/
23
+
24
+ # Secrets and key material
25
+ .env
26
+ .env.*
27
+ *.keystore
28
+ *.jks
29
+ *.p8
30
+ *.p12
31
+ *.pem
32
+ *.mobileprovision
33
+ key.properties
34
+ *service-account*.json
35
+ *adminsdk*.json
36
+ client_secret*.json
37
+
38
+ # OS / editors
39
+ .DS_Store
40
+ .idea/
41
+ .vscode/
@@ -0,0 +1,68 @@
1
+ # AGENTS.md
2
+
3
+ Guidance for AI coding agents (and humans) changing this repository.
4
+
5
+ ## Hard rules
6
+
7
+ 1. **No absolute machine paths** in `recipes/`, `examples/` or Python defaults. Recipes use
8
+ paths relative to the SDK checkout. Absolute paths live only in user settings
9
+ (`~/.config/sdk-release-kit/`). Defaults may use `~`, which is portable.
10
+ 2. **No secrets in git.** Never commit keystores, `.p8` files, Play service account JSON,
11
+ tokens, passwords or card numbers, and never write them into examples or tests. Keys are
12
+ configured through the Settings UI or the user data directory, and passwords are referenced
13
+ (`SecretRef`: keychain or env), never stored.
14
+ 3. **Never log a secret.** Anything resolved from a `SecretRef` must be registered with
15
+ `RunLog.add_secret()` before it can reach a log line. Prefer environment variables over
16
+ command-line arguments for secret values.
17
+ 4. **Keep settings backward compatible.** Settings models use `extra="allow"`; add fields with
18
+ defaults, never rename or remove without a migration and a `schema_version` bump.
19
+ 5. **Destinations soft-skip.** Missing Play or TestFlight credentials must produce a "skipped"
20
+ result with a reason, not a failed run.
21
+
22
+ ## Where things are
23
+
24
+ | Path | Purpose |
25
+ | --- | --- |
26
+ | `recipes/sdks.yaml` | Portable recipes and shared-folder target ids |
27
+ | `examples/*.example.yaml` | Templates copied by `sdk-release-kit init` |
28
+ | `src/sdk_release_kit/models.py` | Pydantic schemas for recipes and settings |
29
+ | `src/sdk_release_kit/paths.py` | User data dir, env overrides, `safe_join` |
30
+ | `src/sdk_release_kit/store/` | One module per settings layer (repos, outputs, keys, custom) |
31
+ | `src/sdk_release_kit/recipes.py` | Recipe loading, overlays, `${...}` substitution |
32
+ | `src/sdk_release_kit/builders/` | Step handlers per platform; `base.py` runs and collects |
33
+ | `src/sdk_release_kit/uploaders/` | Shared folder, Google Play, TestFlight |
34
+ | `src/sdk_release_kit/pipeline.py` | Select, build, publish, report |
35
+ | `src/sdk_release_kit/server.py` | Settings UI API (FastAPI) |
36
+ | `src/sdk_release_kit/cli.py` | Typer CLI |
37
+ | `src/sdk_release_kit/mcp_server.py` | MCP server for agents (`sdk-release-kit mcp`); never return secret values |
38
+ | `plugins/sdk-release-kit/` | Claude Code plugin (MCP config + `release-sdks` skill); `.claude-plugin/marketplace.json` lists it |
39
+ | `server.json`, `context7.json`, `llms.txt` | MCP Registry, Context7 and LLM discovery metadata; keep versions in sync with `pyproject.toml` |
40
+ | `web/` | Static Settings UI (plain HTML, CSS, JS; no build step) |
41
+
42
+ ## Common changes
43
+
44
+ **Add an SDK.** Add a recipe in `recipes/sdks.yaml` using existing step types, plus any new
45
+ `onedrive_targets`. No code change needed. Run `sdk-release-kit recipes` and
46
+ `sdk-release-kit build --dry-run --sdks <id>` to check it.
47
+
48
+ **Add a step type or platform.**
49
+ 1. Add a model class to `models.py` (subclass `_StepBase`, `type: Literal["name"]`) and add it
50
+ to the `Step` union.
51
+ 2. Write a handler in `builders/<platform>.py` decorated with `@handler("name")` that returns a
52
+ `StepPlan` (commands, optional `prepare`/`finish`, extra collect roots).
53
+ 3. Import the module in `builders/__init__.py`.
54
+ 4. Document it in the step table in `README.md`.
55
+
56
+ **Add a destination.** Add an uploader module with `readiness()` and an upload function, add the
57
+ name to `Destination`/`DESTINATIONS` in `models.py`, add an Outputs model section, and wire it
58
+ into `Pipeline.destination_status` and `Pipeline._upload`.
59
+
60
+ **Read a new custom value.** Reference it from a recipe as `${custom.your.key:-default}`. Do not
61
+ hardcode team ids, bundle ids or package names in Python.
62
+
63
+ ## Working style
64
+
65
+ * Small, focused diffs. Keep the UI dependency free.
66
+ * Verify changes with `sdk-release-kit build --dry-run`, then a real build of one recipe into
67
+ a scratch staging directory (`SDK_RELEASE_KIT_HOME=/tmp/srk sdk-release-kit init`).
68
+ * Run `ruff check src` before committing.
@@ -0,0 +1 @@
1
+ @AGENTS.md
@@ -0,0 +1,13 @@
1
+ # Contributing
2
+
3
+ Thanks for helping. Issues and pull requests are welcome.
4
+
5
+ 1. Read [AGENTS.md](AGENTS.md); its hard rules apply to everyone, human or AI.
6
+ 2. Set up: `python3 -m venv .venv && source .venv/bin/activate && pip install -e '.[dev]'`.
7
+ 3. Use a scratch settings directory while developing:
8
+ `export SDK_RELEASE_KIT_HOME=/tmp/srk && sdk-release-kit init`.
9
+ 4. Before opening a pull request: `ruff check src` and `sdk-release-kit build --dry-run`.
10
+ 5. Never include real credentials, client names, real bundle ids or card numbers in code,
11
+ examples, screenshots or issues. Use `com.example.*` identifiers.
12
+
13
+ Keep pull requests small and describe how you verified the change.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Yahya (github.com/yehyatt)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,307 @@
1
+ Metadata-Version: 2.5
2
+ Name: sdk-release-kit
3
+ Version: 0.2.0
4
+ Summary: Build mobile SDK demo apps and packages, then publish them to a shared folder, Google Play internal testing and TestFlight.
5
+ Project-URL: Homepage, https://github.com/yehyatt/sdk-release-kit
6
+ Project-URL: Repository, https://github.com/yehyatt/sdk-release-kit
7
+ Project-URL: Issues, https://github.com/yehyatt/sdk-release-kit/issues
8
+ Author: Yahya
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: ai-agents,android,flutter,google-play,ios,mcp,mcp-server,react-native,release,sdk,testflight
12
+ Classifier: Operating System :: MacOS
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Topic :: Software Development :: Build Tools
15
+ Requires-Python: >=3.11
16
+ Requires-Dist: fastapi>=0.110
17
+ Requires-Dist: keyring>=24
18
+ Requires-Dist: mcp<3,>=2.3
19
+ Requires-Dist: pydantic>=2.6
20
+ Requires-Dist: pyyaml>=6.0
21
+ Requires-Dist: typer>=0.12
22
+ Requires-Dist: uvicorn>=0.29
23
+ Provides-Extra: dev
24
+ Requires-Dist: pytest>=8; extra == 'dev'
25
+ Requires-Dist: ruff>=0.4; extra == 'dev'
26
+ Provides-Extra: play
27
+ Requires-Dist: google-api-python-client>=2.100; extra == 'play'
28
+ Requires-Dist: google-auth>=2.25; extra == 'play'
29
+ Description-Content-Type: text/markdown
30
+
31
+ # SDK Release Kit
32
+
33
+ Build mobile SDK demo apps and SDK packages from one or more local checkouts, then publish
34
+ them to a shared folder (OneDrive or any synced folder), Google Play internal testing and
35
+ TestFlight. Configure it once per machine through a local Settings UI; the same repository
36
+ works on every developer's Mac without editing scripts.
37
+
38
+ Supports Android (Gradle), iOS (Xcode), Flutter and React Native SDKs out of the box. New
39
+ SDKs are added in a YAML recipe file, usually without code changes.
40
+
41
+ AI agents can drive it too: it ships an [MCP server](#use-with-ai-agents) and a Claude Code
42
+ plugin.
43
+
44
+ <!-- mcp-name: io.github.yehyatt/sdk-release-kit -->
45
+
46
+ ## Install
47
+
48
+ Python 3.11 or newer.
49
+
50
+ ```bash
51
+ pip install sdk-release-kit # or run without installing: uvx sdk-release-kit --help
52
+ sdk-release-kit init
53
+ sdk-release-kit serve
54
+ ```
55
+
56
+ From source:
57
+
58
+ ```bash
59
+ git clone https://github.com/yehyatt/sdk-release-kit.git
60
+ cd sdk-release-kit
61
+ python3 -m venv .venv && source .venv/bin/activate
62
+ pip install -e . # add '.[play]' for direct Google Play API uploads
63
+ sdk-release-kit init # creates your settings from examples/
64
+ sdk-release-kit serve # opens the Settings UI on http://127.0.0.1:8765
65
+ ```
66
+
67
+ Fill in **Repositories**, **Outputs** and **Keys** in the UI, then:
68
+
69
+ ```bash
70
+ sdk-release-kit build --sdks flutter,android-current --artifacts both --dest onedrive
71
+ ```
72
+
73
+ ## Use with AI agents
74
+
75
+ `sdk-release-kit mcp` runs an MCP server on stdio. Its tools:
76
+
77
+ | Tool | Does |
78
+ | --- | --- |
79
+ | `get_status` | Checkouts, key status (configured or missing, never values), destination readiness, tools on PATH |
80
+ | `list_recipes` | Recipe ids, steps, store flags and checkout per recipe |
81
+ | `plan_build` | Dry run: exact commands, collected files, ready or skipped destinations |
82
+ | `run_build` | Build and publish; returns the run report and the end of the log |
83
+ | `list_runs`, `get_run_report` | Previous runs and their reports |
84
+ | `set_repo_path` | Point a recipe at a local checkout |
85
+
86
+ **Claude Code plugin** (MCP server plus a `release-sdks` skill):
87
+
88
+ ```
89
+ /plugin install sdk-release-kit --marketplace yehyatt/sdk-release-kit
90
+ ```
91
+
92
+ On older Claude Code versions: `/plugin marketplace add yehyatt/sdk-release-kit`, then
93
+ `/plugin install sdk-release-kit@sdk-release-kit`.
94
+
95
+ **Claude Code, MCP only:**
96
+
97
+ ```bash
98
+ claude mcp add sdk-release-kit -- uvx sdk-release-kit mcp
99
+ ```
100
+
101
+ **Claude Desktop, Cursor, Windsurf, VS Code and other MCP clients:**
102
+
103
+ ```json
104
+ {
105
+ "mcpServers": {
106
+ "sdk-release-kit": { "command": "uvx", "args": ["sdk-release-kit", "mcp"] }
107
+ }
108
+ }
109
+ ```
110
+
111
+ The server never returns secret values, and keys are still entered through the Settings UI.
112
+ Agents are told to plan before building and to confirm before uploading to Play or TestFlight.
113
+ Listed in the [MCP Registry](https://registry.modelcontextprotocol.io) as
114
+ `io.github.yehyatt/sdk-release-kit`. A summary for language models is in
115
+ [llms.txt](https://github.com/yehyatt/sdk-release-kit/blob/main/llms.txt).
116
+
117
+ ## Commands
118
+
119
+ | Command | What it does |
120
+ | --- | --- |
121
+ | `sdk-release-kit init [--force]` | Copy `examples/*.example.yaml` into your settings directory |
122
+ | `sdk-release-kit serve` | Settings UI (Repositories, Outputs, Keys, Custom data, Run) |
123
+ | `sdk-release-kit build ...` | Build and publish (see options below) |
124
+ | `sdk-release-kit build --dry-run` | Print the plan: commands, collected files, destinations |
125
+ | `sdk-release-kit recipes` | List recipe ids and where they were loaded from |
126
+ | `sdk-release-kit status` | Settings files, key status (never values), installed tools |
127
+ | `sdk-release-kit keys set <name>` | Store a secret in the OS keychain (prompted, not echoed) |
128
+ | `sdk-release-kit mcp` | MCP server on stdio for AI agents |
129
+
130
+ `build` options:
131
+
132
+ * `--sdks android-current,flutter`: recipe ids. Default: every enabled repository.
133
+ * `--artifacts demos|packages|both`
134
+ * `--dest onedrive,play,testflight`: default is every destination enabled in Outputs.
135
+ * `--dry-run`: plan only. `--skip-upload`: build only. `--json`: print the report.
136
+
137
+ ## How configuration is split
138
+
139
+ | Layer | Where | In git? | Holds |
140
+ | --- | --- | --- | --- |
141
+ | Recipes | `recipes/sdks.yaml` | Yes | Relative paths inside a repo, Gradle tasks, Xcode schemes, shared-folder target ids, store flags |
142
+ | Repos | `<settings>/repos.yaml` | No | Recipe id to absolute checkout path; JDK, Android SDK, Flutter, Xcode locations |
143
+ | Outputs | `<settings>/outputs.yaml` | No | Staging directory, shared folder root, Play and TestFlight options |
144
+ | Keys | `<settings>/keys.yaml` | No | Paths to key files and *references* to passwords (keychain or env) |
145
+ | Custom | `<settings>/custom.yaml` | No | Free-form YAML read by recipes as `${custom.key}` |
146
+
147
+ `<settings>` is `~/.config/sdk-release-kit/` (or `$XDG_CONFIG_HOME/sdk-release-kit`, or
148
+ `%APPDATA%\sdk-release-kit` on Windows). Overrides:
149
+
150
+ * `SDK_RELEASE_KIT_HOME`: use another settings directory
151
+ * `SDK_RELEASE_KIT_REPOS`, `SDK_RELEASE_KIT_OUTPUTS`, `SDK_RELEASE_KIT_KEYS`, `SDK_RELEASE_KIT_CUSTOM`: point one layer at a specific file
152
+ * `SDK_RELEASE_KIT_RECIPES`: an extra recipe file merged over the shipped one
153
+
154
+ Machine-specific recipe tweaks go in `<settings>/recipes.d/*.yaml`. They are deep-merged over
155
+ `recipes/sdks.yaml`, so you can change one scheme name without forking the file.
156
+
157
+ ## Adapting it to your SDKs
158
+
159
+ The shipped recipes use placeholder names. Do these three things before your first release.
160
+
161
+ 1. **Match the recipes to your repositories.** Open `recipes/sdks.yaml` and check, for every
162
+ recipe you use, the `workdir`, Xcode `workspace` and `scheme`, Gradle tasks, `framework_name`
163
+ and `collect` globs. Placeholders include `DemoApp`, `PaymentSDK`, `SimpleIntegration`,
164
+ `:app` and `example/`. If the change applies to everyone using the repo, edit the file and
165
+ commit it. If it only applies to your machine, put just the changed keys in
166
+ `<settings>/recipes.d/local.yaml`; it is deep-merged over the shipped file:
167
+
168
+ ```yaml
169
+ recipes:
170
+ ios-current:
171
+ demos:
172
+ - type: xcode-simulator
173
+ workspace: Example/Example.xcworkspace
174
+ scheme: Example
175
+ target: ios-native-current
176
+ ```
177
+
178
+ Lists such as `demos` are replaced as a whole, so repeat every step of a list you override.
179
+ Then run `sdk-release-kit build --dry-run --sdks <id>` and read the commands it prints.
180
+
181
+ 2. **Put your real identifiers in Custom data, not in git.** The recipes read store ids and the
182
+ Apple team from `custom.yaml` (Custom data page), falling back to `com.example.*`:
183
+
184
+ ```yaml
185
+ ios_team_id: "ABCDE12345"
186
+ app_ids:
187
+ android_native: com.yourcompany.sdk.demo
188
+ ios_native: com.yourcompany.sdk.demoapp
189
+ flutter_android: com.yourcompany.sdk.flutter.demoapp
190
+ flutter_ios: com.yourcompany.sdk.flutter.demoapp
191
+ rn_android: com.yourcompany.sdk.reactnative.demoapp
192
+ rn_ios: com.yourcompany.sdk.reactnative.demoapp
193
+ ```
194
+
195
+ A recipe with an empty `android_package` is skipped for Play. A `com.example.*` id is not
196
+ skipped: the upload fails, because Play only accepts apps that exist in your Play Console.
197
+
198
+ 3. **Prove each destination once.** Start with the shared folder only:
199
+ `sdk-release-kit build --sdks <id> --artifacts demos --dest onedrive`, and open the copied
200
+ APK or `.app`. Then fill in the Keys page (the Keys chips must all show "configured") and
201
+ run one upload per store with a single recipe:
202
+ `--dest play` with an Android recipe, `--dest testflight` with an iOS one. Check the build
203
+ appears on the internal track or in TestFlight before relying on combined runs. Each run's
204
+ `report.json` lists what was uploaded, skipped or failed and why.
205
+
206
+ ## Multi-device setup
207
+
208
+ 1. Clone the repository and `pip install -e .` on the new machine.
209
+ 2. `sdk-release-kit init`, or copy your `<settings>` directory from another machine and fix
210
+ the absolute paths.
211
+ 3. Open `sdk-release-kit serve` and check that every repository shows a green check and every
212
+ key shows "configured".
213
+ 4. Run `sdk-release-kit build --dry-run` before the first real build.
214
+
215
+ ## Destinations
216
+
217
+ | Artifact | Shared folder | Google Play | TestFlight |
218
+ | --- | --- | --- | --- |
219
+ | Demo apps (APK, AAB, IPA, simulator `.app`) | Yes | AAB, when the recipe sets `store.play` | IPA, when the recipe sets `store.testflight` |
220
+ | SDK packages (AAR zip, XCFramework zip, `npm pack` tarball, Flutter source zip) | Yes | No | No |
221
+
222
+ * **Shared folder.** Treated as a locally synced directory: files are copied in (written to a
223
+ temporary name, then renamed). Demos land at `<root>/<demos folder>/<target name>`, for
224
+ example `android/native-current.apk`. Packages land at
225
+ `<root>/packages/<recipe-id>/<version>/` with `<recipe-id>/LATEST.txt` describing the newest.
226
+ * **Google Play.** Uploads the AAB to the configured track (default `internal`) with the
227
+ Google Play Developer API (`pip install -e '.[play]'`) or `fastlane supply`.
228
+ * **TestFlight.** Uploads the IPA with an App Store Connect API key, through `xcrun altool`
229
+ (default) or `fastlane pilot`.
230
+
231
+ When Play or TestFlight credentials are missing, those uploads are **skipped with a message**
232
+ and the build and shared-folder copy still run.
233
+
234
+ **Store collision rule.** Current and legacy demos often share an applicationId or bundle id.
235
+ Only recipes with `store.play: true` or `store.testflight: true` are uploaded, and within one
236
+ run each id is uploaded once. The shipped recipes enable stores for `*-current` and `flutter`
237
+ and disable them for `*-legacy`.
238
+
239
+ ## Recipes
240
+
241
+ A recipe has `demos` and `packages`, each a list of steps:
242
+
243
+ ```yaml
244
+ recipes:
245
+ android-current:
246
+ platform: android
247
+ version: {file: gradle.properties, regex: '^\s*VERSION_NAME\s*=\s*(\S+)'}
248
+ store: {play: true, android_package: "${custom.app_ids.android_native:-com.example.demo}"}
249
+ demos:
250
+ - type: gradle
251
+ tasks: [":app:assembleRelease", ":app:bundleRelease"]
252
+ collect:
253
+ - {glob: "app/build/outputs/apk/release/*.apk", kind: apk, target: android-native-current}
254
+ - {glob: "app/build/outputs/bundle/release/*.aab", kind: aab}
255
+ ```
256
+
257
+ | Step type | Runs |
258
+ | --- | --- |
259
+ | `run` | Any command (`cmd` as a list or a string, no shell) |
260
+ | `gradle` | `./gradlew <tasks>` with JAVA_HOME, Android SDK and optional release signing |
261
+ | `flutter` | `flutter <args>` |
262
+ | `xcode-simulator` | `xcodebuild` for the iOS Simulator; collects the `.app` |
263
+ | `xcode-ipa` | Archive and export an IPA (App Store Connect key used for automatic signing when set) |
264
+ | `xcframework` | Device and simulator archives combined into a zipped XCFramework |
265
+ | `npm-pack` | `npm pack` |
266
+ | `source-zip` | `git archive` of the committed sources (or a filtered file walk) |
267
+
268
+ Every step accepts `workdir` (relative to the checkout), `env`, `when: [play|testflight|onedrive]`
269
+ (run only when that destination is active, for example IPA archives only for TestFlight),
270
+ `host: macos` and `collect`.
271
+
272
+ Strings may use `${custom.dotted.key}`, `${env.NAME}`, `${version}` and `${recipe}`, with an
273
+ optional default: `${custom.ios_team_id:-}`.
274
+
275
+ The version comes from the recipe's `version` (a file plus regex, or a command), then
276
+ `git describe`, then a timestamp.
277
+
278
+ ## Security model
279
+
280
+ * Secrets are never stored in the repository or in plain YAML. `keys.yaml` holds file paths
281
+ and references such as `{source: keychain, name: android.store_password}` or
282
+ `{source: env, name: ASC_KEY_ID}`. Values are read at run time.
283
+ * Settings files are written with mode `600` and the settings directory with mode `700`. Key
284
+ files uploaded through the UI are stored in `<settings>/keys/` with mode `600`.
285
+ * Android signing is passed to Gradle through `ORG_GRADLE_PROJECT_*` environment variables, not
286
+ the command line, so passwords do not show up in process listings.
287
+ * Every log line passes through a redactor that masks known secret values. The Keys page and
288
+ `status` command only report "configured" or "missing".
289
+ * The Settings UI binds to `127.0.0.1`, accepts only loopback `Host` headers, and requires a
290
+ per-process token on every API call, so other websites in your browser cannot drive it.
291
+ * `.gitignore` excludes settings files, staging output and key material.
292
+
293
+ ## Output
294
+
295
+ Each real run writes `<staging>/runs/<timestamp>/` with the collected artifacts per recipe,
296
+ `log.txt` and `report.json` (selections, versions, commits, artifacts, upload results).
297
+ Intermediate build products (derived data, archives) are kept in `<staging>/work/` between
298
+ runs to speed up rebuilds.
299
+
300
+ ## Contributing
301
+
302
+ See [CONTRIBUTING.md](https://github.com/yehyatt/sdk-release-kit/blob/main/CONTRIBUTING.md). AI coding agents should read [AGENTS.md](https://github.com/yehyatt/sdk-release-kit/blob/main/AGENTS.md)
303
+ first.
304
+
305
+ ## License
306
+
307
+ MIT. See [LICENSE](https://github.com/yehyatt/sdk-release-kit/blob/main/LICENSE).