swoosh-cli 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.
- swoosh/__init__.py +3 -0
- swoosh/cli.py +442 -0
- swoosh/modules/__init__.py +1 -0
- swoosh/modules/auth.py +389 -0
- swoosh/modules/check.py +203 -0
- swoosh/modules/clone.py +196 -0
- swoosh/modules/commit.py +237 -0
- swoosh/modules/config.py +169 -0
- swoosh/modules/deploy.py +619 -0
- swoosh/modules/hooks.py +285 -0
- swoosh/modules/init.py +216 -0
- swoosh/modules/origins.py +692 -0
- swoosh/modules/pr.py +290 -0
- swoosh/modules/release.py +389 -0
- swoosh/modules/secrets.py +580 -0
- swoosh/modules/sync.py +270 -0
- swoosh/modules/templates.py +402 -0
- swoosh/modules/utils.py +342 -0
- swoosh_cli-0.1.0.dist-info/METADATA +198 -0
- swoosh_cli-0.1.0.dist-info/RECORD +23 -0
- swoosh_cli-0.1.0.dist-info/WHEEL +5 -0
- swoosh_cli-0.1.0.dist-info/entry_points.txt +2 -0
- swoosh_cli-0.1.0.dist-info/top_level.txt +1 -0
swoosh/__init__.py
ADDED
swoosh/cli.py
ADDED
|
@@ -0,0 +1,442 @@
|
|
|
1
|
+
"""Swoosh CLI - Main entry point."""
|
|
2
|
+
|
|
3
|
+
import typer
|
|
4
|
+
from rich.console import Console
|
|
5
|
+
from rich.panel import Panel
|
|
6
|
+
from typing import Optional
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
from swoosh import __version__
|
|
10
|
+
from swoosh.modules import (
|
|
11
|
+
init, hooks, config, check, clone,
|
|
12
|
+
commit, release, deploy, sync, secrets, pr, origins, templates, auth
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
app = typer.Typer(
|
|
16
|
+
name="swoosh",
|
|
17
|
+
help="All-in-one CLI for Git workflow automation: init, commit, release, deploy, sync",
|
|
18
|
+
rich_markup_mode="rich",
|
|
19
|
+
no_args_is_help=True,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
console = Console()
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def version_callback(value: bool):
|
|
26
|
+
if value:
|
|
27
|
+
console.print(Panel(
|
|
28
|
+
f"[bold blue]Swoosh[/] v{__version__}",
|
|
29
|
+
subtitle="Git Workflow Automation"
|
|
30
|
+
))
|
|
31
|
+
raise typer.Exit()
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@app.callback()
|
|
35
|
+
def main(
|
|
36
|
+
version: Optional[bool] = typer.Option(
|
|
37
|
+
None, "--version", "-v",
|
|
38
|
+
callback=version_callback,
|
|
39
|
+
is_eager=True,
|
|
40
|
+
help="Show version"
|
|
41
|
+
),
|
|
42
|
+
):
|
|
43
|
+
"""Swoosh - All-in-one Git workflow automation."""
|
|
44
|
+
pass
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
# ============================================================================
|
|
48
|
+
# INIT
|
|
49
|
+
# ============================================================================
|
|
50
|
+
|
|
51
|
+
@app.command("init")
|
|
52
|
+
def init_project(
|
|
53
|
+
name: Optional[str] = typer.Argument(None, help="Project name"),
|
|
54
|
+
here: bool = typer.Option(False, "--here", "-h", help="Initialize in current directory"),
|
|
55
|
+
template: str = typer.Option("generic", "--template", "-t", help="CI template: node, python, rust, go, generic"),
|
|
56
|
+
private: bool = typer.Option(False, "--private", "-p", help="Create private repository"),
|
|
57
|
+
no_ci: bool = typer.Option(False, "--no-ci", help="Skip CI/CD setup"),
|
|
58
|
+
no_autopush: bool = typer.Option(False, "--no-autopush", help="Skip auto-push hook"),
|
|
59
|
+
multi_origin: bool = typer.Option(False, "--multi-origin", "-m", help="Enable multi-origin push"),
|
|
60
|
+
):
|
|
61
|
+
"""Initialize project with Git, GitHub repo, and CI/CD."""
|
|
62
|
+
init.run(
|
|
63
|
+
name=name,
|
|
64
|
+
here=here,
|
|
65
|
+
template=template,
|
|
66
|
+
private=private,
|
|
67
|
+
setup_ci=not no_ci,
|
|
68
|
+
setup_autopush=not no_autopush,
|
|
69
|
+
multi_origin=multi_origin,
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
# ============================================================================
|
|
74
|
+
# COMMIT
|
|
75
|
+
# ============================================================================
|
|
76
|
+
|
|
77
|
+
@app.command("commit")
|
|
78
|
+
def commit_cmd(
|
|
79
|
+
message: Optional[str] = typer.Argument(None, help="Commit message"),
|
|
80
|
+
type_: Optional[str] = typer.Option(None, "--type", "-t", help="Commit type: feat, fix, docs, etc."),
|
|
81
|
+
scope: Optional[str] = typer.Option(None, "--scope", "-s", help="Commit scope"),
|
|
82
|
+
push: bool = typer.Option(False, "--push", "-p", help="Push after commit"),
|
|
83
|
+
all_remotes: bool = typer.Option(False, "--all", "-a", help="Push to all remotes"),
|
|
84
|
+
):
|
|
85
|
+
"""Create a conventional commit."""
|
|
86
|
+
if message and type_:
|
|
87
|
+
commit.quick_commit(message, type_, push=push)
|
|
88
|
+
else:
|
|
89
|
+
commit.interactive_commit(
|
|
90
|
+
commit_type=type_,
|
|
91
|
+
scope=scope,
|
|
92
|
+
message=message,
|
|
93
|
+
push=push,
|
|
94
|
+
all_remotes=all_remotes,
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
# ============================================================================
|
|
99
|
+
# RELEASE
|
|
100
|
+
# ============================================================================
|
|
101
|
+
|
|
102
|
+
@app.command("release")
|
|
103
|
+
def release_cmd(
|
|
104
|
+
bump: Optional[str] = typer.Argument(None, help="Bump type: major, minor, patch, alpha, beta, rc"),
|
|
105
|
+
version: Optional[str] = typer.Option(None, "--version", "-v", help="Specific version"),
|
|
106
|
+
prerelease: Optional[str] = typer.Option(None, "--pre", "-p", help="Pre-release type: alpha, beta, rc"),
|
|
107
|
+
auto: bool = typer.Option(False, "--auto", "-a", help="Auto-detect bump type from commits"),
|
|
108
|
+
no_changelog: bool = typer.Option(False, "--no-changelog", help="Skip changelog generation"),
|
|
109
|
+
no_github: bool = typer.Option(False, "--no-github", help="Skip GitHub release"),
|
|
110
|
+
no_push: bool = typer.Option(False, "--no-push", help="Skip push"),
|
|
111
|
+
):
|
|
112
|
+
"""Create a new release with version bump, changelog, and tag.
|
|
113
|
+
|
|
114
|
+
Supports pre-releases: alpha, beta, rc
|
|
115
|
+
Auto-detects breaking changes for major bumps.
|
|
116
|
+
"""
|
|
117
|
+
release.create_release(
|
|
118
|
+
bump_type=bump,
|
|
119
|
+
version=version,
|
|
120
|
+
prerelease=prerelease,
|
|
121
|
+
auto=auto,
|
|
122
|
+
skip_changelog=no_changelog,
|
|
123
|
+
skip_github=no_github,
|
|
124
|
+
push=not no_push,
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
# ============================================================================
|
|
129
|
+
# DEPLOY
|
|
130
|
+
# ============================================================================
|
|
131
|
+
|
|
132
|
+
@app.command("deploy")
|
|
133
|
+
def deploy_cmd(
|
|
134
|
+
target: Optional[str] = typer.Argument(None, help="Deploy target from swoosh.yaml"),
|
|
135
|
+
dry_run: bool = typer.Option(False, "--dry-run", "-n", help="Show what would be done"),
|
|
136
|
+
skip_health: bool = typer.Option(False, "--skip-health", help="Skip health check"),
|
|
137
|
+
list_: bool = typer.Option(False, "--list", "-l", help="List available targets"),
|
|
138
|
+
releases: bool = typer.Option(False, "--releases", "-r", help="List releases for target"),
|
|
139
|
+
rollback: bool = typer.Option(False, "--rollback", help="Rollback to previous release"),
|
|
140
|
+
):
|
|
141
|
+
"""Deploy to SSH/VPS server with rollback support.
|
|
142
|
+
|
|
143
|
+
Features:
|
|
144
|
+
- Release management (keeps N previous releases)
|
|
145
|
+
- Health checks (HTTP, TCP, process, command)
|
|
146
|
+
- Automatic rollback on failed health check
|
|
147
|
+
"""
|
|
148
|
+
if list_:
|
|
149
|
+
deploy.list_targets()
|
|
150
|
+
elif releases:
|
|
151
|
+
if not target:
|
|
152
|
+
console.print("[red]Error:[/] Target required for --releases")
|
|
153
|
+
raise typer.Exit(1)
|
|
154
|
+
deploy.releases_list(target)
|
|
155
|
+
elif rollback:
|
|
156
|
+
if not target:
|
|
157
|
+
console.print("[red]Error:[/] Target required for --rollback")
|
|
158
|
+
raise typer.Exit(1)
|
|
159
|
+
deploy.rollback(target)
|
|
160
|
+
else:
|
|
161
|
+
deploy.deploy(target=target, dry_run=dry_run, skip_health_check=skip_health)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
# ============================================================================
|
|
165
|
+
# SYNC
|
|
166
|
+
# ============================================================================
|
|
167
|
+
|
|
168
|
+
@app.command("sync")
|
|
169
|
+
def sync_cmd(
|
|
170
|
+
directory: Optional[str] = typer.Argument(None, help="Directory to sync"),
|
|
171
|
+
status: bool = typer.Option(False, "--status", "-s", help="Show status only"),
|
|
172
|
+
no_push: bool = typer.Option(False, "--no-push", help="Pull only, don't push"),
|
|
173
|
+
upstream: bool = typer.Option(False, "--upstream", "-u", help="Sync with upstream (for forks)"),
|
|
174
|
+
):
|
|
175
|
+
"""Sync multiple repositories (pull + push)."""
|
|
176
|
+
path = Path(directory) if directory else None
|
|
177
|
+
|
|
178
|
+
if upstream:
|
|
179
|
+
sync.sync_upstream(cwd=path)
|
|
180
|
+
elif status:
|
|
181
|
+
sync.status_all(directory=path)
|
|
182
|
+
else:
|
|
183
|
+
sync.sync_all(directory=path, push=not no_push)
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
# ============================================================================
|
|
187
|
+
# SECRETS
|
|
188
|
+
# ============================================================================
|
|
189
|
+
|
|
190
|
+
@app.command("secrets")
|
|
191
|
+
def secrets_cmd(
|
|
192
|
+
action: str = typer.Argument("scan", help="Action: scan, add, list, hook"),
|
|
193
|
+
name: Optional[str] = typer.Argument(None, help="Secret name (for add)"),
|
|
194
|
+
staged: bool = typer.Option(False, "--staged", help="Scan staged files only"),
|
|
195
|
+
quiet: bool = typer.Option(False, "--quiet", "-q", help="Minimal output"),
|
|
196
|
+
):
|
|
197
|
+
"""Scan for secrets or manage GitHub secrets."""
|
|
198
|
+
if action == "scan":
|
|
199
|
+
findings = secrets.scan(staged_only=staged, quiet=quiet)
|
|
200
|
+
if findings and not quiet:
|
|
201
|
+
raise typer.Exit(1)
|
|
202
|
+
elif action == "add":
|
|
203
|
+
if not name:
|
|
204
|
+
console.print("[red]Error:[/] Secret name required")
|
|
205
|
+
raise typer.Exit(1)
|
|
206
|
+
secrets.add_github_secret(name)
|
|
207
|
+
elif action == "list":
|
|
208
|
+
secrets.list_github_secrets()
|
|
209
|
+
elif action == "hook":
|
|
210
|
+
secrets.install_pre_commit_hook()
|
|
211
|
+
else:
|
|
212
|
+
console.print(f"[red]Unknown action:[/] {action}")
|
|
213
|
+
console.print("Use: scan, add, list, hook")
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
# ============================================================================
|
|
217
|
+
# PR
|
|
218
|
+
# ============================================================================
|
|
219
|
+
|
|
220
|
+
@app.command("pr")
|
|
221
|
+
def pr_cmd(
|
|
222
|
+
title: Optional[str] = typer.Argument(None, help="PR title"),
|
|
223
|
+
base: Optional[str] = typer.Option(None, "--base", "-b", help="Base branch"),
|
|
224
|
+
draft: bool = typer.Option(False, "--draft", "-d", help="Create as draft"),
|
|
225
|
+
list_: bool = typer.Option(False, "--list", "-l", help="List open PRs"),
|
|
226
|
+
):
|
|
227
|
+
"""Create or list pull requests."""
|
|
228
|
+
if list_:
|
|
229
|
+
pr.list_prs()
|
|
230
|
+
else:
|
|
231
|
+
pr.create_pr(title=title, base=base, draft=draft)
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
# ============================================================================
|
|
235
|
+
# ORIGIN (Multi-origin management)
|
|
236
|
+
# ============================================================================
|
|
237
|
+
|
|
238
|
+
@app.command("origin")
|
|
239
|
+
def origin_cmd(
|
|
240
|
+
action: str = typer.Argument("list", help="Action: list, add, remove, push, status, mirror"),
|
|
241
|
+
name: Optional[str] = typer.Argument(None, help="Remote name or repo name"),
|
|
242
|
+
url: Optional[str] = typer.Option(None, "--url", "-u", help="Remote URL"),
|
|
243
|
+
provider: Optional[str] = typer.Option(None, "--provider", "-p", help="Provider: github, gitlab, bitbucket, gitea"),
|
|
244
|
+
owner: Optional[str] = typer.Option(None, "--owner", "-o", help="Owner/organization/group"),
|
|
245
|
+
host: Optional[str] = typer.Option(None, "--host", help="Host for self-hosted (GitLab, Gitea)"),
|
|
246
|
+
verbose: bool = typer.Option(False, "--verbose", "-v", help="Show more details"),
|
|
247
|
+
):
|
|
248
|
+
"""Manage multiple git remotes.
|
|
249
|
+
|
|
250
|
+
Actions:
|
|
251
|
+
- list: Show all configured remotes
|
|
252
|
+
- add: Add a new remote
|
|
253
|
+
- remove: Remove a remote
|
|
254
|
+
- push: Push to all remotes
|
|
255
|
+
- status: Check sync status of all remotes
|
|
256
|
+
- mirror: Set up a mirror to another provider
|
|
257
|
+
"""
|
|
258
|
+
if action == "list":
|
|
259
|
+
origins.list_origins(verbose=verbose)
|
|
260
|
+
elif action == "add":
|
|
261
|
+
origins.add_origin(name=name, url=url, provider=provider, owner=owner, host=host)
|
|
262
|
+
elif action == "remove":
|
|
263
|
+
if not name:
|
|
264
|
+
console.print("[red]Error:[/] Remote name required")
|
|
265
|
+
raise typer.Exit(1)
|
|
266
|
+
origins.remove_origin(name)
|
|
267
|
+
elif action == "push":
|
|
268
|
+
origins.push_all()
|
|
269
|
+
elif action == "status":
|
|
270
|
+
origins.status_all()
|
|
271
|
+
elif action == "mirror":
|
|
272
|
+
if not provider:
|
|
273
|
+
console.print("[red]Error:[/] --provider required for mirror")
|
|
274
|
+
console.print("Example: swoosh origin mirror --provider gitlab")
|
|
275
|
+
raise typer.Exit(1)
|
|
276
|
+
origins.setup_mirror(
|
|
277
|
+
source_remote=name or "origin",
|
|
278
|
+
target_provider=provider,
|
|
279
|
+
target_owner=owner,
|
|
280
|
+
target_host=host,
|
|
281
|
+
)
|
|
282
|
+
else:
|
|
283
|
+
console.print(f"[red]Unknown action:[/] {action}")
|
|
284
|
+
console.print("Use: list, add, remove, push, status, mirror")
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
# ============================================================================
|
|
288
|
+
# HOOK
|
|
289
|
+
# ============================================================================
|
|
290
|
+
|
|
291
|
+
@app.command("hook")
|
|
292
|
+
def hook_cmd(
|
|
293
|
+
action: str = typer.Argument(..., help="Action: install, remove, status"),
|
|
294
|
+
multi_origin: bool = typer.Option(False, "--multi-origin", "-m", help="Enable multi-origin push"),
|
|
295
|
+
):
|
|
296
|
+
"""Manage auto-push git hooks."""
|
|
297
|
+
if action == "install":
|
|
298
|
+
hooks.install(multi_origin=multi_origin)
|
|
299
|
+
elif action == "remove":
|
|
300
|
+
hooks.remove()
|
|
301
|
+
elif action == "status":
|
|
302
|
+
hooks.status()
|
|
303
|
+
elif action == "multi":
|
|
304
|
+
hooks.enable_multi_origin()
|
|
305
|
+
else:
|
|
306
|
+
console.print(f"[red]Unknown action:[/] {action}")
|
|
307
|
+
console.print("Use: install, remove, status")
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
# ============================================================================
|
|
311
|
+
# TEMPLATES
|
|
312
|
+
# ============================================================================
|
|
313
|
+
|
|
314
|
+
@app.command("templates")
|
|
315
|
+
def templates_cmd(
|
|
316
|
+
action: str = typer.Argument("list", help="Action: list, show"),
|
|
317
|
+
name: Optional[str] = typer.Argument(None, help="Template name"),
|
|
318
|
+
):
|
|
319
|
+
"""List or show CI/CD templates."""
|
|
320
|
+
if action == "list":
|
|
321
|
+
templates.list_templates()
|
|
322
|
+
elif action == "show" and name:
|
|
323
|
+
templates.show_template(name)
|
|
324
|
+
else:
|
|
325
|
+
templates.list_templates()
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
# ============================================================================
|
|
329
|
+
# CLONE
|
|
330
|
+
# ============================================================================
|
|
331
|
+
|
|
332
|
+
@app.command("clone")
|
|
333
|
+
def clone_cmd(
|
|
334
|
+
name: Optional[str] = typer.Argument(None, help="Repository name or owner/repo"),
|
|
335
|
+
owner: Optional[str] = typer.Option(None, "--owner", "-o", help="Repository owner"),
|
|
336
|
+
dest: Optional[str] = typer.Option(None, "--dest", "-d", help="Destination directory"),
|
|
337
|
+
no_hook: bool = typer.Option(False, "--no-hook", help="Skip auto-push hook"),
|
|
338
|
+
):
|
|
339
|
+
"""Clone a repository from GitHub."""
|
|
340
|
+
clone.clone_repo(
|
|
341
|
+
name=name,
|
|
342
|
+
owner=owner,
|
|
343
|
+
dest=Path(dest) if dest else None,
|
|
344
|
+
setup_hook=not no_hook,
|
|
345
|
+
)
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
@app.command("clone-all")
|
|
349
|
+
def clone_all_cmd(
|
|
350
|
+
owner: Optional[str] = typer.Option(None, "--owner", "-o", help="Clone from specific owner/org"),
|
|
351
|
+
dest: Optional[str] = typer.Option(None, "--dest", "-d", help="Destination directory"),
|
|
352
|
+
no_private: bool = typer.Option(False, "--no-private", help="Skip private repos"),
|
|
353
|
+
no_hooks: bool = typer.Option(False, "--no-hooks", help="Skip hooks"),
|
|
354
|
+
):
|
|
355
|
+
"""Clone all your repositories."""
|
|
356
|
+
clone.clone_all(
|
|
357
|
+
owner=owner,
|
|
358
|
+
dest_dir=Path(dest) if dest else None,
|
|
359
|
+
include_private=not no_private,
|
|
360
|
+
setup_hooks=not no_hooks,
|
|
361
|
+
)
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
# ============================================================================
|
|
365
|
+
# REPOS
|
|
366
|
+
# ============================================================================
|
|
367
|
+
|
|
368
|
+
@app.command("repos")
|
|
369
|
+
def repos_cmd(
|
|
370
|
+
owner: Optional[str] = typer.Option(None, "--owner", "-o", help="List from specific owner"),
|
|
371
|
+
limit: int = typer.Option(20, "--limit", "-n", help="Number of repos"),
|
|
372
|
+
):
|
|
373
|
+
"""List your GitHub repositories."""
|
|
374
|
+
clone.list_repos(owner=owner, limit=limit)
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
# ============================================================================
|
|
378
|
+
# DOCTOR & CONFIG
|
|
379
|
+
# ============================================================================
|
|
380
|
+
|
|
381
|
+
@app.command("doctor")
|
|
382
|
+
def doctor_cmd():
|
|
383
|
+
"""Check system dependencies and configuration."""
|
|
384
|
+
check.run_doctor()
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
@app.command("config")
|
|
388
|
+
def config_cmd(
|
|
389
|
+
show: bool = typer.Option(False, "--show", "-s", help="Show current config"),
|
|
390
|
+
github_user: Optional[str] = typer.Option(None, "--github-user", "-u", help="Set GitHub username"),
|
|
391
|
+
template: Optional[str] = typer.Option(None, "--template", "-t", help="Set default template"),
|
|
392
|
+
):
|
|
393
|
+
"""Configure Swoosh defaults."""
|
|
394
|
+
if show:
|
|
395
|
+
config.show_config()
|
|
396
|
+
elif github_user or template:
|
|
397
|
+
config.update_config(github_user=github_user, default_template=template)
|
|
398
|
+
else:
|
|
399
|
+
config.interactive_config()
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
# ============================================================================
|
|
403
|
+
# AUTH
|
|
404
|
+
# ============================================================================
|
|
405
|
+
|
|
406
|
+
@app.command("auth")
|
|
407
|
+
def auth_cmd(
|
|
408
|
+
method: Optional[str] = typer.Argument(None, help="Method: ssh, token, oauth, status, logout"),
|
|
409
|
+
token: Optional[str] = typer.Option(None, "--token", "-t", help="Personal Access Token"),
|
|
410
|
+
username: Optional[str] = typer.Option(None, "--name", "-n", help="Git username"),
|
|
411
|
+
email: Optional[str] = typer.Option(None, "--email", "-e", help="Git email"),
|
|
412
|
+
ssh: bool = typer.Option(False, "--ssh", "-s", help="Use/generate SSH key"),
|
|
413
|
+
):
|
|
414
|
+
"""Authenticate with GitHub.
|
|
415
|
+
|
|
416
|
+
Methods:
|
|
417
|
+
- ssh: Use or generate SSH key
|
|
418
|
+
- token: Use Personal Access Token
|
|
419
|
+
- oauth: Browser-based login
|
|
420
|
+
- status: Show auth status
|
|
421
|
+
- logout: Logout from GitHub
|
|
422
|
+
"""
|
|
423
|
+
if method == "status" or (not method and not token and not ssh):
|
|
424
|
+
if not token and not ssh and not username and not email:
|
|
425
|
+
auth.status()
|
|
426
|
+
return
|
|
427
|
+
|
|
428
|
+
if method == "logout":
|
|
429
|
+
auth.logout()
|
|
430
|
+
return
|
|
431
|
+
|
|
432
|
+
auth.login(
|
|
433
|
+
method="ssh" if ssh else method,
|
|
434
|
+
token=token,
|
|
435
|
+
username=username,
|
|
436
|
+
email=email,
|
|
437
|
+
generate_key=ssh,
|
|
438
|
+
)
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
if __name__ == "__main__":
|
|
442
|
+
app()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Swoosh modules."""
|