action-platform 0.1.2__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.1.2 → action_platform-0.3.0}/PKG-INFO +150 -9
- action_platform-0.3.0/README.md +293 -0
- {action_platform-0.1.2 → 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.1.2 → action_platform-0.3.0}/action_platform/cli/commands/branch.py +1 -1
- {action_platform-0.1.2 → action_platform-0.3.0}/action_platform/cli/commands/cloud.py +2 -2
- {action_platform-0.1.2 → action_platform-0.3.0}/action_platform/cli/commands/gitflow.py +1 -1
- {action_platform-0.1.2 → action_platform-0.3.0}/action_platform/cli/commands/init.py +6 -2
- {action_platform-0.1.2 → action_platform-0.3.0}/action_platform/cli/commands/install.py +6 -2
- action_platform-0.3.0/action_platform/cli/commands/login.py +39 -0
- {action_platform-0.1.2 → action_platform-0.3.0}/action_platform/cli/commands/mcp.py +9 -0
- {action_platform-0.1.2 → action_platform-0.3.0}/action_platform/cli/commands/pr.py +1 -1
- {action_platform-0.1.2 → action_platform-0.3.0}/action_platform/cli/commands/release.py +13 -2
- {action_platform-0.1.2 → action_platform-0.3.0}/action_platform/cli/commands/service.py +2 -2
- {action_platform-0.1.2 → 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.1.2 → action_platform-0.3.0}/action_platform/core/action_platform.py +21 -7
- {action_platform-0.1.2 → 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.1.2/action_platform/core → action_platform-0.3.0/action_platform/core/flow}/branching.py +1 -1
- {action_platform-0.1.2/action_platform/core → action_platform-0.3.0/action_platform/core/flow}/git.py +35 -4
- {action_platform-0.1.2/action_platform/core → action_platform-0.3.0/action_platform/core/flow}/gitflow.py +2 -2
- {action_platform-0.1.2/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.1.2/action_platform/core/pipeline.py → action_platform-0.3.0/action_platform/core/release/deploy.py +4 -64
- action_platform-0.3.0/action_platform/core/release/release.py +135 -0
- action_platform-0.3.0/action_platform/core/release/versioning.py +98 -0
- action_platform-0.3.0/action_platform/core/scaffold/__init__.py +1 -0
- {action_platform-0.1.2/action_platform/core → action_platform-0.3.0/action_platform/core/scaffold}/generate.py +43 -69
- {action_platform-0.1.2/action_platform/core → action_platform-0.3.0/action_platform/core/scaffold}/install.py +23 -8
- action_platform-0.3.0/action_platform/mcp/prompts.py +136 -0
- {action_platform-0.1.2 → action_platform-0.3.0}/action_platform/mcp/server.py +39 -3
- {action_platform-0.1.2 → action_platform-0.3.0}/action_platform/mcp/tools/flow.py +6 -2
- {action_platform-0.1.2 → action_platform-0.3.0}/action_platform/mcp/tools/lifecycle.py +9 -2
- {action_platform-0.1.2 → action_platform-0.3.0}/action_platform/mcp/tools/matrix.py +1 -1
- {action_platform-0.1.2 → 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.1.2 → action_platform-0.3.0}/action_platform/settings.py +2 -0
- {action_platform-0.1.2 → action_platform-0.3.0}/pyproject.toml +12 -5
- action_platform-0.1.2/README.md +0 -155
- action_platform-0.1.2/action_platform/core/__init__.py +0 -1
- action_platform-0.1.2/action_platform/core/versioning.py +0 -43
- action_platform-0.1.2/action_platform/providers/__init__.py +0 -5
- action_platform-0.1.2/action_platform/providers/source_github.py +0 -122
- {action_platform-0.1.2 → action_platform-0.3.0}/LICENSE +0 -0
- {action_platform-0.1.2 → action_platform-0.3.0}/action_platform/abc/__init__.py +0 -0
- {action_platform-0.1.2 → action_platform-0.3.0}/action_platform/abc/ci_runner.py +0 -0
- {action_platform-0.1.2 → action_platform-0.3.0}/action_platform/abc/deploy_target.py +0 -0
- {action_platform-0.1.2 → action_platform-0.3.0}/action_platform/abc/source_host.py +0 -0
- {action_platform-0.1.2 → action_platform-0.3.0}/action_platform/cli/__init__.py +0 -0
- {action_platform-0.1.2 → action_platform-0.3.0}/action_platform/cli/commands/__init__.py +0 -0
- {action_platform-0.1.2 → action_platform-0.3.0}/action_platform/cli/commands/deploy.py +0 -0
- {action_platform-0.1.2 → action_platform-0.3.0}/action_platform/core/context.py +0 -0
- {action_platform-0.1.2 → action_platform-0.3.0}/action_platform/core/exception.py +0 -0
- {action_platform-0.1.2 → action_platform-0.3.0}/action_platform/core/module.py +0 -0
- {action_platform-0.1.2/action_platform/core → action_platform-0.3.0/action_platform/core/release}/changelog.py +0 -0
- {action_platform-0.1.2/action_platform/core → action_platform-0.3.0/action_platform/core/scaffold}/templates.py +0 -0
- {action_platform-0.1.2 → action_platform-0.3.0}/action_platform/hooks/__init__.py +0 -0
- {action_platform-0.1.2 → action_platform-0.3.0}/action_platform/hooks/commit-msg +0 -0
- {action_platform-0.1.2 → action_platform-0.3.0}/action_platform/hooks/gitflow.sh +0 -0
- {action_platform-0.1.2 → action_platform-0.3.0}/action_platform/hooks/pre-commit +0 -0
- {action_platform-0.1.2 → action_platform-0.3.0}/action_platform/hooks/pre-push +0 -0
- {action_platform-0.1.2 → action_platform-0.3.0}/action_platform/logging.py +0 -0
- {action_platform-0.1.2 → action_platform-0.3.0}/action_platform/main.py +0 -0
- {action_platform-0.1.2 → action_platform-0.3.0}/action_platform/mcp/__init__.py +0 -0
- {action_platform-0.1.2 → action_platform-0.3.0}/action_platform/mcp/__main__.py +0 -0
- {action_platform-0.1.2 → action_platform-0.3.0}/action_platform/mcp/annotations.py +0 -0
- {action_platform-0.1.2 → action_platform-0.3.0}/action_platform/mcp/tools/__init__.py +0 -0
- {action_platform-0.1.2 → action_platform-0.3.0}/action_platform/testing/__init__.py +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: action-platform
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary:
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: Your platform team, as a CLI.
|
|
5
5
|
License: Apache License
|
|
6
6
|
Version 2.0, January 2004
|
|
7
7
|
http://www.apache.org/licenses/
|
|
@@ -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>
|
|
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
|
|
|
@@ -321,13 +344,64 @@ action-platform branch feature 42 login # develop (or main) → pull
|
|
|
321
344
|
action-platform branch hotfix PROJ-7 # from main/master
|
|
322
345
|
action-platform gitflow # audit current branch + commits; --install-hooks
|
|
323
346
|
action-platform pr [--draft] [--dry-run] # PR for the current branch: target from git-flow, body from commits
|
|
324
|
-
action-platform release patch # bump, changelog, tag, GitHub release
|
|
347
|
+
action-platform release patch # bump, changelog, tag, GitHub release; off main/master → X.Y.Z-rc.N pre-release
|
|
325
348
|
action-platform deploy --stage prod
|
|
326
349
|
action-platform rollback
|
|
327
350
|
action-platform diagnose
|
|
328
351
|
action-platform destroy
|
|
329
352
|
```
|
|
330
353
|
|
|
354
|
+
## Git-flow, enforced
|
|
355
|
+
|
|
356
|
+
Every project follows the same flow. Git hooks refuse the wrong move before it exists; CI refuses it on the pull request; the CLI and the MCP tools guide the right one.
|
|
357
|
+
|
|
358
|
+
```mermaid
|
|
359
|
+
gitGraph
|
|
360
|
+
commit id: "chore: bootstrap"
|
|
361
|
+
branch develop
|
|
362
|
+
checkout develop
|
|
363
|
+
commit id: "chore(release): 0.3.2-rc.1" tag: "v0.3.2-rc.1"
|
|
364
|
+
branch feature/42-login
|
|
365
|
+
checkout feature/42-login
|
|
366
|
+
commit id: "feat(login): form"
|
|
367
|
+
commit id: "test(login): cover form"
|
|
368
|
+
checkout develop
|
|
369
|
+
merge feature/42-login id: "PR #1 → develop"
|
|
370
|
+
branch release/0.3.2
|
|
371
|
+
checkout release/0.3.2
|
|
372
|
+
commit id: "chore(release): 0.3.2-rc.2" tag: "v0.3.2-rc.2"
|
|
373
|
+
checkout main
|
|
374
|
+
merge release/0.3.2 id: "PR #2 → main"
|
|
375
|
+
commit id: "chore(release): 0.3.2" tag: "v0.3.2"
|
|
376
|
+
checkout develop
|
|
377
|
+
merge main id: "back-merge"
|
|
378
|
+
checkout main
|
|
379
|
+
branch hotfix/PROJ-7
|
|
380
|
+
checkout hotfix/PROJ-7
|
|
381
|
+
commit id: "fix(auth): expiry"
|
|
382
|
+
checkout main
|
|
383
|
+
merge hotfix/PROJ-7 id: "PR #3 → main"
|
|
384
|
+
commit id: "chore(release): 0.3.3" tag: "v0.3.3"
|
|
385
|
+
checkout develop
|
|
386
|
+
merge main id: "back-merge hotfix"
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
| Branch | Starts from | Merges into | Release |
|
|
390
|
+
|--------|-------------|-------------|---------|
|
|
391
|
+
| `feature/<code>`, `bugfix/…`, `chore/…`, `docs/…`, `refactor/…`, `test/…`, `ci/…`, `perf/…` | `develop` (or the default branch when there is no `develop`) | `develop` | `X.Y.Z-rc.N` pre-release |
|
|
392
|
+
| `release/<version>` | `develop` | `main` and `develop` | `X.Y.Z-rc.N` until merged |
|
|
393
|
+
| `hotfix/<code>` | `main` | `main` and `develop` | `X.Y.Z-rc.N` until merged |
|
|
394
|
+
| `main` / `master` | — | — | stable `X.Y.Z` → PyPI / npm / … |
|
|
395
|
+
|
|
396
|
+
Rules the hooks and CI apply: branch names are `<kind>/<code>[-slug]`; commits are [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/); no direct commits on `main`, `master` or `develop` except `chore(release):`, `chore(platform):` and the bootstrap commit; a pull request may only target what the table allows.
|
|
397
|
+
|
|
398
|
+
```bash
|
|
399
|
+
action-platform branch feature 42 login # develop → pull → feature/42-login → push
|
|
400
|
+
action-platform gitflow # audit branch + commits
|
|
401
|
+
action-platform pr # target and body from the rules and the commits
|
|
402
|
+
action-platform release patch # rc off main, stable on main
|
|
403
|
+
```
|
|
404
|
+
|
|
331
405
|
Everything a project needs is declared in one file:
|
|
332
406
|
|
|
333
407
|
```toml
|
|
@@ -336,6 +410,7 @@ name = "orders"
|
|
|
336
410
|
type = "web"
|
|
337
411
|
stack = "python"
|
|
338
412
|
template = "fastapi"
|
|
413
|
+
ci = "github"
|
|
339
414
|
language = "python"
|
|
340
415
|
|
|
341
416
|
[source_host]
|
|
@@ -351,7 +426,7 @@ postgres = "aws-rds"
|
|
|
351
426
|
|
|
352
427
|
## Use it from an AI client
|
|
353
428
|
|
|
354
|
-
The platform ships as an MCP server. Claude Code, Codex, Cursor — anything that speaks MCP — gets `list_matrix`, `init_project`, `
|
|
429
|
+
The platform ships as an MCP server. Claude Code, Codex, Cursor — anything that speaks MCP — gets 17 tools (`list_matrix`, `init_project`, `install_platform`, `start_branch`, `gitflow_audit`, `propose_pull_request`, `release`, `deploy`, `diagnose`, …), 6 prompts that put them in the right order (`new_service`, `ship_feature`, `cut_release`, `deploy_project`, `adopt_repository`, `fix_gitflow`) and 12 skills that make the agent preview and ask before anything leaves the machine.
|
|
355
430
|
|
|
356
431
|
```bash
|
|
357
432
|
pip install "action-platform[mcp]"
|
|
@@ -372,13 +447,80 @@ Any client — add to `.mcp.json`:
|
|
|
372
447
|
{ "mcpServers": { "action-platform": { "command": "uvx", "args": ["--from", "action-platform[mcp]", "action-platform-mcp"] } } }
|
|
373
448
|
```
|
|
374
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
|
+
|
|
375
517
|
## Extend it
|
|
376
518
|
|
|
377
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.
|
|
378
520
|
|
|
379
521
|
```python
|
|
380
522
|
from action_platform import ActionPlatform, Config
|
|
381
|
-
from action_platform.providers import SourceGithub
|
|
523
|
+
from action_platform.providers.source.github import SourceGithub
|
|
382
524
|
|
|
383
525
|
tool = ActionPlatform(config=Config(source_host=SourceGithub(repo="acme/orders")))
|
|
384
526
|
tool.release("minor")
|
|
@@ -393,7 +535,6 @@ Templates are plain cookiecutters in [actionplatform/templates](https://github.c
|
|
|
393
535
|
| [templates](https://github.com/actionplatform/templates) | projects, clouds, services |
|
|
394
536
|
| [ci-scripts](https://github.com/actionplatform/ci-scripts) | the one implementation of setup / check / release / commit lint |
|
|
395
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 |
|
|
396
|
-
| [strategy](https://github.com/actionplatform/strategy) | why it is built this way |
|
|
397
538
|
|
|
398
539
|
## License
|
|
399
540
|
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/actionplatform/action-platform/master/docs/assets/cover.png" alt="Action Platform" width="100%">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<strong>Your platform team: a web app, a CLI and an MCP server on one core.</strong>
|
|
7
|
+
</p>
|
|
8
|
+
|
|
9
|
+
<p align="center">
|
|
10
|
+
<a href="https://pypi.org/project/action-platform/"><img alt="PyPI" src="https://img.shields.io/pypi/v/action-platform?color=2ea44f"></a>
|
|
11
|
+
<a href="https://pypi.org/project/action-platform/"><img alt="Python" src="https://img.shields.io/pypi/pyversions/action-platform"></a>
|
|
12
|
+
<a href="LICENSE"><img alt="License" src="https://img.shields.io/badge/license-Apache--2.0-blue"></a>
|
|
13
|
+
<a href="https://github.com/actionplatform/templates"><img alt="Templates" src="https://img.shields.io/badge/templates-13%20projects%20%C2%B7%203%20clouds-6f42c1"></a>
|
|
14
|
+
</p>
|
|
15
|
+
|
|
16
|
+
---
|
|
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 — 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
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
pipx install action-platform
|
|
42
|
+
|
|
43
|
+
action-platform init web python fastapi --name "orders" --cloud aws/lambda
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Thirty seconds later you have a FastAPI service with tests, lint, CI wired, a SAM template, a least-privilege IAM policy, and a GitHub repo already pushed (`--no-push` to keep it local).
|
|
47
|
+
|
|
48
|
+
## Why teams pick it
|
|
49
|
+
|
|
50
|
+
| | |
|
|
51
|
+
|---|---|
|
|
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. |
|
|
53
|
+
| **Templates from production, not tutorials** | Every project template is extracted from a real shipping product. Real layout, real CI, real gotchas already fixed. |
|
|
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. |
|
|
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. |
|
|
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/`. |
|
|
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. |
|
|
58
|
+
|
|
59
|
+
## What you get
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
action-platform init --list
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
| Type | Stacks | Ready with |
|
|
66
|
+
|------|--------|-----------|
|
|
67
|
+
| `web` | python (FastAPI, FastMCP), go (Gin), node (React) | `/ping`, versioned API, tests, lint, CI |
|
|
68
|
+
| `library` | python, go, php, node, java, rust | packaging, version test, publish workflow |
|
|
69
|
+
| `docs` | mkdocs | Material theme, strict build in CI |
|
|
70
|
+
| `plugin` | chrome | Manifest V3, popup, background, tests, store zip |
|
|
71
|
+
| `empty` | — | `platform.toml` + code quality only |
|
|
72
|
+
|
|
73
|
+
| Cloud | Adds |
|
|
74
|
+
|-------|------|
|
|
75
|
+
| `aws/lambda` | SAM template, HTTP API, custom domain, deploy workflow, IAM policy |
|
|
76
|
+
| `aws/amplify` | `amplify.yml`, security headers, start-job workflow, IAM policy |
|
|
77
|
+
| `docker` | Dockerfile per language, compose |
|
|
78
|
+
|
|
79
|
+
| Service | Providers |
|
|
80
|
+
|---------|-----------|
|
|
81
|
+
| `postgres` | docker (local), aws-rds (Terraform/OpenTofu + SSM) |
|
|
82
|
+
|
|
83
|
+
## Commands
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
action-platform init # interactive: type → stack → template → name → ci
|
|
87
|
+
action-platform init web go gin --ci gitlab # direct
|
|
88
|
+
action-platform init web python --cloud docker # project + deploy overlay
|
|
89
|
+
action-platform init ... --no-push # skip creating the remote repo
|
|
90
|
+
action-platform install [--dry-run] # existing repo: platform.toml, hooks, code quality, CI — never overwrites
|
|
91
|
+
|
|
92
|
+
action-platform cloud set aws/lambda # add or switch the deploy target
|
|
93
|
+
action-platform service add postgres --provider aws-rds
|
|
94
|
+
|
|
95
|
+
action-platform branch feature 42 login # develop (or main) → pull → feature/42-login → push
|
|
96
|
+
action-platform branch hotfix PROJ-7 # from main/master
|
|
97
|
+
action-platform gitflow # audit current branch + commits; --install-hooks
|
|
98
|
+
action-platform pr [--draft] [--dry-run] # PR for the current branch: target from git-flow, body from commits
|
|
99
|
+
action-platform release patch # bump, changelog, tag, GitHub release; off main/master → X.Y.Z-rc.N pre-release
|
|
100
|
+
action-platform deploy --stage prod
|
|
101
|
+
action-platform rollback
|
|
102
|
+
action-platform diagnose
|
|
103
|
+
action-platform destroy
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Git-flow, enforced
|
|
107
|
+
|
|
108
|
+
Every project follows the same flow. Git hooks refuse the wrong move before it exists; CI refuses it on the pull request; the CLI and the MCP tools guide the right one.
|
|
109
|
+
|
|
110
|
+
```mermaid
|
|
111
|
+
gitGraph
|
|
112
|
+
commit id: "chore: bootstrap"
|
|
113
|
+
branch develop
|
|
114
|
+
checkout develop
|
|
115
|
+
commit id: "chore(release): 0.3.2-rc.1" tag: "v0.3.2-rc.1"
|
|
116
|
+
branch feature/42-login
|
|
117
|
+
checkout feature/42-login
|
|
118
|
+
commit id: "feat(login): form"
|
|
119
|
+
commit id: "test(login): cover form"
|
|
120
|
+
checkout develop
|
|
121
|
+
merge feature/42-login id: "PR #1 → develop"
|
|
122
|
+
branch release/0.3.2
|
|
123
|
+
checkout release/0.3.2
|
|
124
|
+
commit id: "chore(release): 0.3.2-rc.2" tag: "v0.3.2-rc.2"
|
|
125
|
+
checkout main
|
|
126
|
+
merge release/0.3.2 id: "PR #2 → main"
|
|
127
|
+
commit id: "chore(release): 0.3.2" tag: "v0.3.2"
|
|
128
|
+
checkout develop
|
|
129
|
+
merge main id: "back-merge"
|
|
130
|
+
checkout main
|
|
131
|
+
branch hotfix/PROJ-7
|
|
132
|
+
checkout hotfix/PROJ-7
|
|
133
|
+
commit id: "fix(auth): expiry"
|
|
134
|
+
checkout main
|
|
135
|
+
merge hotfix/PROJ-7 id: "PR #3 → main"
|
|
136
|
+
commit id: "chore(release): 0.3.3" tag: "v0.3.3"
|
|
137
|
+
checkout develop
|
|
138
|
+
merge main id: "back-merge hotfix"
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
| Branch | Starts from | Merges into | Release |
|
|
142
|
+
|--------|-------------|-------------|---------|
|
|
143
|
+
| `feature/<code>`, `bugfix/…`, `chore/…`, `docs/…`, `refactor/…`, `test/…`, `ci/…`, `perf/…` | `develop` (or the default branch when there is no `develop`) | `develop` | `X.Y.Z-rc.N` pre-release |
|
|
144
|
+
| `release/<version>` | `develop` | `main` and `develop` | `X.Y.Z-rc.N` until merged |
|
|
145
|
+
| `hotfix/<code>` | `main` | `main` and `develop` | `X.Y.Z-rc.N` until merged |
|
|
146
|
+
| `main` / `master` | — | — | stable `X.Y.Z` → PyPI / npm / … |
|
|
147
|
+
|
|
148
|
+
Rules the hooks and CI apply: branch names are `<kind>/<code>[-slug]`; commits are [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/); no direct commits on `main`, `master` or `develop` except `chore(release):`, `chore(platform):` and the bootstrap commit; a pull request may only target what the table allows.
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
action-platform branch feature 42 login # develop → pull → feature/42-login → push
|
|
152
|
+
action-platform gitflow # audit branch + commits
|
|
153
|
+
action-platform pr # target and body from the rules and the commits
|
|
154
|
+
action-platform release patch # rc off main, stable on main
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Everything a project needs is declared in one file:
|
|
158
|
+
|
|
159
|
+
```toml
|
|
160
|
+
[project]
|
|
161
|
+
name = "orders"
|
|
162
|
+
type = "web"
|
|
163
|
+
stack = "python"
|
|
164
|
+
template = "fastapi"
|
|
165
|
+
ci = "github"
|
|
166
|
+
language = "python"
|
|
167
|
+
|
|
168
|
+
[source_host]
|
|
169
|
+
kind = "github"
|
|
170
|
+
repo = "acme/orders"
|
|
171
|
+
|
|
172
|
+
[deploy]
|
|
173
|
+
target = "aws/lambda"
|
|
174
|
+
|
|
175
|
+
[services]
|
|
176
|
+
postgres = "aws-rds"
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## Use it from an AI client
|
|
180
|
+
|
|
181
|
+
The platform ships as an MCP server. Claude Code, Codex, Cursor — anything that speaks MCP — gets 17 tools (`list_matrix`, `init_project`, `install_platform`, `start_branch`, `gitflow_audit`, `propose_pull_request`, `release`, `deploy`, `diagnose`, …), 6 prompts that put them in the right order (`new_service`, `ship_feature`, `cut_release`, `deploy_project`, `adopt_repository`, `fix_gitflow`) and 12 skills that make the agent preview and ask before anything leaves the machine.
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
pip install "action-platform[mcp]"
|
|
185
|
+
action-platform mcp # stdio
|
|
186
|
+
action-platform mcp --http # http://127.0.0.1:8765/mcp
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Claude Code:
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
/plugin marketplace add actionplatform/action-platform
|
|
193
|
+
/plugin install action-platform@action-platform
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Any client — add to `.mcp.json`:
|
|
197
|
+
|
|
198
|
+
```json
|
|
199
|
+
{ "mcpServers": { "action-platform": { "command": "uvx", "args": ["--from", "action-platform[mcp]", "action-platform-mcp"] } } }
|
|
200
|
+
```
|
|
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
|
+
|
|
269
|
+
## Extend it
|
|
270
|
+
|
|
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.
|
|
272
|
+
|
|
273
|
+
```python
|
|
274
|
+
from action_platform import ActionPlatform, Config
|
|
275
|
+
from action_platform.providers.source.github import SourceGithub
|
|
276
|
+
|
|
277
|
+
tool = ActionPlatform(config=Config(source_host=SourceGithub(repo="acme/orders")))
|
|
278
|
+
tool.release("minor")
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
Templates are plain cookiecutters in [actionplatform/templates](https://github.com/actionplatform/templates). Add a stack or a cloud with a pull request — the CLI reads `index.toml`, nothing to redeploy. Point `ACTION_PLATFORM_TEMPLATES` at a local checkout while you work on them.
|
|
282
|
+
|
|
283
|
+
## Ecosystem
|
|
284
|
+
|
|
285
|
+
| Repo | Role |
|
|
286
|
+
|------|------|
|
|
287
|
+
| [templates](https://github.com/actionplatform/templates) | projects, clouds, services |
|
|
288
|
+
| [ci-scripts](https://github.com/actionplatform/ci-scripts) | the one implementation of setup / check / release / commit lint |
|
|
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 |
|
|
290
|
+
|
|
291
|
+
## License
|
|
292
|
+
|
|
293
|
+
Apache 2.0.
|
|
@@ -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
|