action-platform 0.2.0__tar.gz → 0.3.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 (85) hide show
  1. {action_platform-0.2.0 → action_platform-0.3.0}/PKG-INFO +95 -6
  2. {action_platform-0.2.0 → action_platform-0.3.0}/README.md +91 -5
  3. {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/__init__.py +2 -2
  4. action_platform-0.3.0/action_platform/api/LAST_VERSION +1 -0
  5. action_platform-0.3.0/action_platform/api/__init__.py +1 -0
  6. action_platform-0.3.0/action_platform/api/credentials.py +87 -0
  7. action_platform-0.3.0/action_platform/api/models.py +177 -0
  8. action_platform-0.3.0/action_platform/api/registry.py +167 -0
  9. action_platform-0.3.0/action_platform/api/server.py +436 -0
  10. action_platform-0.3.0/action_platform/cli/commands/api.py +32 -0
  11. {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/cli/commands/branch.py +1 -1
  12. {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/cli/commands/cloud.py +2 -2
  13. {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/cli/commands/gitflow.py +1 -1
  14. {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/cli/commands/init.py +6 -2
  15. {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/cli/commands/install.py +1 -1
  16. action_platform-0.3.0/action_platform/cli/commands/login.py +39 -0
  17. {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/cli/commands/mcp.py +9 -0
  18. {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/cli/commands/pr.py +1 -1
  19. {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/cli/commands/release.py +7 -1
  20. {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/cli/commands/service.py +2 -2
  21. {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/cli/setup.py +6 -0
  22. action_platform-0.3.0/action_platform/core/__init__.py +8 -0
  23. {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/core/action_platform.py +14 -7
  24. {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/core/config.py +11 -5
  25. action_platform-0.3.0/action_platform/core/flow/__init__.py +1 -0
  26. {action_platform-0.2.0/action_platform/core → action_platform-0.3.0/action_platform/core/flow}/branching.py +1 -1
  27. {action_platform-0.2.0/action_platform/core → action_platform-0.3.0/action_platform/core/flow}/git.py +18 -4
  28. {action_platform-0.2.0/action_platform/core → action_platform-0.3.0/action_platform/core/flow}/gitflow.py +2 -2
  29. {action_platform-0.2.0/action_platform/core → action_platform-0.3.0/action_platform/core/flow}/pullrequest.py +3 -2
  30. action_platform-0.3.0/action_platform/core/release/__init__.py +1 -0
  31. action_platform-0.3.0/action_platform/core/release/components.py +78 -0
  32. action_platform-0.3.0/action_platform/core/release/deploy.py +91 -0
  33. action_platform-0.3.0/action_platform/core/release/release.py +135 -0
  34. action_platform-0.3.0/action_platform/core/scaffold/__init__.py +1 -0
  35. {action_platform-0.2.0/action_platform/core → action_platform-0.3.0/action_platform/core/scaffold}/generate.py +43 -69
  36. {action_platform-0.2.0/action_platform/core → action_platform-0.3.0/action_platform/core/scaffold}/install.py +2 -2
  37. {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/mcp/server.py +37 -3
  38. {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/mcp/tools/flow.py +6 -2
  39. {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/mcp/tools/lifecycle.py +7 -1
  40. {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/mcp/tools/matrix.py +1 -1
  41. {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/mcp/tools/project.py +3 -3
  42. action_platform-0.3.0/action_platform/mcp/tools/remote.py +109 -0
  43. action_platform-0.3.0/action_platform/providers/__init__.py +9 -0
  44. action_platform-0.3.0/action_platform/providers/source/__init__.py +48 -0
  45. action_platform-0.3.0/action_platform/providers/source/bitbucket.py +111 -0
  46. action_platform-0.3.0/action_platform/providers/source/generic.py +61 -0
  47. action_platform-0.3.0/action_platform/providers/source/github.py +204 -0
  48. action_platform-0.3.0/action_platform/providers/source/gitlab.py +126 -0
  49. action_platform-0.3.0/action_platform/providers/source/rest.py +58 -0
  50. action_platform-0.3.0/action_platform/remote/__init__.py +1 -0
  51. action_platform-0.3.0/action_platform/remote/client.py +216 -0
  52. action_platform-0.3.0/action_platform/remote/credentials.py +58 -0
  53. {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/settings.py +2 -0
  54. {action_platform-0.2.0 → action_platform-0.3.0}/pyproject.toml +10 -3
  55. action_platform-0.2.0/action_platform/core/__init__.py +0 -1
  56. action_platform-0.2.0/action_platform/core/pipeline.py +0 -192
  57. action_platform-0.2.0/action_platform/providers/__init__.py +0 -5
  58. action_platform-0.2.0/action_platform/providers/source_github.py +0 -122
  59. {action_platform-0.2.0 → action_platform-0.3.0}/LICENSE +0 -0
  60. {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/abc/__init__.py +0 -0
  61. {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/abc/ci_runner.py +0 -0
  62. {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/abc/deploy_target.py +0 -0
  63. {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/abc/source_host.py +0 -0
  64. {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/cli/__init__.py +0 -0
  65. {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/cli/commands/__init__.py +0 -0
  66. {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/cli/commands/deploy.py +0 -0
  67. {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/core/context.py +0 -0
  68. {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/core/exception.py +0 -0
  69. {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/core/module.py +0 -0
  70. {action_platform-0.2.0/action_platform/core → action_platform-0.3.0/action_platform/core/release}/changelog.py +0 -0
  71. {action_platform-0.2.0/action_platform/core → action_platform-0.3.0/action_platform/core/release}/versioning.py +0 -0
  72. {action_platform-0.2.0/action_platform/core → action_platform-0.3.0/action_platform/core/scaffold}/templates.py +0 -0
  73. {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/hooks/__init__.py +0 -0
  74. {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/hooks/commit-msg +0 -0
  75. {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/hooks/gitflow.sh +0 -0
  76. {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/hooks/pre-commit +0 -0
  77. {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/hooks/pre-push +0 -0
  78. {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/logging.py +0 -0
  79. {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/main.py +0 -0
  80. {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/mcp/__init__.py +0 -0
  81. {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/mcp/__main__.py +0 -0
  82. {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/mcp/annotations.py +0 -0
  83. {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/mcp/prompts.py +0 -0
  84. {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/mcp/tools/__init__.py +0 -0
  85. {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/testing/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: action-platform
3
- Version: 0.2.0
3
+ Version: 0.3.0
4
4
  Summary: Your platform team, as a CLI.
5
5
  License: Apache License
6
6
  Version 2.0, January 2004
@@ -222,11 +222,13 @@ Classifier: Programming Language :: Python :: 3.10
222
222
  Classifier: Programming Language :: Python :: 3.11
223
223
  Classifier: Programming Language :: Python :: 3.12
224
224
  Classifier: Programming Language :: Python :: 3.13
225
+ Provides-Extra: api
225
226
  Provides-Extra: github
226
227
  Provides-Extra: gitlab
227
228
  Provides-Extra: mcp
228
229
  Requires-Dist: PyGithub ; extra == "github"
229
230
  Requires-Dist: cookiecutter
231
+ Requires-Dist: fastapi (>=0.115) ; extra == "api"
230
232
  Requires-Dist: jinja2
231
233
  Requires-Dist: mcp (>=2.0) ; extra == "mcp"
232
234
  Requires-Dist: pydantic
@@ -235,6 +237,7 @@ Requires-Dist: python-ulid (>=3.0)
235
237
  Requires-Dist: rich
236
238
  Requires-Dist: typer
237
239
  Requires-Dist: typing-extensions
240
+ Requires-Dist: uvicorn (>=0.30) ; extra == "api"
238
241
  Project-URL: Changelog, https://github.com/actionplatform/action-platform/blob/master/CHANGELOG.md
239
242
  Project-URL: Homepage, https://github.com/actionplatform/action-platform
240
243
  Project-URL: Issues, https://github.com/actionplatform/action-platform/issues
@@ -248,7 +251,7 @@ Description-Content-Type: text/markdown
248
251
  </p>
249
252
 
250
253
  <p align="center">
251
- <strong>Your platform team, as a CLI.</strong>
254
+ <strong>Your platform team: a web app, a CLI and an MCP server on one core.</strong>
252
255
  </p>
253
256
 
254
257
  <p align="center">
@@ -260,7 +263,27 @@ Description-Content-Type: text/markdown
260
263
 
261
264
  ---
262
265
 
263
- Every new service costs the same week: scaffold, lint config, CI, versioning, deploy pipeline, IAM. Then the next one drifts from the last. **Action Platform** turns that week into one command and keeps every project on the same rails — without a control plane to host, a UI to learn, or a vendor to trust with your cloud.
266
+ Every new service costs the same week: scaffold, lint config, CI, versioning, deploy pipeline, IAM. Then the next one drifts from the last. **Action Platform** turns that week into one command — or one click — and keeps every app on the same rails, in your git host and your cloud account.
267
+
268
+ Three doors, one core:
269
+
270
+ | | |
271
+ |---|---|
272
+ | **Web app** | Organizations › projects › apps. Create an app from a template, connect GitHub / GitLab / Bitbucket, cut releases, run deploys — from a browser. One command to self-host. |
273
+ | **CLI** | `pipx install action-platform` and the same verbs on your machine: `init`, `branch`, `pr`, `release`, `deploy`, `rollback`, `diagnose`. |
274
+ | **MCP server** | The same operations as tools for Claude Code, Codex, Cursor — locally, or against your hosted platform after `action-platform login`. |
275
+
276
+ ## Self-host in one command
277
+
278
+ ```bash
279
+ curl -fsSL https://raw.githubusercontent.com/actionplatform/action-platform/master/deploy/install.sh | sudo sh
280
+ # with a domain and TLS:
281
+ curl -fsSL https://raw.githubusercontent.com/actionplatform/action-platform/master/deploy/install.sh | sudo sh -s -- platform.example.com you@example.com
282
+ ```
283
+
284
+ Installs Docker if needed, generates the secrets, starts Postgres + API + web (+ Traefik with Let's Encrypt when a domain is given) and prints the URL. Open it: first account, first organization, connect a code host — done. Files live in `/opt/action-platform`; see [`deploy/`](deploy/) for the compose file and the two Dockerfiles.
285
+
286
+ ## Or just the CLI
264
287
 
265
288
  ```bash
266
289
  pipx install action-platform
@@ -277,7 +300,7 @@ Thirty seconds later you have a FastAPI service with tests, lint, CI wired, a SA
277
300
  | **One command, whole lifecycle** | `init` → `release` → `deploy` → `rollback` → `diagnose` → `destroy`. Same verbs for a Python API on Lambda, a Go service in Docker, a React app on Amplify. |
278
301
  | **Templates from production, not tutorials** | Every project template is extracted from a real shipping product. Real layout, real CI, real gotchas already fixed. |
279
302
  | **Cloud is a layer, not a fork** | Projects stay cloud-agnostic. `--cloud aws/lambda` overlays deploy files; swap to `docker` tomorrow with one command. |
280
- | **Your CI, your account, your git** | Runs on GitHub Actions, GitLab CI or Jenkins you already have. Infra lands in **your** AWS account through OIDC — no long-lived keys, no agent, no SaaS in the loop. |
303
+ | **Your CI, your account, your git** | Runs on GitHub Actions, GitLab CI or Jenkins you already have. Repositories on GitHub, GitLab, Bitbucket or any git server, connected with OAuth. Infra lands in **your** AWS account through OIDC — no long-lived keys, no vendor in the loop. |
281
304
  | **Governance that ships with the code** | Git-flow and Conventional Commits enforced by git hooks before a commit exists and by CI on every PR; changelog generated; `AGENTS.md` for humans and AI agents; Trivy scans; least-privilege IAM in `requirements/`. |
282
305
  | **Fix once, everywhere** | CI logic lives in versioned shared repos (`ci-scripts`, `ci-github`, `ci-gitlab`, `ci-jenkins`). Bump `v1`, every project picks it up. |
283
306
 
@@ -424,13 +447,80 @@ Any client — add to `.mcp.json`:
424
447
  { "mcpServers": { "action-platform": { "command": "uvx", "args": ["--from", "action-platform[mcp]", "action-platform-mcp"] } } }
425
448
  ```
426
449
 
450
+ ## The web app
451
+
452
+ [`apps/web`](apps/web) — Next.js on top of the Python API. **Organization › Project › App**: an organization is the tenant and owns its code hosts; a project groups the apps that ship together; an app is one git repository the platform clones, audits, releases and deploys.
453
+
454
+ - **Setup wizard** on first run: database (SQLite, PostgreSQL or MySQL), first account, first organization, code hosts.
455
+ - **Connect with GitHub / GitLab / Bitbucket** through OAuth (or paste a token). Tokens are encrypted at rest and refreshed when they expire.
456
+ - **Create project** wizard: type → stack → template → configure → review, with the equivalent `action-platform init` shown. Or add an existing repository by URL.
457
+ - Per app: git-flow audit, commits, branches, tags, release preview → confirm → publish, deploy preflight → confirm → ship.
458
+ - Strictly monochrome UI; every confirmation is an in-app dialog.
459
+
460
+ Local development:
461
+
462
+ ```bash
463
+ pip install "action-platform[api]"
464
+ action-platform api --reload # http://127.0.0.1:7788, OpenAPI at /docs
465
+ cd apps/web && npm install && npm run dev # http://localhost:3000 → /setup
466
+ ```
467
+
468
+ Drive a hosted platform from anywhere:
469
+
470
+ ```bash
471
+ action-platform login https://platform.example.com # browser opens, approve the code
472
+ action-platform mcp --remote # the MCP tools now act on that platform
473
+ ```
474
+
475
+ The TypeScript client is generated from the API's OpenAPI schema (`npm run api:types`): the Python response models in `action_platform/api/models.py` are the contract both sides compile against.
476
+
477
+ ## Releases: three artifacts, three versions
478
+
479
+ One repository, independent versions — declared in `platform.toml`:
480
+
481
+ ```toml
482
+ [components.web]
483
+ path = "apps/web"
484
+
485
+ [components.api]
486
+ path = "action_platform/api"
487
+ ```
488
+
489
+ | Command | Tag | Publishes |
490
+ |---|---|---|
491
+ | `action-platform release minor` | `v0.3.0` | `action-platform` on PyPI (library + CLI) |
492
+ | `action-platform release -c api patch` | `api/v0.1.1` | `actionplatform/action-platform-api` image |
493
+ | `action-platform release -c web minor` | `web/v0.2.0` | `actionplatform/action-platform-web` image |
494
+
495
+ Each component keeps its own `LAST_VERSION` and `CHANGELOG.md` under its path; its changelog lists only the commits that touched it, and the root's excludes them. Off `master` every release is an `-rc.N` pre-release. Images go to Docker Hub and are mirrored to GHCR.
496
+
497
+ ## Layout
498
+
499
+ ```
500
+ action_platform/
501
+ core/
502
+ manifest/ platform.toml: read, edit tables
503
+ scaffold/ templates matrix, generate, install into an existing repo
504
+ flow/ git, git-flow rules, branching, pull requests
505
+ release/ versioning, changelog, release, deploy
506
+ config.py · context.py · exception.py · action_platform.py (facade)
507
+ providers/
508
+ source/ github, gitlab, bitbucket, generic — repositories, releases, PRs
509
+ api/ FastAPI for the web app: apps registry, actions, per-request credentials
510
+ remote/ client + device-flow login for a hosted platform
511
+ mcp/ MCP server: local tools, or remote tools after login
512
+ cli/ Typer commands
513
+ apps/web/ the web app (see its README)
514
+ deploy/ Dockerfiles, docker-compose, install.sh
515
+ ```
516
+
427
517
  ## Extend it
428
518
 
429
519
  Deploy targets are plugins. Implement the `DeployTarget` contract — `preflight`, `create`, `deploy`, `switch_traffic`, `rollback`, `diagnose`, `delete` — publish it under the `action_platform.deploy_target` entry-point group, and `action-platform deploy` finds it by name.
430
520
 
431
521
  ```python
432
522
  from action_platform import ActionPlatform, Config
433
- from action_platform.providers import SourceGithub
523
+ from action_platform.providers.source.github import SourceGithub
434
524
 
435
525
  tool = ActionPlatform(config=Config(source_host=SourceGithub(repo="acme/orders")))
436
526
  tool.release("minor")
@@ -445,7 +535,6 @@ Templates are plain cookiecutters in [actionplatform/templates](https://github.c
445
535
  | [templates](https://github.com/actionplatform/templates) | projects, clouds, services |
446
536
  | [ci-scripts](https://github.com/actionplatform/ci-scripts) | the one implementation of setup / check / release / commit lint |
447
537
  | [ci-github](https://github.com/actionplatform/ci-github) · [ci-gitlab](https://github.com/actionplatform/ci-gitlab) · [ci-jenkins](https://github.com/actionplatform/ci-jenkins) | thin wrappers per CI |
448
- | [strategy](https://github.com/actionplatform/strategy) | why it is built this way |
449
538
 
450
539
  ## License
451
540
 
@@ -3,7 +3,7 @@
3
3
  </p>
4
4
 
5
5
  <p align="center">
6
- <strong>Your platform team, as a CLI.</strong>
6
+ <strong>Your platform team: a web app, a CLI and an MCP server on one core.</strong>
7
7
  </p>
8
8
 
9
9
  <p align="center">
@@ -15,7 +15,27 @@
15
15
 
16
16
  ---
17
17
 
18
- Every new service costs the same week: scaffold, lint config, CI, versioning, deploy pipeline, IAM. Then the next one drifts from the last. **Action Platform** turns that week into one command and keeps every project on the same rails — without a control plane to host, a UI to learn, or a vendor to trust with your cloud.
18
+ Every new service costs the same week: scaffold, lint config, CI, versioning, deploy pipeline, IAM. Then the next one drifts from the last. **Action Platform** turns that week into one command — or one click — and keeps every app on the same rails, in your git host and your cloud account.
19
+
20
+ Three doors, one core:
21
+
22
+ | | |
23
+ |---|---|
24
+ | **Web app** | Organizations › projects › apps. Create an app from a template, connect GitHub / GitLab / Bitbucket, cut releases, run deploys — from a browser. One command to self-host. |
25
+ | **CLI** | `pipx install action-platform` and the same verbs on your machine: `init`, `branch`, `pr`, `release`, `deploy`, `rollback`, `diagnose`. |
26
+ | **MCP server** | The same operations as tools for Claude Code, Codex, Cursor — locally, or against your hosted platform after `action-platform login`. |
27
+
28
+ ## Self-host in one command
29
+
30
+ ```bash
31
+ curl -fsSL https://raw.githubusercontent.com/actionplatform/action-platform/master/deploy/install.sh | sudo sh
32
+ # with a domain and TLS:
33
+ curl -fsSL https://raw.githubusercontent.com/actionplatform/action-platform/master/deploy/install.sh | sudo sh -s -- platform.example.com you@example.com
34
+ ```
35
+
36
+ Installs Docker if needed, generates the secrets, starts Postgres + API + web (+ Traefik with Let's Encrypt when a domain is given) and prints the URL. Open it: first account, first organization, connect a code host — done. Files live in `/opt/action-platform`; see [`deploy/`](deploy/) for the compose file and the two Dockerfiles.
37
+
38
+ ## Or just the CLI
19
39
 
20
40
  ```bash
21
41
  pipx install action-platform
@@ -32,7 +52,7 @@ Thirty seconds later you have a FastAPI service with tests, lint, CI wired, a SA
32
52
  | **One command, whole lifecycle** | `init` → `release` → `deploy` → `rollback` → `diagnose` → `destroy`. Same verbs for a Python API on Lambda, a Go service in Docker, a React app on Amplify. |
33
53
  | **Templates from production, not tutorials** | Every project template is extracted from a real shipping product. Real layout, real CI, real gotchas already fixed. |
34
54
  | **Cloud is a layer, not a fork** | Projects stay cloud-agnostic. `--cloud aws/lambda` overlays deploy files; swap to `docker` tomorrow with one command. |
35
- | **Your CI, your account, your git** | Runs on GitHub Actions, GitLab CI or Jenkins you already have. Infra lands in **your** AWS account through OIDC — no long-lived keys, no agent, no SaaS in the loop. |
55
+ | **Your CI, your account, your git** | Runs on GitHub Actions, GitLab CI or Jenkins you already have. Repositories on GitHub, GitLab, Bitbucket or any git server, connected with OAuth. Infra lands in **your** AWS account through OIDC — no long-lived keys, no vendor in the loop. |
36
56
  | **Governance that ships with the code** | Git-flow and Conventional Commits enforced by git hooks before a commit exists and by CI on every PR; changelog generated; `AGENTS.md` for humans and AI agents; Trivy scans; least-privilege IAM in `requirements/`. |
37
57
  | **Fix once, everywhere** | CI logic lives in versioned shared repos (`ci-scripts`, `ci-github`, `ci-gitlab`, `ci-jenkins`). Bump `v1`, every project picks it up. |
38
58
 
@@ -179,13 +199,80 @@ Any client — add to `.mcp.json`:
179
199
  { "mcpServers": { "action-platform": { "command": "uvx", "args": ["--from", "action-platform[mcp]", "action-platform-mcp"] } } }
180
200
  ```
181
201
 
202
+ ## The web app
203
+
204
+ [`apps/web`](apps/web) — Next.js on top of the Python API. **Organization › Project › App**: an organization is the tenant and owns its code hosts; a project groups the apps that ship together; an app is one git repository the platform clones, audits, releases and deploys.
205
+
206
+ - **Setup wizard** on first run: database (SQLite, PostgreSQL or MySQL), first account, first organization, code hosts.
207
+ - **Connect with GitHub / GitLab / Bitbucket** through OAuth (or paste a token). Tokens are encrypted at rest and refreshed when they expire.
208
+ - **Create project** wizard: type → stack → template → configure → review, with the equivalent `action-platform init` shown. Or add an existing repository by URL.
209
+ - Per app: git-flow audit, commits, branches, tags, release preview → confirm → publish, deploy preflight → confirm → ship.
210
+ - Strictly monochrome UI; every confirmation is an in-app dialog.
211
+
212
+ Local development:
213
+
214
+ ```bash
215
+ pip install "action-platform[api]"
216
+ action-platform api --reload # http://127.0.0.1:7788, OpenAPI at /docs
217
+ cd apps/web && npm install && npm run dev # http://localhost:3000 → /setup
218
+ ```
219
+
220
+ Drive a hosted platform from anywhere:
221
+
222
+ ```bash
223
+ action-platform login https://platform.example.com # browser opens, approve the code
224
+ action-platform mcp --remote # the MCP tools now act on that platform
225
+ ```
226
+
227
+ The TypeScript client is generated from the API's OpenAPI schema (`npm run api:types`): the Python response models in `action_platform/api/models.py` are the contract both sides compile against.
228
+
229
+ ## Releases: three artifacts, three versions
230
+
231
+ One repository, independent versions — declared in `platform.toml`:
232
+
233
+ ```toml
234
+ [components.web]
235
+ path = "apps/web"
236
+
237
+ [components.api]
238
+ path = "action_platform/api"
239
+ ```
240
+
241
+ | Command | Tag | Publishes |
242
+ |---|---|---|
243
+ | `action-platform release minor` | `v0.3.0` | `action-platform` on PyPI (library + CLI) |
244
+ | `action-platform release -c api patch` | `api/v0.1.1` | `actionplatform/action-platform-api` image |
245
+ | `action-platform release -c web minor` | `web/v0.2.0` | `actionplatform/action-platform-web` image |
246
+
247
+ Each component keeps its own `LAST_VERSION` and `CHANGELOG.md` under its path; its changelog lists only the commits that touched it, and the root's excludes them. Off `master` every release is an `-rc.N` pre-release. Images go to Docker Hub and are mirrored to GHCR.
248
+
249
+ ## Layout
250
+
251
+ ```
252
+ action_platform/
253
+ core/
254
+ manifest/ platform.toml: read, edit tables
255
+ scaffold/ templates matrix, generate, install into an existing repo
256
+ flow/ git, git-flow rules, branching, pull requests
257
+ release/ versioning, changelog, release, deploy
258
+ config.py · context.py · exception.py · action_platform.py (facade)
259
+ providers/
260
+ source/ github, gitlab, bitbucket, generic — repositories, releases, PRs
261
+ api/ FastAPI for the web app: apps registry, actions, per-request credentials
262
+ remote/ client + device-flow login for a hosted platform
263
+ mcp/ MCP server: local tools, or remote tools after login
264
+ cli/ Typer commands
265
+ apps/web/ the web app (see its README)
266
+ deploy/ Dockerfiles, docker-compose, install.sh
267
+ ```
268
+
182
269
  ## Extend it
183
270
 
184
271
  Deploy targets are plugins. Implement the `DeployTarget` contract — `preflight`, `create`, `deploy`, `switch_traffic`, `rollback`, `diagnose`, `delete` — publish it under the `action_platform.deploy_target` entry-point group, and `action-platform deploy` finds it by name.
185
272
 
186
273
  ```python
187
274
  from action_platform import ActionPlatform, Config
188
- from action_platform.providers import SourceGithub
275
+ from action_platform.providers.source.github import SourceGithub
189
276
 
190
277
  tool = ActionPlatform(config=Config(source_host=SourceGithub(repo="acme/orders")))
191
278
  tool.release("minor")
@@ -200,7 +287,6 @@ Templates are plain cookiecutters in [actionplatform/templates](https://github.c
200
287
  | [templates](https://github.com/actionplatform/templates) | projects, clouds, services |
201
288
  | [ci-scripts](https://github.com/actionplatform/ci-scripts) | the one implementation of setup / check / release / commit lint |
202
289
  | [ci-github](https://github.com/actionplatform/ci-github) · [ci-gitlab](https://github.com/actionplatform/ci-gitlab) · [ci-jenkins](https://github.com/actionplatform/ci-jenkins) | thin wrappers per CI |
203
- | [strategy](https://github.com/actionplatform/strategy) | why it is built this way |
204
290
 
205
291
  ## License
206
292
 
@@ -1,7 +1,7 @@
1
1
  """Action Platform __init__ module."""
2
2
 
3
- __version__ = "0.1.2"
4
- __description__ = "🛠️ Action Platform padroniza init, release e deploy."
3
+ __version__ = "0.3.0"
4
+ __description__ = "Your platform team, as a CLI."
5
5
 
6
6
  from .core.config import Config
7
7
  from .core.context import Context
@@ -0,0 +1 @@
1
+ """Local web UI: a JSON API over the core modules plus the bundled SPA."""
@@ -0,0 +1,87 @@
1
+ """Source-host credentials handed to the API per request by the web app.
2
+
3
+ Nothing is stored here: the web app keeps them (encrypted, per
4
+ organization) and sends them with the calls that need them — push,
5
+ release, pull request. While such a call runs, git authenticates with the
6
+ same token through a credential helper injected via the environment.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import os
12
+ from contextlib import contextmanager
13
+ from typing import Iterator, Optional
14
+
15
+ from pydantic import BaseModel
16
+
17
+ from action_platform.core.config import Config
18
+ from action_platform.providers.source import build_source_host
19
+
20
+ GIT_USERNAMES = {
21
+ "github": "x-access-token",
22
+ "gitlab": "oauth2",
23
+ "bitbucket": "x-token-auth",
24
+ }
25
+
26
+
27
+ class SourceCredentials(BaseModel):
28
+ kind: str
29
+ token: str
30
+ username: Optional[str] = None
31
+ base_url: Optional[str] = None
32
+ owner: Optional[str] = None
33
+
34
+
35
+ def apply(config: Config, creds: Optional[SourceCredentials]) -> Config:
36
+ """Rebuild config.source_host with the request's token instead of the environment's."""
37
+ if creds is None or config.source_host is None:
38
+ return config
39
+
40
+ config.source_host = build_source_host(
41
+ creds.kind,
42
+ config.source_host.repo,
43
+ base_url=creds.base_url,
44
+ token=creds.token,
45
+ username=creds.username,
46
+ )
47
+
48
+ return config
49
+
50
+
51
+ @contextmanager
52
+ def git_auth(creds: Optional[SourceCredentials]) -> Iterator[None]:
53
+ """Make every git subprocess in this block authenticate with `creds`.
54
+
55
+ Uses GIT_CONFIG_{COUNT,KEY,VALUE} (git ≥ 2.31) to replace the credential
56
+ helpers with one that answers from two environment variables, so the
57
+ token never lands in .git/config or on a command line — and the host's
58
+ own helpers (keychain, gh) never answer for the wrong account.
59
+ """
60
+ if creds is None:
61
+ yield
62
+ return
63
+
64
+ username = creds.username or GIT_USERNAMES.get(creds.kind, "git")
65
+ helper = '!f() { printf \'username=%s\\npassword=%s\\n\' "$AP_GIT_USER" "$AP_GIT_TOKEN"; }; f'
66
+ keys = {
67
+ "GIT_TERMINAL_PROMPT": "0",
68
+ "AP_GIT_USER": username,
69
+ "AP_GIT_TOKEN": creds.token,
70
+ "GIT_CONFIG_COUNT": "2",
71
+ # an empty value clears the helpers configured so far
72
+ "GIT_CONFIG_KEY_0": "credential.helper",
73
+ "GIT_CONFIG_VALUE_0": "",
74
+ "GIT_CONFIG_KEY_1": "credential.helper",
75
+ "GIT_CONFIG_VALUE_1": helper,
76
+ }
77
+ saved = {k: os.environ.get(k) for k in keys}
78
+ os.environ.update(keys)
79
+
80
+ try:
81
+ yield
82
+ finally:
83
+ for k, v in saved.items():
84
+ if v is None:
85
+ os.environ.pop(k, None)
86
+ else:
87
+ os.environ[k] = v
@@ -0,0 +1,177 @@
1
+ """Response models. They are the contract the web app's TypeScript client is generated from."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Optional
6
+
7
+ from pydantic import BaseModel
8
+
9
+
10
+ class Version(BaseModel):
11
+ version: str
12
+
13
+
14
+ class MatrixProject(BaseModel):
15
+ type: str
16
+ stack: str
17
+ template: str
18
+ default: bool
19
+ description: str
20
+
21
+
22
+ class MatrixCloud(BaseModel):
23
+ name: str
24
+ types: list[str]
25
+ languages: list[str]
26
+ description: str
27
+
28
+
29
+ class MatrixService(BaseModel):
30
+ name: str
31
+ providers: list[str]
32
+ description: str
33
+
34
+
35
+ class Matrix(BaseModel):
36
+ projects: list[MatrixProject]
37
+ clouds: list[MatrixCloud]
38
+ services: list[MatrixService]
39
+
40
+
41
+ class GitflowRules(BaseModel):
42
+ kinds: list[str]
43
+ protected: list[str]
44
+ types: list[str]
45
+
46
+
47
+ class AppRow(BaseModel):
48
+ id: str
49
+ name: str
50
+ url: str
51
+ exists: bool
52
+ language: Optional[str] = None
53
+ type: Optional[str] = None
54
+ last_version: Optional[str] = None
55
+ branch: Optional[str] = None
56
+
57
+
58
+ class AppEntry(BaseModel):
59
+ id: str
60
+ name: str
61
+ url: str
62
+ path: str
63
+ default_branch: str = ""
64
+
65
+
66
+ class AppMeta(BaseModel):
67
+ name: str = ""
68
+ type: Optional[str] = None
69
+ stack: Optional[str] = None
70
+ template: Optional[str] = None
71
+ language: Optional[str] = None
72
+ ci: Optional[str] = None
73
+
74
+
75
+ class AppDetail(BaseModel):
76
+ id: str
77
+ url: str
78
+ default_branch: str = ""
79
+ project: AppMeta
80
+ source_host: dict[str, str]
81
+ deploy: dict
82
+ release: dict[str, str]
83
+ services: dict
84
+ last_version: Optional[str] = None
85
+ branch: str
86
+ latest_tag: Optional[str] = None
87
+ clean: bool
88
+
89
+
90
+ class GitflowReport(BaseModel):
91
+ branch: str
92
+ problems: list[str]
93
+ checked_commits: int
94
+ ok: bool
95
+
96
+
97
+ class Commit(BaseModel):
98
+ sha: str
99
+ subject: str
100
+ author: str
101
+ date: str
102
+
103
+
104
+ class Branch(BaseModel):
105
+ name: str
106
+ date: str
107
+ kind: Optional[str] = None
108
+ protected: bool
109
+ problem: Optional[str] = None
110
+
111
+
112
+ class ReleasePreview(BaseModel):
113
+ current: str
114
+ next: str
115
+ changelog: str
116
+ dry_run: bool
117
+
118
+
119
+ class DeployResult(BaseModel):
120
+ target: str
121
+ ok: bool
122
+ version: str
123
+ url: Optional[str] = None
124
+ error: Optional[str] = None
125
+
126
+
127
+ class Diagnosis(BaseModel):
128
+ ok: bool
129
+ target: str
130
+ status: str = ""
131
+ version: Optional[str] = None
132
+ url: Optional[str] = None
133
+ details: dict[str, str] = {}
134
+
135
+
136
+ class SourceCredentials(BaseModel):
137
+ kind: str
138
+ token: str
139
+ username: Optional[str] = None
140
+ base_url: Optional[str] = None
141
+ owner: Optional[str] = None
142
+
143
+
144
+ class InitRequest(BaseModel):
145
+ type: str
146
+ stack: Optional[str] = None
147
+ template: Optional[str] = None
148
+ name: str
149
+ description: str = ""
150
+ package_name: Optional[str] = None
151
+ github_owner: Optional[str] = None
152
+ ci: Optional[str] = None
153
+ cloud: Optional[str] = None
154
+ git_init: bool = True
155
+ push: bool = False
156
+ private: bool = False
157
+ credentials: Optional[SourceCredentials] = None
158
+
159
+
160
+ class InitResult(BaseModel):
161
+ id: str
162
+ name: str
163
+ path: str
164
+ url: str
165
+ template: str
166
+ cloud: Optional[str] = None
167
+ pushed: bool
168
+
169
+
170
+ class PushRequest(BaseModel):
171
+ private: bool = False
172
+ credentials: Optional[SourceCredentials] = None
173
+
174
+
175
+ class PushResult(BaseModel):
176
+ id: str
177
+ url: str