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.
- {action_platform-0.2.0 → action_platform-0.3.0}/PKG-INFO +95 -6
- {action_platform-0.2.0 → action_platform-0.3.0}/README.md +91 -5
- {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/__init__.py +2 -2
- action_platform-0.3.0/action_platform/api/LAST_VERSION +1 -0
- action_platform-0.3.0/action_platform/api/__init__.py +1 -0
- action_platform-0.3.0/action_platform/api/credentials.py +87 -0
- action_platform-0.3.0/action_platform/api/models.py +177 -0
- action_platform-0.3.0/action_platform/api/registry.py +167 -0
- action_platform-0.3.0/action_platform/api/server.py +436 -0
- action_platform-0.3.0/action_platform/cli/commands/api.py +32 -0
- {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/cli/commands/branch.py +1 -1
- {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/cli/commands/cloud.py +2 -2
- {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/cli/commands/gitflow.py +1 -1
- {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/cli/commands/init.py +6 -2
- {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/cli/commands/install.py +1 -1
- action_platform-0.3.0/action_platform/cli/commands/login.py +39 -0
- {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/cli/commands/mcp.py +9 -0
- {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/cli/commands/pr.py +1 -1
- {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/cli/commands/release.py +7 -1
- {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/cli/commands/service.py +2 -2
- {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/cli/setup.py +6 -0
- action_platform-0.3.0/action_platform/core/__init__.py +8 -0
- {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/core/action_platform.py +14 -7
- {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/core/config.py +11 -5
- action_platform-0.3.0/action_platform/core/flow/__init__.py +1 -0
- {action_platform-0.2.0/action_platform/core → action_platform-0.3.0/action_platform/core/flow}/branching.py +1 -1
- {action_platform-0.2.0/action_platform/core → action_platform-0.3.0/action_platform/core/flow}/git.py +18 -4
- {action_platform-0.2.0/action_platform/core → action_platform-0.3.0/action_platform/core/flow}/gitflow.py +2 -2
- {action_platform-0.2.0/action_platform/core → action_platform-0.3.0/action_platform/core/flow}/pullrequest.py +3 -2
- action_platform-0.3.0/action_platform/core/release/__init__.py +1 -0
- action_platform-0.3.0/action_platform/core/release/components.py +78 -0
- action_platform-0.3.0/action_platform/core/release/deploy.py +91 -0
- action_platform-0.3.0/action_platform/core/release/release.py +135 -0
- action_platform-0.3.0/action_platform/core/scaffold/__init__.py +1 -0
- {action_platform-0.2.0/action_platform/core → action_platform-0.3.0/action_platform/core/scaffold}/generate.py +43 -69
- {action_platform-0.2.0/action_platform/core → action_platform-0.3.0/action_platform/core/scaffold}/install.py +2 -2
- {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/mcp/server.py +37 -3
- {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/mcp/tools/flow.py +6 -2
- {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/mcp/tools/lifecycle.py +7 -1
- {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/mcp/tools/matrix.py +1 -1
- {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/mcp/tools/project.py +3 -3
- action_platform-0.3.0/action_platform/mcp/tools/remote.py +109 -0
- action_platform-0.3.0/action_platform/providers/__init__.py +9 -0
- action_platform-0.3.0/action_platform/providers/source/__init__.py +48 -0
- action_platform-0.3.0/action_platform/providers/source/bitbucket.py +111 -0
- action_platform-0.3.0/action_platform/providers/source/generic.py +61 -0
- action_platform-0.3.0/action_platform/providers/source/github.py +204 -0
- action_platform-0.3.0/action_platform/providers/source/gitlab.py +126 -0
- action_platform-0.3.0/action_platform/providers/source/rest.py +58 -0
- action_platform-0.3.0/action_platform/remote/__init__.py +1 -0
- action_platform-0.3.0/action_platform/remote/client.py +216 -0
- action_platform-0.3.0/action_platform/remote/credentials.py +58 -0
- {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/settings.py +2 -0
- {action_platform-0.2.0 → action_platform-0.3.0}/pyproject.toml +10 -3
- action_platform-0.2.0/action_platform/core/__init__.py +0 -1
- action_platform-0.2.0/action_platform/core/pipeline.py +0 -192
- action_platform-0.2.0/action_platform/providers/__init__.py +0 -5
- action_platform-0.2.0/action_platform/providers/source_github.py +0 -122
- {action_platform-0.2.0 → action_platform-0.3.0}/LICENSE +0 -0
- {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/abc/__init__.py +0 -0
- {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/abc/ci_runner.py +0 -0
- {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/abc/deploy_target.py +0 -0
- {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/abc/source_host.py +0 -0
- {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/cli/__init__.py +0 -0
- {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/cli/commands/__init__.py +0 -0
- {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/cli/commands/deploy.py +0 -0
- {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/core/context.py +0 -0
- {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/core/exception.py +0 -0
- {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/core/module.py +0 -0
- {action_platform-0.2.0/action_platform/core → action_platform-0.3.0/action_platform/core/release}/changelog.py +0 -0
- {action_platform-0.2.0/action_platform/core → action_platform-0.3.0/action_platform/core/release}/versioning.py +0 -0
- {action_platform-0.2.0/action_platform/core → action_platform-0.3.0/action_platform/core/scaffold}/templates.py +0 -0
- {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/hooks/__init__.py +0 -0
- {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/hooks/commit-msg +0 -0
- {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/hooks/gitflow.sh +0 -0
- {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/hooks/pre-commit +0 -0
- {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/hooks/pre-push +0 -0
- {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/logging.py +0 -0
- {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/main.py +0 -0
- {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/mcp/__init__.py +0 -0
- {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/mcp/__main__.py +0 -0
- {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/mcp/annotations.py +0 -0
- {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/mcp/prompts.py +0 -0
- {action_platform-0.2.0 → action_platform-0.3.0}/action_platform/mcp/tools/__init__.py +0 -0
- {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.
|
|
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,
|
|
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
|
|
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
|
|
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,
|
|
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
|
|
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
|
|
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.
|
|
4
|
-
__description__ = "
|
|
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
|
+
0.1.0
|
|
@@ -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
|