action-platform 0.1.0__py3-none-any.whl

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 (59) hide show
  1. action_platform/__init__.py +10 -0
  2. action_platform/abc/__init__.py +7 -0
  3. action_platform/abc/ci_runner.py +25 -0
  4. action_platform/abc/deploy_target.py +46 -0
  5. action_platform/abc/source_host.py +52 -0
  6. action_platform/cli/__init__.py +1 -0
  7. action_platform/cli/commands/__init__.py +1 -0
  8. action_platform/cli/commands/branch.py +36 -0
  9. action_platform/cli/commands/cloud.py +52 -0
  10. action_platform/cli/commands/deploy.py +90 -0
  11. action_platform/cli/commands/gitflow.py +49 -0
  12. action_platform/cli/commands/init.py +136 -0
  13. action_platform/cli/commands/install.py +50 -0
  14. action_platform/cli/commands/mcp.py +26 -0
  15. action_platform/cli/commands/pr.py +41 -0
  16. action_platform/cli/commands/release.py +22 -0
  17. action_platform/cli/commands/service.py +50 -0
  18. action_platform/cli/setup.py +35 -0
  19. action_platform/core/__init__.py +1 -0
  20. action_platform/core/action_platform.py +63 -0
  21. action_platform/core/branching.py +132 -0
  22. action_platform/core/changelog.py +73 -0
  23. action_platform/core/config.py +109 -0
  24. action_platform/core/context.py +65 -0
  25. action_platform/core/exception.py +25 -0
  26. action_platform/core/generate.py +218 -0
  27. action_platform/core/git.py +98 -0
  28. action_platform/core/gitflow.py +189 -0
  29. action_platform/core/install.py +199 -0
  30. action_platform/core/module.py +21 -0
  31. action_platform/core/pipeline.py +151 -0
  32. action_platform/core/pullrequest.py +113 -0
  33. action_platform/core/templates.py +261 -0
  34. action_platform/core/versioning.py +43 -0
  35. action_platform/hooks/__init__.py +1 -0
  36. action_platform/hooks/commit-msg +7 -0
  37. action_platform/hooks/gitflow.sh +78 -0
  38. action_platform/hooks/pre-commit +5 -0
  39. action_platform/hooks/pre-push +16 -0
  40. action_platform/logging.py +14 -0
  41. action_platform/main.py +35 -0
  42. action_platform/mcp/__init__.py +11 -0
  43. action_platform/mcp/__main__.py +4 -0
  44. action_platform/mcp/annotations.py +33 -0
  45. action_platform/mcp/server.py +70 -0
  46. action_platform/mcp/tools/__init__.py +3 -0
  47. action_platform/mcp/tools/flow.py +148 -0
  48. action_platform/mcp/tools/lifecycle.py +111 -0
  49. action_platform/mcp/tools/matrix.py +49 -0
  50. action_platform/mcp/tools/project.py +146 -0
  51. action_platform/providers/__init__.py +5 -0
  52. action_platform/providers/source_github.py +122 -0
  53. action_platform/settings.py +27 -0
  54. action_platform/testing/__init__.py +1 -0
  55. action_platform-0.1.0.dist-info/METADATA +401 -0
  56. action_platform-0.1.0.dist-info/RECORD +59 -0
  57. action_platform-0.1.0.dist-info/WHEEL +4 -0
  58. action_platform-0.1.0.dist-info/entry_points.txt +4 -0
  59. action_platform-0.1.0.dist-info/licenses/LICENSE +201 -0
