deploynator 1.1.1__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.
@@ -0,0 +1,4 @@
1
+ from deploynator._app import DeploynatorApp
2
+
3
+ __version__ = "1.1.1"
4
+ __all__ = ["DeploynatorApp"]
@@ -0,0 +1,282 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import sys
5
+ from typing import NoReturn
6
+
7
+ from deploynator import DeploynatorApp
8
+ from deploynator._config import Config
9
+ from deploynator._models import CommitType
10
+
11
+ KNOWN_COMMANDS = {
12
+ "release", "commit", "start", "auth", "docs", "init",
13
+ "status", "projects", "workflow", "install", "interactive", "serve",
14
+ }
15
+
16
+
17
+ def build_parser() -> argparse.ArgumentParser:
18
+ parser = argparse.ArgumentParser(
19
+ prog="deploynator",
20
+ description="Multi-platform deployer for GitHub, GitLab, and Bitbucket.",
21
+ epilog="Deployed with deploynator (https://github.com/grisuno/deploynator)",
22
+ )
23
+ parser.add_argument(
24
+ "--version", action="version",
25
+ version=f"deploynator {__import__('deploynator').__version__}",
26
+ )
27
+ parser.add_argument(
28
+ "--workdir", type=str, default="",
29
+ help="Working directory (default: current dir)",
30
+ )
31
+
32
+ subparsers = parser.add_subparsers(dest="command", help="Available commands")
33
+
34
+ # release
35
+ release_p = subparsers.add_parser("release", help="Create a release")
36
+ release_p.add_argument(
37
+ "--type", dest="commit_type", default="release",
38
+ choices=CommitType.valid_types(),
39
+ help="Commit type (default: release)",
40
+ )
41
+ release_p.add_argument("--subject", type=str, default="", help="Commit subject")
42
+ release_p.add_argument("--scope", type=str, default="", help="Commit scope")
43
+ release_p.add_argument("--body", type=str, default="", help="Commit body")
44
+ release_p.add_argument("--project", "-p", type=str, default="", help="Project name from projects/")
45
+ release_p.add_argument("--bump", type=str, default="", choices=["major", "minor", "patch", ""],
46
+ help="Force version bump")
47
+ release_p.add_argument("--dry-run", action="store_true", help="Simulate without changes")
48
+ release_p.add_argument("--no-test", dest="no_test", action="store_true",
49
+ help="Skip tests (like DEPLOY.sh --no-test)")
50
+ release_p.add_argument("--no-docs", dest="no_docs", action="store_true",
51
+ help="Skip readmenator docs regeneration")
52
+ release_p.add_argument("--no-pypi", dest="no_pypi", action="store_true",
53
+ help="Skip PyPI build+upload")
54
+ release_p.add_argument("--platforms", type=str, nargs="*",
55
+ choices=["github", "gitlab", "bitbucket"],
56
+ help="Platforms to deploy to")
57
+ release_p.add_argument("--all", dest="all_projects", action="store_true",
58
+ help="Deploy all discovered projects")
59
+
60
+ # commit
61
+ commit_p = subparsers.add_parser("commit", help="Create a conventional commit")
62
+ commit_p.add_argument(
63
+ "--type", dest="commit_type", required=True,
64
+ choices=CommitType.valid_types(),
65
+ help="Commit type",
66
+ )
67
+ commit_p.add_argument("--subject", type=str, required=True, help="Commit subject")
68
+ commit_p.add_argument("--scope", type=str, default="", help="Commit scope")
69
+ commit_p.add_argument("--body", type=str, default="", help="Commit body")
70
+ commit_p.add_argument("--project", "-p", type=str, default="", help="Project name from projects/")
71
+ commit_p.add_argument("--dry-run", action="store_true", help="Simulate without changes")
72
+
73
+ # start
74
+ start_p = subparsers.add_parser("start", help="Create a feature/bug branch")
75
+ start_p.add_argument(
76
+ "--type", dest="commit_type", required=True,
77
+ choices=CommitType.valid_types(),
78
+ help="Branch type",
79
+ )
80
+ start_p.add_argument("--desc", type=str, required=True, help="Branch description")
81
+ start_p.add_argument("--project", "-p", type=str, default="", help="Project name from projects/")
82
+ start_p.add_argument("--remote", action="store_true", help="Push branch to remote")
83
+
84
+ # auth
85
+ auth_p = subparsers.add_parser("auth", help="Authentication commands")
86
+ auth_sub = auth_p.add_subparsers(dest="auth_command", help="Auth subcommands")
87
+ auth_login_p = auth_sub.add_parser("login", help="Login to platforms")
88
+ auth_login_p.add_argument(
89
+ "--platforms", type=str, nargs="*",
90
+ choices=["github", "gitlab", "bitbucket"],
91
+ help="Platforms to authenticate (default: all)",
92
+ )
93
+ auth_sub.add_parser("status", help="Show authentication status")
94
+
95
+ # docs
96
+ docs_p = subparsers.add_parser("docs", help="Generate documentation via ReadMenator")
97
+ docs_p.add_argument("--project", "-p", type=str, default="", help="Project name from projects/")
98
+
99
+ # init
100
+ init_p = subparsers.add_parser("init", help="Initialize deploynator in project")
101
+ init_p.add_argument("--name", type=str, default="", help="Project name")
102
+
103
+ # status
104
+ status_p = subparsers.add_parser("status", help="Show project status")
105
+ status_p.add_argument("--project", "-p", type=str, default="", help="Project name from projects/")
106
+
107
+ # projects
108
+ subparsers.add_parser("projects", help="List discovered projects")
109
+
110
+ # workflow
111
+ workflow_p = subparsers.add_parser("workflow", help="Generate CI/CD workflows")
112
+ workflow_p.add_argument("--project", "-p", type=str, default="", help="Specific project")
113
+ workflow_p.add_argument("--all", action="store_true", help="All projects")
114
+
115
+ # install
116
+ install_p = subparsers.add_parser("install", help="Install platform dependencies")
117
+ install_p.add_argument("--project", "-p", type=str, default="", help="Specific project")
118
+
119
+ # interactive (no args/flags — bare mode menu)
120
+ subparsers.add_parser("interactive", help="Start interactive menu (also default with no args)")
121
+
122
+ # serve (MCP stdio server for agent harnesses)
123
+ serve_p = subparsers.add_parser("serve", help="Start MCP stdio server")
124
+ serve_p.add_argument("path", type=str, nargs="?", default=".",
125
+ help="Target directory (default: .)")
126
+
127
+ return parser
128
+
129
+
130
+ def preprocess_argv(argv: list[str]) -> tuple[argparse.Namespace, str]:
131
+ parser = build_parser()
132
+ extracted_project = ""
133
+
134
+ if not argv or argv[0] in ("--help", "-h", "--version"):
135
+ return parser.parse_args(argv), extracted_project
136
+
137
+ # Scan past global flags (--workdir, etc.) to find first positional token
138
+ first_pos = None
139
+ first_pos_idx = None
140
+ i = 0
141
+ while i < len(argv):
142
+ arg = argv[i]
143
+ if not arg.startswith("-"):
144
+ first_pos = arg
145
+ first_pos_idx = i
146
+ break
147
+ if arg in ("--workdir",):
148
+ i += 2
149
+ else:
150
+ i += 1
151
+
152
+ if first_pos is not None and first_pos not in KNOWN_COMMANDS:
153
+ extracted_project = first_pos
154
+ argv = argv[:first_pos_idx] + argv[first_pos_idx + 1:]
155
+
156
+ args = parser.parse_args(argv)
157
+
158
+ if hasattr(args, "project") and not args.project and extracted_project:
159
+ args.project = extracted_project
160
+
161
+ return args, extracted_project
162
+
163
+
164
+ def main(argv: list[str] | None = None) -> NoReturn | None:
165
+ raw_argv = list(argv) if argv is not None else sys.argv[1:]
166
+
167
+ # Bare `deploynator` (no args) = interactive menu. Flags = headless.
168
+ if not raw_argv:
169
+ config = Config.create()
170
+ DeploynatorApp(config).interactive_menu()
171
+ return
172
+
173
+ args, project_pos = preprocess_argv(raw_argv)
174
+
175
+ config = Config.create()
176
+ app = DeploynatorApp(config, workdir=args.workdir if hasattr(args, "workdir") else "")
177
+
178
+ if not args.command:
179
+ parser = build_parser()
180
+ parser.print_help()
181
+ return
182
+
183
+ project = getattr(args, "project", "") or project_pos
184
+
185
+ if args.command == "release":
186
+ results = app.release(
187
+ commit_type=args.commit_type,
188
+ subject=args.subject,
189
+ scope=args.scope,
190
+ body=args.body,
191
+ project_name=project,
192
+ bump=args.bump if hasattr(args, "bump") else "",
193
+ dry_run=args.dry_run,
194
+ platforms=args.platforms if hasattr(args, "platforms") else None,
195
+ all_projects=args.all_projects if hasattr(args, "all_projects") else False,
196
+ no_test=getattr(args, "no_test", False),
197
+ no_docs=getattr(args, "no_docs", False),
198
+ no_pypi=getattr(args, "no_pypi", False),
199
+ )
200
+ sys.exit(0 if all(r.success for r in results) else 1)
201
+
202
+ elif args.command == "commit":
203
+ result = app.commit(
204
+ commit_type=args.commit_type,
205
+ subject=args.subject,
206
+ scope=args.scope,
207
+ body=args.body,
208
+ project_name=project,
209
+ dry_run=args.dry_run,
210
+ )
211
+ print(result)
212
+
213
+ elif args.command == "start":
214
+ result = app.start_branch(
215
+ commit_type=args.commit_type,
216
+ description=args.desc,
217
+ project_name=project,
218
+ remote=args.remote,
219
+ )
220
+ print(result)
221
+
222
+ elif args.command == "auth":
223
+ if args.auth_command == "login":
224
+ platforms = getattr(args, "platforms", None)
225
+ results = app.auth_login(platforms)
226
+ for p, ok in results.items():
227
+ print(f" {p}: {'OK' if ok else 'FAIL'}")
228
+ elif args.auth_command == "status":
229
+ status = app.auth_status()
230
+ for p, ok in status.items():
231
+ print(f" {p}: {'authenticated' if ok else 'not authenticated'}")
232
+ else:
233
+ build_parser().parse_args(["auth", "--help"])
234
+
235
+ elif args.command == "docs":
236
+ ok = app.generate_docs(project_name=project)
237
+ print("[+] Documentation generated" if ok else "[!] Documentation failed")
238
+
239
+ elif args.command == "init":
240
+ result = app.init_project(name=args.name)
241
+ print(result)
242
+
243
+ elif args.command == "status":
244
+ info = app.status(project_name=project)
245
+ for k, v in info.items():
246
+ print(f" {k}: {v}")
247
+
248
+ elif args.command == "projects":
249
+ projects = app.list_projects()
250
+ if not projects:
251
+ print("No projects found.")
252
+ return
253
+ for p in projects:
254
+ platforms = ", ".join(
255
+ f"{pc.platform.label()}/{pc.repo}" for pc in p.platforms if pc.enabled
256
+ )
257
+ print(f" {p.name}: {platforms}" if platforms else f" {p.name}")
258
+
259
+ elif args.command == "workflow":
260
+ created = app.generate_ci(
261
+ project_name=project,
262
+ all_projects=getattr(args, "all", False),
263
+ )
264
+ for path in created:
265
+ print(f"[+] Created {path}")
266
+
267
+ elif args.command == "install":
268
+ deps = app.install_deps(project_name=project)
269
+ for p, ok in deps.items():
270
+ print(f" {p}: {'available' if ok else 'not found'}")
271
+
272
+ elif args.command == "interactive":
273
+ app.interactive_menu()
274
+
275
+ elif args.command == "serve":
276
+ from deploynator._mcp_server import MCPServer
277
+ target = getattr(args, "path", ".") or "."
278
+ MCPServer(app, target).run()
279
+
280
+
281
+ if __name__ == "__main__":
282
+ main()