@@ -0,0 +1,10 @@
1
+ """Action Platform __init__ module."""
2
+
3
+ __version__ = "0.1.0"
4
+ __description__ = "🛠️ Action Platform padroniza init, release e deploy."
5
+
6
+ from .core.config import Config
7
+ from .core.context import Context
8
+ from .core.action_platform import ActionPlatform
9
+
10
+ __all__ = ["Config", "Context", "ActionPlatform"]
@@ -0,0 +1,7 @@
1
+ """Action Platform abstract base classes."""
2
+
3
+ from .ci_runner import CIRunner
4
+ from .deploy_target import DeployTarget
5
+ from .source_host import SourceHost
6
+
7
+ __all__ = ["CIRunner", "DeployTarget", "SourceHost"]
@@ -0,0 +1,25 @@
1
+ """CIRunner ABC."""
2
+
3
+ from abc import ABC, abstractmethod
4
+ from typing import TYPE_CHECKING, Iterator
5
+
6
+ if TYPE_CHECKING:
7
+ from action_platform.core.context import Context, RunRef, RunResult
8
+
9
+
10
+ class CIRunner(ABC):
11
+ """CIRunner"""
12
+
13
+ name: str
14
+
15
+ @abstractmethod
16
+ def trigger(self, ctx: "Context", job: str, params: dict) -> "RunRef":
17
+ """Enqueue pipeline run."""
18
+
19
+ @abstractmethod
20
+ def wait(self, ctx: "Context", run: "RunRef", timeout: int = 1800) -> "RunResult":
21
+ """Block until pipeline finishes or timeout."""
22
+
23
+ @abstractmethod
24
+ def logs(self, ctx: "Context", run: "RunRef") -> Iterator[str]:
25
+ """Stream pipeline logs."""
@@ -0,0 +1,46 @@
1
+ """DeployTarget ABC — the lifecycle every deploy target answers to.
2
+
3
+ Vocabulary mirrors what a deployment scope needs over its life: provision the
4
+ target once, ship versions, move traffic, undo, inspect, tear down. A target
5
+ that cannot do one of them raises NotImplementedError and the CLI says so.
6
+ """
7
+
8
+ from abc import ABC, abstractmethod
9
+ from typing import TYPE_CHECKING
10
+
11
+ if TYPE_CHECKING:
12
+ from action_platform.core.context import Context, DeployResult, Diagnosis
13
+
14
+
15
+ class DeployTarget(ABC):
16
+ """DeployTarget"""
17
+
18
+ name: str
19
+
20
+ @abstractmethod
21
+ def preflight(self, ctx: "Context") -> None:
22
+ """Validate credentials, tooling, artifacts. Raise DeployError otherwise."""
23
+
24
+ def create(self, ctx: "Context") -> None:
25
+ """Provision the target itself (stack, app, registry). Idempotent."""
26
+ raise NotImplementedError(f"{self.name} cannot create")
27
+
28
+ @abstractmethod
29
+ def deploy(self, ctx: "Context") -> "DeployResult":
30
+ """Ship the current version."""
31
+
32
+ def switch_traffic(self, ctx: "Context", weight: int) -> None:
33
+ """Move `weight` percent of traffic to the newest version (blue-green)."""
34
+ raise NotImplementedError(f"{self.name} cannot switch traffic")
35
+
36
+ def rollback(self, ctx: "Context", to_version: str | None = None) -> None:
37
+ """Revert to `to_version` or the previous one."""
38
+ raise NotImplementedError(f"{self.name} cannot rollback")
39
+
40
+ def diagnose(self, ctx: "Context") -> "Diagnosis":
41
+ """Health, last deploy, logs pointer."""
42
+ raise NotImplementedError(f"{self.name} cannot diagnose")
43
+
44
+ def delete(self, ctx: "Context") -> None:
45
+ """Tear the target down."""
46
+ raise NotImplementedError(f"{self.name} cannot delete")
@@ -0,0 +1,52 @@
1
+ """SourceHost ABC."""
2
+
3
+ from abc import ABC, abstractmethod
4
+ from pathlib import Path
5
+ from typing import TYPE_CHECKING
6
+
7
+ if TYPE_CHECKING:
8
+ from action_platform.core.context import Context, PRRef, ReleaseRef
9
+
10
+
11
+ class SourceHost(ABC):
12
+ """SourceHost"""
13
+
14
+ name: str
15
+
16
+ @abstractmethod
17
+ def detect(self, remote_url: str) -> bool:
18
+ """Return True if this provider handles remote_url."""
19
+
20
+ def create_repository(
21
+ self, repo: str, description: str = "", private: bool = False
22
+ ) -> str:
23
+ """Create the remote repository; return its clone URL."""
24
+ raise NotImplementedError(f"{self.name} cannot create repositories")
25
+
26
+ @abstractmethod
27
+ def create_tag(self, ctx: "Context", tag: str) -> None:
28
+ """Create annotated tag on remote."""
29
+
30
+ @abstractmethod
31
+ def create_release(
32
+ self,
33
+ ctx: "Context",
34
+ tag: str,
35
+ notes: str,
36
+ assets: list[Path] | None = None,
37
+ draft: bool = False,
38
+ prerelease: bool = False,
39
+ ) -> "ReleaseRef":
40
+ """Publish release on remote host."""
41
+
42
+ @abstractmethod
43
+ def open_pr(
44
+ self,
45
+ ctx: "Context",
46
+ base: str,
47
+ head: str,
48
+ title: str,
49
+ body: str,
50
+ draft: bool = False,
51
+ ) -> "PRRef":
52
+ """Open pull/merge request."""
@@ -0,0 +1 @@
1
+ """Action Platform CLI package."""
@@ -0,0 +1 @@
1
+ """Action Platform CLI commands."""
@@ -0,0 +1,36 @@
1
+ """`action-platform branch` command."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import typer
6
+
7
+ from action_platform.core import branching
8
+ from action_platform.logging import logger
9
+
10
+
11
+ def run(
12
+ kind: str = typer.Argument(
13
+ ...,
14
+ help="feature | bugfix | hotfix | release | "
15
+ + " | ".join(
16
+ k
17
+ for k in branching.KINDS
18
+ if k not in {"feature", "bugfix", "hotfix", "release"}
19
+ ),
20
+ ),
21
+ code: str = typer.Argument(
22
+ ..., help="Issue or ticket code: 42, PROJ-123, 1.4.0 for release"
23
+ ),
24
+ slug: str | None = typer.Argument(None, help="Optional words appended as a slug"),
25
+ push: bool = typer.Option(
26
+ True, "--push/--no-push", help="Push the new branch upstream"
27
+ ),
28
+ ) -> None:
29
+ """Start a branch: checkout the right base (develop or main), pull, create <kind>/<code>."""
30
+ branch = branching.start(kind, code, slug, push=push)
31
+ logger.info(
32
+ "on %s (from %s)%s",
33
+ branch.name,
34
+ branch.base,
35
+ " — pushed" if branch.pushed else "",
36
+ )
@@ -0,0 +1,52 @@
1
+ """`action-platform cloud` commands."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+
7
+ import typer
8
+ from rich.console import Console
9
+
10
+ from action_platform.core.generate import apply_cloud, read_platform
11
+ from action_platform.core.templates import load_matrix
12
+ from action_platform.logging import logger
13
+
14
+ app = typer.Typer(
15
+ help="Apply deploy overlays to an existing project.", no_args_is_help=True
16
+ )
17
+ console = Console()
18
+
19
+
20
+ @app.command("set")
21
+ def set_(
22
+ name: str = typer.Argument(..., help="aws/lambda, docker, ..."),
23
+ project: Path = typer.Option(
24
+ None, "--project", "-p", help="Project directory (default: cwd)"
25
+ ),
26
+ update: bool = typer.Option(False, "--update", help="Refresh the templates cache"),
27
+ ) -> None:
28
+ """Apply a cloud overlay and set [deploy] target in platform.toml (replaces the previous one)."""
29
+ repo, matrix = load_matrix(update=update)
30
+ target = (project or Path.cwd()).resolve()
31
+
32
+ apply_cloud(repo, matrix.cloud(name), target)
33
+
34
+ logger.info("applied cloud %s to %s", name, target)
35
+
36
+
37
+ @app.command("list")
38
+ def list_(
39
+ project: Path = typer.Option(
40
+ None, "--project", "-p", help="Filter by what this project supports"
41
+ ),
42
+ ) -> None:
43
+ """List cloud overlays, optionally only those compatible with a project."""
44
+ _, matrix = load_matrix()
45
+ clouds = matrix.clouds
46
+
47
+ if project is not None:
48
+ meta = read_platform(project.resolve())
49
+ clouds = matrix.clouds_for(meta.get("type", ""), meta.get("language", ""))
50
+
51
+ for cloud in clouds:
52
+ console.print(f"[bold]{cloud.name}[/bold] {cloud.description}")
@@ -0,0 +1,90 @@
1
+ """`action-platform deploy | rollback | diagnose | destroy` commands."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+
7
+ import typer
8
+ from rich.console import Console
9
+
10
+ from action_platform.core.action_platform import ActionPlatform
11
+ from action_platform.core.config import Config
12
+ from action_platform.core.exception import DeployError
13
+ from action_platform.logging import logger
14
+ from action_platform.settings import settings
15
+
16
+ console = Console()
17
+
18
+ TARGET = typer.Option(
19
+ None, "--target", help="Filter by target name (aws/lambda, docker, ...)"
20
+ )
21
+ STAGE = typer.Option(
22
+ None, "--stage", help="dev | prod (default: prod on main/master, else dev)"
23
+ )
24
+
25
+
26
+ def _tool() -> ActionPlatform:
27
+ return ActionPlatform(config=Config.from_toml(Path.cwd() / settings.CONFIG_FILE))
28
+
29
+
30
+ def run(
31
+ target: str | None = TARGET,
32
+ stage: str | None = STAGE,
33
+ dry_run: bool = typer.Option(False, "--dry-run"),
34
+ ) -> None:
35
+ """Ship the current version to the [deploy] target in platform.toml."""
36
+ for r in _tool().deploy(target=target, dry_run=dry_run, stage=stage):
37
+ logger.info(
38
+ "deploy %s ok=%s version=%s url=%s", r.target, r.ok, r.version, r.url
39
+ )
40
+
41
+ if not r.ok:
42
+ raise DeployError(r.error or f"{r.target} failed")
43
+
44
+
45
+ def rollback(
46
+ to_version: str | None = typer.Argument(
47
+ None, help="Version to return to (default: previous)"
48
+ ),
49
+ target: str | None = TARGET,
50
+ stage: str | None = STAGE,
51
+ ) -> None:
52
+ """Return the target to a previous version."""
53
+ try:
54
+ _tool().rollback(target=target, to_version=to_version, stage=stage)
55
+ except NotImplementedError as e:
56
+ raise DeployError(str(e)) from e
57
+ logger.info("rollback done")
58
+
59
+
60
+ def diagnose(target: str | None = TARGET, stage: str | None = STAGE) -> None:
61
+ """Health, status and URL of the deployed target."""
62
+ try:
63
+ results = _tool().diagnose(target=target, stage=stage)
64
+ except NotImplementedError as e:
65
+ raise DeployError(str(e)) from e
66
+ for d in results:
67
+ mark = "[green]ok[/green]" if d.ok else "[red]not ok[/red]"
68
+ console.print(f"[bold]{d.target}[/bold] {mark} {d.status}")
69
+
70
+ if d.url:
71
+ console.print(f" url: {d.url}")
72
+
73
+ for k, v in d.details.items():
74
+ console.print(f" {k}: {v}")
75
+
76
+
77
+ def destroy(
78
+ target: str | None = TARGET,
79
+ stage: str | None = STAGE,
80
+ yes: bool = typer.Option(False, "--yes", "-y", help="Skip confirmation"),
81
+ ) -> None:
82
+ """Tear the target down. Irreversible."""
83
+ if not yes and not typer.confirm("Delete the deployed target?"):
84
+ raise typer.Abort()
85
+
86
+ try:
87
+ _tool().destroy(target=target, stage=stage)
88
+ except NotImplementedError as e:
89
+ raise DeployError(str(e)) from e
90
+ logger.info("destroy done")
@@ -0,0 +1,49 @@
1
+ """`action-platform gitflow` command."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+
7
+ import typer
8
+ from rich.console import Console
9
+
10
+ from action_platform.core import gitflow
11
+ from action_platform.core.exception import ActionPlatformError
12
+
13
+ console = Console()
14
+
15
+
16
+ def run(
17
+ since: str | None = typer.Option(
18
+ None,
19
+ "--since",
20
+ help="Check commits after this ref (default: merge base with develop/main)",
21
+ ),
22
+ install: bool = typer.Option(
23
+ False,
24
+ "--install-hooks",
25
+ help="Install the bundled git hooks into .git/hooks and exit",
26
+ ),
27
+ ) -> None:
28
+ """Check the current branch and commits against git-flow and Conventional Commits."""
29
+ cwd = Path.cwd()
30
+
31
+ if install:
32
+ if gitflow.install_hooks(cwd):
33
+ console.print("[green]hooks installed[/green] (.git/hooks)")
34
+ return
35
+ raise ActionPlatformError("not a git repository")
36
+
37
+ report = gitflow.audit(cwd, since=since)
38
+ console.print(
39
+ f"branch [bold]{report.branch}[/bold], {report.checked_commits} commit(s) checked"
40
+ )
41
+
42
+ if report.ok:
43
+ console.print("[green]git-flow ok[/green]")
44
+ return
45
+
46
+ for problem in report.problems:
47
+ console.print(f"[red]✗[/red] {problem}")
48
+
49
+ raise typer.Exit(code=1)
@@ -0,0 +1,136 @@
1
+ """`action-platform init` command."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+
7
+ import typer
8
+ from rich.console import Console
9
+ from rich.table import Table
10
+
11
+ from action_platform.core.exception import TemplateError
12
+ from action_platform.core.generate import apply_cloud, generate_project, push_project
13
+ from action_platform.core.templates import Matrix, load_matrix
14
+ from action_platform.logging import logger
15
+
16
+ CI_PROVIDERS = ["github", "gitlab", "jenkins"]
17
+
18
+ console = Console()
19
+
20
+
21
+ def run(
22
+ type_: str | None = typer.Argument(
23
+ None, metavar="TYPE", help="web, library, mcp, ..."
24
+ ),
25
+ stack: str | None = typer.Argument(None, help="python, go, node, ..."),
26
+ template: str | None = typer.Argument(
27
+ None, help="fastapi, gin, ... (default per stack)"
28
+ ),
29
+ name: str | None = typer.Option(None, "--name", "-n", help="Project name"),
30
+ ci: str | None = typer.Option(
31
+ None, "--ci", help="CI provider: " + ", ".join(CI_PROVIDERS)
32
+ ),
33
+ cloud: str | None = typer.Option(
34
+ None, "--cloud", help="Deploy overlay: aws/lambda, docker, ..."
35
+ ),
36
+ output: Path | None = typer.Option(
37
+ None, "--output", "-o", help="Where to create the project"
38
+ ),
39
+ push: bool = typer.Option(
40
+ True,
41
+ "--push/--no-push",
42
+ help="Create the remote repo via [source_host] and push (default: on)",
43
+ ),
44
+ private: bool = typer.Option(False, "--private", help="With --push: private repo"),
45
+ list_: bool = typer.Option(False, "--list", "-l", help="Show the template matrix"),
46
+ update: bool = typer.Option(False, "--update", help="Refresh the templates cache"),
47
+ ) -> None:
48
+ """Bootstrap a project from the templates matrix."""
49
+ repo, matrix = load_matrix(update=update)
50
+
51
+ if list_:
52
+ print_matrix(matrix)
53
+ return
54
+
55
+ if type_ is None:
56
+ type_ = choose("type", matrix.types())
57
+ if stack is None and matrix.stacks(type_):
58
+ stack = choose("stack", matrix.stacks(type_))
59
+ if template is None and stack is not None:
60
+ leaves = matrix.templates(type_, stack)
61
+ if len(leaves) > 1:
62
+ template = choose("template", [leaf.template for leaf in leaves])
63
+
64
+ leaf = matrix.resolve(type_, stack, template)
65
+
66
+ if name is None:
67
+ name = typer.prompt("project name")
68
+ if ci is None and leaf.type != "empty":
69
+ ci = choose("ci", CI_PROVIDERS)
70
+ if ci is not None and ci not in CI_PROVIDERS:
71
+ raise TemplateError(f"unknown ci: {ci} (available: {', '.join(CI_PROVIDERS)})")
72
+
73
+ project = generate_project(
74
+ repo, leaf, name=name, ci=ci, output=output or Path.cwd()
75
+ )
76
+ logger.info("created %s", project)
77
+
78
+ if cloud is not None:
79
+ apply_cloud(repo, matrix.cloud(cloud), project)
80
+ logger.info("applied cloud %s", cloud)
81
+
82
+ if push:
83
+ url = push_project(project, private=private)
84
+ logger.info("pushed to %s", url)
85
+
86
+
87
+ def choose(label: str, options: list[str]) -> str:
88
+ console.print(f"[bold]{label}[/bold]")
89
+ for i, opt in enumerate(options, 1):
90
+ console.print(f" {i}. {opt}")
91
+ while True:
92
+ raw = typer.prompt(f"{label} [1-{len(options)}]")
93
+ if raw in options:
94
+ return raw
95
+ if raw.isdigit() and 1 <= int(raw) <= len(options):
96
+ return options[int(raw) - 1]
97
+ console.print("[red]invalid choice[/red]")
98
+
99
+
100
+ def print_matrix(matrix: Matrix) -> None:
101
+ table = Table(title="Projects")
102
+ table.add_column("type")
103
+ table.add_column("stack")
104
+ table.add_column("template")
105
+ table.add_column("description")
106
+ for leaf in matrix.leaves:
107
+ tpl = f"{leaf.template} *" if leaf.default else leaf.template
108
+ table.add_row(leaf.type, leaf.stack, tpl, leaf.description)
109
+ console.print(table)
110
+ console.print("[dim]* default template for the stack[/dim]\n")
111
+
112
+ clouds = Table(title="Clouds")
113
+ clouds.add_column("cloud")
114
+ clouds.add_column("types")
115
+ clouds.add_column("languages")
116
+ clouds.add_column("description")
117
+ for cloud in matrix.clouds:
118
+ clouds.add_row(
119
+ cloud.name,
120
+ ", ".join(cloud.types) or "any",
121
+ ", ".join(cloud.languages) or "any",
122
+ cloud.description,
123
+ )
124
+ console.print(clouds)
125
+
126
+ services = Table(title="Services")
127
+ services.add_column("service")
128
+ services.add_column("providers")
129
+ services.add_column("description")
130
+
131
+ for service in matrix.services:
132
+ services.add_row(
133
+ service.name, ", ".join(service.providers), service.description
134
+ )
135
+
136
+ console.print(services)
@@ -0,0 +1,50 @@
1
+ """`action-platform install` command."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+
7
+ import typer
8
+ from rich.console import Console
9
+
10
+ from action_platform.core import install as installing
11
+
12
+ console = Console()
13
+
14
+
15
+ def run(
16
+ type_: str = typer.Option("web", "--type", help="web, library, docs, plugin"),
17
+ language: str | None = typer.Option(
18
+ None, "--language", help="python, go, node, php, java, rust (default: detected)"
19
+ ),
20
+ ci: str = typer.Option("github", "--ci", help="github, gitlab, jenkins"),
21
+ dry_run: bool = typer.Option(False, "--dry-run", help="Show what would be created"),
22
+ ) -> None:
23
+ """Install the platform in this repository: platform.toml, code quality, CI files (never overwritten) and git hooks into .git/hooks (always refreshed)."""
24
+ plan = installing.install(
25
+ Path.cwd(), type_=type_, language=language, ci=ci, dry_run=dry_run
26
+ )
27
+ verb = "would create" if dry_run else "created"
28
+ console.print(
29
+ f"[bold]{plan.root.name}[/bold] — {plan.language} {plan.type}, ci {plan.ci}"
30
+ )
31
+
32
+ for rel in plan.created:
33
+ console.print(f" [green]+[/green] {rel} ({verb})")
34
+
35
+ for rel in plan.skipped:
36
+ console.print(f" [dim]= {rel} (exists, kept)[/dim]")
37
+
38
+ if plan.hooks_installed:
39
+ console.print(
40
+ " [green]✓[/green] git hooks installed (.git/hooks, unversioned)"
41
+ )
42
+
43
+ if dry_run or not plan.created:
44
+ return
45
+
46
+ console.print(
47
+ "\nnext: review the files, then "
48
+ '[bold]git add -A && git commit -m "chore(platform): install action-platform"[/bold] '
49
+ "— chore(platform) commits are allowed on main"
50
+ )
@@ -0,0 +1,26 @@
1
+ """`action-platform mcp` command."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import typer
6
+
7
+ from action_platform.core.exception import ActionPlatformError
8
+
9
+
10
+ def run(
11
+ http: bool = typer.Option(
12
+ False, "--http", help="Serve over streamable HTTP instead of stdio"
13
+ ),
14
+ host: str = typer.Option("127.0.0.1", "--host"),
15
+ port: int = typer.Option(8765, "--port", help="Port for --http"),
16
+ ) -> None:
17
+ """Run the embedded MCP server so AI agents can scaffold, deploy and operate projects."""
18
+ try:
19
+ from action_platform.mcp.server import main
20
+ except ModuleNotFoundError as e:
21
+ raise ActionPlatformError(
22
+ "MCP support is not installed: pip install 'action-platform[mcp]'"
23
+ ) from e
24
+
25
+ argv = ["--http", "--host", host, "--port", str(port)] if http else []
26
+ main(argv)
@@ -0,0 +1,41 @@
1
+ """`action-platform pr` command."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+
7
+ import typer
8
+ from rich.console import Console
9
+
10
+ from action_platform.core import pullrequest
11
+
12
+ console = Console()
13
+
14
+
15
+ def run(
16
+ base: str | None = typer.Option(
17
+ None, "--base", help="Target branch (default from git-flow)"
18
+ ),
19
+ title: str | None = typer.Option(
20
+ None, "--title", help="Default: first commit on the branch"
21
+ ),
22
+ body: str | None = typer.Option(
23
+ None, "--body", help="Default: commits grouped by type"
24
+ ),
25
+ draft: bool = typer.Option(False, "--draft"),
26
+ dry_run: bool = typer.Option(
27
+ False, "--dry-run", help="Show target, title and body; open nothing"
28
+ ),
29
+ ) -> None:
30
+ """Open a pull request for the current branch: audits git-flow, picks the target, describes the commits."""
31
+ cwd = Path.cwd()
32
+
33
+ if dry_run:
34
+ proposal = pullrequest.propose(cwd, base=base, title=title)
35
+ console.print(f"[bold]{proposal.head}[/bold] → [bold]{proposal.base}[/bold]")
36
+ console.print(f"title: {proposal.title}\n")
37
+ console.print(body or proposal.body)
38
+ return
39
+
40
+ ref = pullrequest.open_pr(cwd, base=base, title=title, body=body, draft=draft)
41
+ console.print(f"[green]opened[/green] #{ref.number} {ref.url}")
@@ -0,0 +1,22 @@
1
+ """`action-platform release` command."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+
7
+ import typer
8
+
9
+ from action_platform.core.config import Config
10
+ from action_platform.core.action_platform import ActionPlatform
11
+ from action_platform.logging import logger
12
+
13
+
14
+ def run(
15
+ level: str = typer.Argument("patch", help="patch | minor | major | X.Y.Z"),
16
+ dry_run: bool = typer.Option(False, "--dry-run"),
17
+ ) -> None:
18
+ """Bump version, generate changelog, tag, and publish release."""
19
+ config = Config.from_toml(Path.cwd() / "platform.toml")
20
+ tool = ActionPlatform(config=config)
21
+ ctx = tool.release(level=level, dry_run=dry_run)
22
+ logger.info("release done: %s", ctx.next_version)