sum-cli 3.0.0__py3-none-any.whl → 3.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.
- sum/cli.py +3 -3
- sum/commands/__init__.py +2 -2
- sum/commands/init.py +97 -15
- sum/commands/promote.py +29 -10
- sum/commands/setup.py +143 -0
- sum/setup/git_ops.py +69 -50
- sum/setup/orchestrator.py +2 -2
- sum/setup/scaffold.py +9 -12
- sum/setup/site_orchestrator.py +29 -4
- sum/site_config.py +129 -0
- sum/system_config.py +0 -37
- sum/utils/environment.py +2 -2
- sum/utils/validation.py +2 -2
- sum_cli-3.1.0.dist-info/METADATA +230 -0
- {sum_cli-3.0.0.dist-info → sum_cli-3.1.0.dist-info}/RECORD +19 -19
- sum/commands/run.py +0 -96
- sum/docs/USER_GUIDE.md +0 -663
- sum_cli-3.0.0.dist-info/METADATA +0 -127
- {sum_cli-3.0.0.dist-info → sum_cli-3.1.0.dist-info}/WHEEL +0 -0
- {sum_cli-3.0.0.dist-info → sum_cli-3.1.0.dist-info}/entry_points.txt +0 -0
- {sum_cli-3.0.0.dist-info → sum_cli-3.1.0.dist-info}/licenses/LICENSE +0 -0
- {sum_cli-3.0.0.dist-info → sum_cli-3.1.0.dist-info}/top_level.txt +0 -0
sum/setup/scaffold.py
CHANGED
|
@@ -17,7 +17,6 @@ from typing import cast
|
|
|
17
17
|
|
|
18
18
|
from sum.exceptions import SetupError, ThemeNotFoundError, ThemeValidationError
|
|
19
19
|
from sum.setup.remote_themes import parse_theme_spec, resolve_remote_theme
|
|
20
|
-
from sum.system_config import ConfigurationError, get_system_config
|
|
21
20
|
from sum.utils.environment import find_monorepo_root
|
|
22
21
|
from sum.utils.project import (
|
|
23
22
|
ProjectNaming,
|
|
@@ -366,23 +365,20 @@ def _copy_seeders_and_content(
|
|
|
366
365
|
shutil.copytree(content_profile_dir, target_content, ignore=ignore)
|
|
367
366
|
|
|
368
367
|
|
|
369
|
-
def _configure_ci_workflows(project_path: Path) -> None:
|
|
368
|
+
def _configure_ci_workflows(project_path: Path, git_provider: str | None) -> None:
|
|
370
369
|
"""Keep only the CI workflow directory for the configured git provider.
|
|
371
370
|
|
|
372
371
|
Boilerplate contains both .github/workflows/ and .gitea/workflows/.
|
|
373
|
-
This removes the one that doesn't match the
|
|
372
|
+
This removes the one that doesn't match the git_provider.
|
|
373
|
+
|
|
374
|
+
Args:
|
|
375
|
+
project_path: Path to the project.
|
|
376
|
+
git_provider: "github", "gitea", or None (defaults to github).
|
|
374
377
|
"""
|
|
375
378
|
github_dir = project_path / ".github"
|
|
376
379
|
gitea_dir = project_path / ".gitea"
|
|
377
380
|
|
|
378
|
-
|
|
379
|
-
try:
|
|
380
|
-
config = get_system_config()
|
|
381
|
-
provider = config.agency.git_provider
|
|
382
|
-
except ConfigurationError:
|
|
383
|
-
# Config not available (e.g., running in test without config)
|
|
384
|
-
# Default to GitHub workflows
|
|
385
|
-
provider = "github"
|
|
381
|
+
provider = git_provider or "github"
|
|
386
382
|
|
|
387
383
|
if provider == "gitea":
|
|
388
384
|
# Remove GitHub workflows, keep Gitea
|
|
@@ -399,6 +395,7 @@ def scaffold_project(
|
|
|
399
395
|
clients_dir: Path,
|
|
400
396
|
theme_slug: str = DEFAULT_THEME_SLUG,
|
|
401
397
|
seed_profile: str = "starter",
|
|
398
|
+
git_provider: str | None = None,
|
|
402
399
|
) -> Path:
|
|
403
400
|
try:
|
|
404
401
|
naming = validate_project_name(project_name)
|
|
@@ -460,7 +457,7 @@ def scaffold_project(
|
|
|
460
457
|
_rename_project_package_dir(project_path, naming)
|
|
461
458
|
_replace_placeholders(project_path, naming)
|
|
462
459
|
_create_env_from_example(project_path)
|
|
463
|
-
_configure_ci_workflows(project_path)
|
|
460
|
+
_configure_ci_workflows(project_path, git_provider)
|
|
464
461
|
_copy_theme_to_active(project_path, theme_source_dir, theme_slug)
|
|
465
462
|
_write_theme_config(project_path, theme_slug, theme_manifest.version)
|
|
466
463
|
_copy_seeders_and_content(
|
sum/setup/site_orchestrator.py
CHANGED
|
@@ -15,6 +15,7 @@ from __future__ import annotations
|
|
|
15
15
|
|
|
16
16
|
import shutil
|
|
17
17
|
from dataclasses import dataclass
|
|
18
|
+
from datetime import UTC, datetime
|
|
18
19
|
from pathlib import Path
|
|
19
20
|
|
|
20
21
|
from sum.exceptions import SeedError, SetupError
|
|
@@ -38,6 +39,7 @@ from sum.setup.infrastructure import (
|
|
|
38
39
|
)
|
|
39
40
|
from sum.setup.scaffold import scaffold_project
|
|
40
41
|
from sum.setup.venv import VenvManager
|
|
42
|
+
from sum.site_config import GitConfig, SiteConfig
|
|
41
43
|
from sum.system_config import SystemConfig, get_system_config
|
|
42
44
|
from sum.utils.django import DjangoCommandExecutor
|
|
43
45
|
from sum.utils.environment import ExecutionMode
|
|
@@ -69,9 +71,9 @@ class SiteSetupConfig:
|
|
|
69
71
|
seed_profile: str | None = "starter"
|
|
70
72
|
content_path: str | None = None
|
|
71
73
|
superuser_username: str = "admin"
|
|
72
|
-
skip_git: bool = False
|
|
73
74
|
skip_systemd: bool = False
|
|
74
75
|
skip_caddy: bool = False
|
|
76
|
+
git_config: GitConfig | None = None # None means --no-git
|
|
75
77
|
|
|
76
78
|
|
|
77
79
|
class SiteOrchestrator:
|
|
@@ -147,6 +149,11 @@ class SiteOrchestrator:
|
|
|
147
149
|
site_slug,
|
|
148
150
|
setup_config.theme_slug,
|
|
149
151
|
setup_config.seed_profile,
|
|
152
|
+
git_provider=(
|
|
153
|
+
setup_config.git_config.provider
|
|
154
|
+
if setup_config.git_config
|
|
155
|
+
else None
|
|
156
|
+
),
|
|
150
157
|
)
|
|
151
158
|
# Copy .env to app directory (Django reads from app/.env, not site root)
|
|
152
159
|
self._copy_env_to_app()
|
|
@@ -212,7 +219,7 @@ class SiteOrchestrator:
|
|
|
212
219
|
repo_url = setup_git_for_site(
|
|
213
220
|
self._app_dir,
|
|
214
221
|
site_slug,
|
|
215
|
-
|
|
222
|
+
git_config=setup_config.git_config,
|
|
216
223
|
)
|
|
217
224
|
if repo_url:
|
|
218
225
|
self._done(current_step, total_steps, f"Repository created: {repo_url}")
|
|
@@ -246,7 +253,19 @@ class SiteOrchestrator:
|
|
|
246
253
|
start_site_service(site_slug, self.sys_config)
|
|
247
254
|
self._done(current_step, total_steps, "Service started")
|
|
248
255
|
|
|
249
|
-
# Step 18: Write
|
|
256
|
+
# Step 18: Write site config
|
|
257
|
+
current_step += 1
|
|
258
|
+
self._progress(current_step, total_steps, "Writing site config")
|
|
259
|
+
site_config = SiteConfig(
|
|
260
|
+
slug=site_slug,
|
|
261
|
+
theme=setup_config.theme_slug,
|
|
262
|
+
created=datetime.now(UTC),
|
|
263
|
+
git=setup_config.git_config,
|
|
264
|
+
)
|
|
265
|
+
site_config.save(self._site_dir)
|
|
266
|
+
self._done(current_step, total_steps, "Site config saved")
|
|
267
|
+
|
|
268
|
+
# Step 19: Write credentials file
|
|
250
269
|
current_step += 1
|
|
251
270
|
self._progress(current_step, total_steps, "Saving credentials")
|
|
252
271
|
creds_path = write_credentials_file(
|
|
@@ -317,6 +336,7 @@ class SiteOrchestrator:
|
|
|
317
336
|
if not config.skip_systemd:
|
|
318
337
|
steps.append("start_systemd_service")
|
|
319
338
|
|
|
339
|
+
steps.append("write_site_config")
|
|
320
340
|
steps.append("write_credentials_file")
|
|
321
341
|
|
|
322
342
|
return len(steps)
|
|
@@ -330,7 +350,11 @@ class SiteOrchestrator:
|
|
|
330
350
|
OutputFormatter.progress(step, total, message, "✅")
|
|
331
351
|
|
|
332
352
|
def _scaffold_to_app_dir(
|
|
333
|
-
self,
|
|
353
|
+
self,
|
|
354
|
+
site_slug: str,
|
|
355
|
+
theme_slug: str,
|
|
356
|
+
seed_profile: str | None = None,
|
|
357
|
+
git_provider: str | None = None,
|
|
334
358
|
) -> None:
|
|
335
359
|
"""Scaffold project code to the app directory.
|
|
336
360
|
|
|
@@ -358,6 +382,7 @@ class SiteOrchestrator:
|
|
|
358
382
|
clients_dir=self._site_dir,
|
|
359
383
|
theme_slug=theme_slug,
|
|
360
384
|
seed_profile=seed_profile or "starter",
|
|
385
|
+
git_provider=git_provider,
|
|
361
386
|
)
|
|
362
387
|
|
|
363
388
|
# Rename from site_slug to app
|
sum/site_config.py
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"""Per-site configuration stored at /srv/sum/<site>/.sum/config.yml."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from datetime import datetime
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
import yaml
|
|
10
|
+
from sum.exceptions import SumCliError
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class SiteConfigError(SumCliError):
|
|
14
|
+
"""Site configuration error."""
|
|
15
|
+
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass
|
|
20
|
+
class GitConfig:
|
|
21
|
+
"""Git configuration for a site."""
|
|
22
|
+
|
|
23
|
+
provider: str # "github" or "gitea"
|
|
24
|
+
org: str
|
|
25
|
+
url: str | None = None # Required for gitea
|
|
26
|
+
ssh_port: int = 22
|
|
27
|
+
token_env: str = "GITEA_TOKEN"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass
|
|
31
|
+
class SiteConfig:
|
|
32
|
+
"""Per-site configuration."""
|
|
33
|
+
|
|
34
|
+
slug: str
|
|
35
|
+
theme: str
|
|
36
|
+
created: datetime
|
|
37
|
+
git: GitConfig | None # None if --no-git
|
|
38
|
+
|
|
39
|
+
@classmethod
|
|
40
|
+
def load(cls, site_dir: Path) -> SiteConfig:
|
|
41
|
+
"""Load site config from .sum/config.yml."""
|
|
42
|
+
config_path = site_dir / ".sum" / "config.yml"
|
|
43
|
+
if not config_path.exists():
|
|
44
|
+
raise SiteConfigError(
|
|
45
|
+
f"Site config not found: {config_path}\n"
|
|
46
|
+
f"This site may have been created before site configs were introduced."
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
try:
|
|
50
|
+
with open(config_path) as f:
|
|
51
|
+
data = yaml.safe_load(f)
|
|
52
|
+
except yaml.YAMLError as exc:
|
|
53
|
+
raise SiteConfigError(
|
|
54
|
+
f"Invalid YAML in site config: {config_path}\n{exc}"
|
|
55
|
+
) from exc
|
|
56
|
+
|
|
57
|
+
if not isinstance(data, dict):
|
|
58
|
+
raise SiteConfigError(
|
|
59
|
+
f"Site config is empty or not a mapping: {config_path}"
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
try:
|
|
63
|
+
site_data = data["site"]
|
|
64
|
+
slug = site_data["slug"]
|
|
65
|
+
theme = site_data["theme"]
|
|
66
|
+
created_raw = site_data["created"]
|
|
67
|
+
except KeyError as exc:
|
|
68
|
+
raise SiteConfigError(
|
|
69
|
+
f"Missing required field '{exc.args[0]}' in site config: {config_path}"
|
|
70
|
+
) from exc
|
|
71
|
+
|
|
72
|
+
try:
|
|
73
|
+
created_dt = datetime.fromisoformat(created_raw)
|
|
74
|
+
except (TypeError, ValueError) as exc:
|
|
75
|
+
raise SiteConfigError(
|
|
76
|
+
f"Invalid datetime format for 'site.created' in: {config_path}"
|
|
77
|
+
) from exc
|
|
78
|
+
|
|
79
|
+
git_data = data.get("git")
|
|
80
|
+
git_config = None
|
|
81
|
+
if git_data:
|
|
82
|
+
try:
|
|
83
|
+
git_config = GitConfig(
|
|
84
|
+
provider=git_data["provider"],
|
|
85
|
+
org=git_data["org"],
|
|
86
|
+
url=git_data.get("url"),
|
|
87
|
+
ssh_port=git_data.get("ssh_port", 22),
|
|
88
|
+
token_env=git_data.get("token_env", "GITEA_TOKEN"),
|
|
89
|
+
)
|
|
90
|
+
except KeyError as exc:
|
|
91
|
+
raise SiteConfigError(
|
|
92
|
+
f"Missing required git field '{exc.args[0]}' in: {config_path}"
|
|
93
|
+
) from exc
|
|
94
|
+
|
|
95
|
+
return cls(
|
|
96
|
+
slug=slug,
|
|
97
|
+
theme=theme,
|
|
98
|
+
created=created_dt,
|
|
99
|
+
git=git_config,
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
def save(self, site_dir: Path) -> None:
|
|
103
|
+
"""Save site config to .sum/config.yml."""
|
|
104
|
+
config_dir = site_dir / ".sum"
|
|
105
|
+
config_dir.mkdir(parents=True, exist_ok=True)
|
|
106
|
+
config_path = config_dir / "config.yml"
|
|
107
|
+
|
|
108
|
+
data: dict = {
|
|
109
|
+
"site": {
|
|
110
|
+
"slug": self.slug,
|
|
111
|
+
"theme": self.theme,
|
|
112
|
+
"created": self.created.isoformat(),
|
|
113
|
+
},
|
|
114
|
+
"git": None,
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if self.git:
|
|
118
|
+
git_data: dict = {
|
|
119
|
+
"provider": self.git.provider,
|
|
120
|
+
"org": self.git.org,
|
|
121
|
+
}
|
|
122
|
+
if self.git.provider == "gitea":
|
|
123
|
+
git_data["url"] = self.git.url
|
|
124
|
+
git_data["ssh_port"] = self.git.ssh_port
|
|
125
|
+
git_data["token_env"] = self.git.token_env
|
|
126
|
+
data["git"] = git_data
|
|
127
|
+
|
|
128
|
+
with open(config_path, "w") as f:
|
|
129
|
+
yaml.dump(data, f, default_flow_style=False, sort_keys=False)
|
sum/system_config.py
CHANGED
|
@@ -33,43 +33,6 @@ class AgencyConfig:
|
|
|
33
33
|
"""Agency identification settings."""
|
|
34
34
|
|
|
35
35
|
name: str
|
|
36
|
-
# Git provider: "github" (default) or "gitea"
|
|
37
|
-
git_provider: str = "github"
|
|
38
|
-
# GitHub-specific (required when git_provider=github)
|
|
39
|
-
github_org: str | None = None
|
|
40
|
-
# Gitea-specific (required when git_provider=gitea)
|
|
41
|
-
gitea_url: str | None = None
|
|
42
|
-
gitea_org: str | None = None
|
|
43
|
-
gitea_token_env: str = "GITEA_TOKEN" # env var name containing API token
|
|
44
|
-
|
|
45
|
-
def __post_init__(self) -> None:
|
|
46
|
-
"""Validate provider-specific fields."""
|
|
47
|
-
if self.git_provider not in ("github", "gitea"):
|
|
48
|
-
raise ConfigurationError(
|
|
49
|
-
f"Invalid git_provider '{self.git_provider}'. Must be 'github' or 'gitea'."
|
|
50
|
-
)
|
|
51
|
-
|
|
52
|
-
if self.git_provider == "github":
|
|
53
|
-
if not self.github_org:
|
|
54
|
-
raise ConfigurationError(
|
|
55
|
-
"github_org is required when git_provider is 'github'"
|
|
56
|
-
)
|
|
57
|
-
elif self.git_provider == "gitea":
|
|
58
|
-
if not self.gitea_url:
|
|
59
|
-
raise ConfigurationError(
|
|
60
|
-
"gitea_url is required when git_provider is 'gitea'"
|
|
61
|
-
)
|
|
62
|
-
if not self.gitea_org:
|
|
63
|
-
raise ConfigurationError(
|
|
64
|
-
"gitea_org is required when git_provider is 'gitea'"
|
|
65
|
-
)
|
|
66
|
-
|
|
67
|
-
@property
|
|
68
|
-
def org(self) -> str:
|
|
69
|
-
"""Get the organization name for the configured provider."""
|
|
70
|
-
if self.git_provider == "gitea":
|
|
71
|
-
return self.gitea_org or ""
|
|
72
|
-
return self.github_org or ""
|
|
73
36
|
|
|
74
37
|
|
|
75
38
|
@dataclass
|
sum/utils/environment.py
CHANGED
sum/utils/validation.py
CHANGED
|
@@ -7,7 +7,7 @@ import re
|
|
|
7
7
|
import subprocess
|
|
8
8
|
import sys
|
|
9
9
|
from dataclasses import dataclass
|
|
10
|
-
from enum import
|
|
10
|
+
from enum import StrEnum
|
|
11
11
|
from pathlib import Path
|
|
12
12
|
from typing import cast
|
|
13
13
|
|
|
@@ -16,7 +16,7 @@ from sum.utils.django import DjangoCommandExecutor
|
|
|
16
16
|
from sum.utils.environment import ExecutionMode, find_monorepo_root
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
class ValidationStatus(
|
|
19
|
+
class ValidationStatus(StrEnum):
|
|
20
20
|
OK = "ok"
|
|
21
21
|
FAIL = "fail"
|
|
22
22
|
SKIP = "skip"
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sum-cli
|
|
3
|
+
Version: 3.1.0
|
|
4
|
+
Summary: SUM Platform CLI: single control plane for site lifecycle management
|
|
5
|
+
Author: Mark Ashton
|
|
6
|
+
License-Expression: BSD-3-Clause
|
|
7
|
+
Project-URL: Homepage, https://github.com/markashton480/sum-platform
|
|
8
|
+
Project-URL: Repository, https://github.com/markashton480/sum-platform
|
|
9
|
+
Project-URL: Issues, https://github.com/markashton480/sum-platform/issues
|
|
10
|
+
Project-URL: Documentation, https://github.com/markashton480/sum-platform/tree/main/docs/dev/cli.md
|
|
11
|
+
Keywords: sum,cli,django,wagtail
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
19
|
+
Classifier: Topic :: Software Development :: Code Generators
|
|
20
|
+
Requires-Python: >=3.12
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: click>=8.1.7
|
|
24
|
+
Requires-Dist: pyyaml>=6.0
|
|
25
|
+
Provides-Extra: gitea
|
|
26
|
+
Requires-Dist: httpx>=0.27.0; extra == "gitea"
|
|
27
|
+
Dynamic: license-file
|
|
28
|
+
|
|
29
|
+
# SUM CLI (v3.1)
|
|
30
|
+
|
|
31
|
+
[](https://pypi.org/project/sum-cli/)
|
|
32
|
+
|
|
33
|
+
The SUM CLI is the single control plane for deploying and managing SUM Platform client sites on staging and production servers.
|
|
34
|
+
|
|
35
|
+
## Install
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install sum-cli
|
|
39
|
+
sum-platform --version
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### With Gitea Support
|
|
43
|
+
|
|
44
|
+
If using Gitea instead of GitHub for repository hosting:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
pip install sum-cli[gitea]
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Initial Setup
|
|
51
|
+
|
|
52
|
+
Before using the CLI, configure your infrastructure settings:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
sudo sum-platform setup
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
This interactive command creates `/etc/sum/config.yml` with your staging/production server settings.
|
|
59
|
+
|
|
60
|
+
## Commands
|
|
61
|
+
|
|
62
|
+
| Command | Description | Requires Sudo |
|
|
63
|
+
|---------|-------------|---------------|
|
|
64
|
+
| `setup` | Configure infrastructure settings | Yes |
|
|
65
|
+
| `init` | Create new site at `/srv/sum/<name>/` | Yes |
|
|
66
|
+
| `update` | Pull updates, migrate, restart | No (staging) |
|
|
67
|
+
| `backup` | Database and media backup | No |
|
|
68
|
+
| `promote` | Deploy staging site to production | No |
|
|
69
|
+
| `check` | Validate project setup | No |
|
|
70
|
+
| `themes` | List available themes | No |
|
|
71
|
+
|
|
72
|
+
## Creating Sites
|
|
73
|
+
|
|
74
|
+
### With GitHub
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
sudo sum-platform init acme --git-provider github --git-org acme-corp
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### With Gitea
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
sudo sum-platform init acme --git-provider gitea --git-org clients \
|
|
84
|
+
--gitea-url https://gitea.agency.com
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### With Gitea (Custom SSH Port)
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
sudo sum-platform init acme --git-provider gitea --git-org clients \
|
|
91
|
+
--gitea-url https://gitea.agency.com --gitea-ssh-port 2222
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Without Git
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
sudo sum-platform init acme --no-git
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Additional Init Options
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
sudo sum-platform init acme --git-provider github --git-org acme-corp \
|
|
104
|
+
--theme theme_a \
|
|
105
|
+
--profile sage-stone \
|
|
106
|
+
--content-path /path/to/custom/content
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Managing Sites
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
# Update a deployed site (pull, migrate, restart)
|
|
113
|
+
sum-platform update acme
|
|
114
|
+
|
|
115
|
+
# Backup database and media
|
|
116
|
+
sum-platform backup acme --include-media
|
|
117
|
+
|
|
118
|
+
# Promote staging to production
|
|
119
|
+
sum-platform promote acme --domain acme.example.com
|
|
120
|
+
|
|
121
|
+
# Validate project setup
|
|
122
|
+
sum-platform check acme
|
|
123
|
+
|
|
124
|
+
# List available themes
|
|
125
|
+
sum-platform themes
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Configuration
|
|
129
|
+
|
|
130
|
+
### Global Config (`/etc/sum/config.yml`)
|
|
131
|
+
|
|
132
|
+
Infrastructure settings only. Created via `sum-platform setup`.
|
|
133
|
+
|
|
134
|
+
```yaml
|
|
135
|
+
agency:
|
|
136
|
+
name: Your Agency Name
|
|
137
|
+
|
|
138
|
+
staging:
|
|
139
|
+
server: staging.example.com
|
|
140
|
+
domain_pattern: "{slug}.staging.example.com"
|
|
141
|
+
base_dir: /srv/sum
|
|
142
|
+
|
|
143
|
+
production:
|
|
144
|
+
server: prod.example.com
|
|
145
|
+
ssh_host: 10.0.0.1
|
|
146
|
+
base_dir: /srv/sum
|
|
147
|
+
|
|
148
|
+
templates:
|
|
149
|
+
dir: /opt/your-ops/infra
|
|
150
|
+
systemd: systemd/sum-site-gunicorn.service.template
|
|
151
|
+
caddy: caddy/Caddyfile.template
|
|
152
|
+
|
|
153
|
+
defaults:
|
|
154
|
+
theme: theme_a
|
|
155
|
+
seed_profile: starter
|
|
156
|
+
deploy_user: deploy
|
|
157
|
+
postgres_port: 5432
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Site Config (`/srv/sum/<site>/.sum/config.yml`)
|
|
161
|
+
|
|
162
|
+
Per-site configuration. Auto-created when you run `init`.
|
|
163
|
+
|
|
164
|
+
**GitHub site:**
|
|
165
|
+
```yaml
|
|
166
|
+
site:
|
|
167
|
+
slug: acme
|
|
168
|
+
theme: theme_a
|
|
169
|
+
created: 2026-02-03T14:30:00Z
|
|
170
|
+
|
|
171
|
+
git:
|
|
172
|
+
provider: github
|
|
173
|
+
org: acme-corp
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
**Gitea site:**
|
|
177
|
+
```yaml
|
|
178
|
+
site:
|
|
179
|
+
slug: acme
|
|
180
|
+
theme: theme_a
|
|
181
|
+
created: 2026-02-03T14:30:00Z
|
|
182
|
+
|
|
183
|
+
git:
|
|
184
|
+
provider: gitea
|
|
185
|
+
org: clients
|
|
186
|
+
url: https://gitea.agency.com
|
|
187
|
+
ssh_port: 2222
|
|
188
|
+
token_env: GITEA_TOKEN
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
**No-git site:**
|
|
192
|
+
```yaml
|
|
193
|
+
site:
|
|
194
|
+
slug: acme
|
|
195
|
+
theme: theme_a
|
|
196
|
+
created: 2026-02-03T14:30:00Z
|
|
197
|
+
|
|
198
|
+
git: null
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## Git Provider Setup
|
|
202
|
+
|
|
203
|
+
### GitHub
|
|
204
|
+
|
|
205
|
+
Requires the GitHub CLI (`gh`) to be installed and authenticated:
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
gh auth login
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Gitea
|
|
212
|
+
|
|
213
|
+
Set the API token environment variable:
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
export GITEA_TOKEN=your-token-here
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
Or use a custom environment variable name:
|
|
220
|
+
|
|
221
|
+
```bash
|
|
222
|
+
sudo sum-platform init acme --git-provider gitea --git-org clients \
|
|
223
|
+
--gitea-url https://gitea.agency.com --gitea-token-env MY_GITEA_TOKEN
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## Development Install (monorepo)
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
pip install -e ./cli
|
|
230
|
+
```
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
sum/__init__.py,sha256=4Hqvsw_wQrS-HCr8d7k9lsTk-whfS-XuLIja6cwe_K8,27
|
|
2
|
-
sum/cli.py,sha256=
|
|
2
|
+
sum/cli.py,sha256=iYsjVSOBXgWpAvYW_DBbtuVAh3AJMHlTBttkRr21FGQ,986
|
|
3
3
|
sum/config.py,sha256=LucWpecznyXfYGKFfMzlYc_YGgQz6YYgEYss1ohqtU0,1500
|
|
4
4
|
sum/exceptions.py,sha256=bcTxqy_MXysc3-_CtLITuA0mea3PJv2gSmm6Opdx038,1172
|
|
5
|
-
sum/
|
|
5
|
+
sum/site_config.py,sha256=sPCICpXvT-X4-jwVo90ZMxNmLbQRcQ5sPouvlKXwJP8,3890
|
|
6
|
+
sum/system_config.py,sha256=8gej6_7Wlhip0OSvaetm_WKYfM45Z40et47WhO5_ruE,8874
|
|
6
7
|
sum/themes_registry.py,sha256=86SJjNHne2t24G5x3FQglL2toqo06-WBGujV598d4w4,5565
|
|
7
8
|
sum/boilerplate/.env.example,sha256=Y3GajiSPEQA3_uCFxzUwWwJEataGgZRySktb6UgaQD8,5038
|
|
8
9
|
sum/boilerplate/.gitignore,sha256=ko-akHlkIhaS9vqz78zAlwMfp0W5z1TE52dBmoFdSu8,397
|
|
@@ -36,37 +37,36 @@ sum/boilerplate/static/client/.gitkeep,sha256=oljA0ouMsVDR4goUYUlxPrm_uxOCM7fAYo
|
|
|
36
37
|
sum/boilerplate/templates/overrides/.gitkeep,sha256=OsCO3X-nPRZHK3_hxkn8B1KZRVmwaXwUfJbHD8ywk90,174
|
|
37
38
|
sum/boilerplate/tests/__init__.py,sha256=h4W89xW2hAKb5WX3DGURe7gmvJbsv2n2aG5hyYvv07A,46
|
|
38
39
|
sum/boilerplate/tests/test_health.py,sha256=cSptlBMzz8ppTEW0xHSiTGKdZVCHkGI-0HUTj6U0IhA,1794
|
|
39
|
-
sum/commands/__init__.py,sha256=
|
|
40
|
+
sum/commands/__init__.py,sha256=xu_gWw3cG6n9NYUof3hMlZQwfScLjaJL8V4L9E1xmlM,268
|
|
40
41
|
sum/commands/backup.py,sha256=2_qhP5yHUSXxif0M1MgVn78dHF7T6GDlBmuw07ZtrX0,9071
|
|
41
42
|
sum/commands/check.py,sha256=OQhHMtnF54ojySYeveToIAB-YvpRnAFuHQuCLZRNT-s,4332
|
|
42
|
-
sum/commands/init.py,sha256=
|
|
43
|
-
sum/commands/promote.py,sha256=
|
|
44
|
-
sum/commands/
|
|
43
|
+
sum/commands/init.py,sha256=2wM3ZbmmXK-y8mpOA_RU-N3I-wjmWJ1VXq9SWHMzADw,10303
|
|
44
|
+
sum/commands/promote.py,sha256=1BonaU_QyMRMaCPlkV9NlMPsEDItZsYV0_F9gzVxOJc,25957
|
|
45
|
+
sum/commands/setup.py,sha256=LG_zt8Nxheaac-Fx3tPg62J40e_A_zFSnhBV7kH_300,4509
|
|
45
46
|
sum/commands/themes.py,sha256=DrPD0R9kig9DeyTXFPvsDL12qGhRFs_06N5g0BAPM3k,1307
|
|
46
47
|
sum/commands/update.py,sha256=AXYa0BugWuYWJpj0vx5N5Ni0D0ZS_NsZeUhk3v3HwLc,9157
|
|
47
|
-
sum/docs/USER_GUIDE.md,sha256=nRO1vH6SxhPnpjaUcNxRJvIDIaWWMdyEmiK91WLWVss,14698
|
|
48
48
|
sum/setup/__init__.py,sha256=dm8Qq819REBxcZxOYQ6sjYOGAYdnMyZHMR8PFgMrNu0,457
|
|
49
49
|
sum/setup/auth.py,sha256=kBBLZKZjt8EUgK4_UV2QFmlQ0Z2RbekCQzkSBYxMJVA,5899
|
|
50
50
|
sum/setup/database.py,sha256=gsMa8aqxyfOBcJ1u13hbUPYSVMpFbP-S2gesVOK1UpE,1630
|
|
51
51
|
sum/setup/deps.py,sha256=snciWZg7TtFlullkxZGs2Acjx1jzrbiG8wFy2rb4Vw8,3018
|
|
52
|
-
sum/setup/git_ops.py,sha256=
|
|
52
|
+
sum/setup/git_ops.py,sha256=RTbgFoJd0Y_N2c_DIsFLL0bvySD771zpLS-Ir5ZsV5M,14858
|
|
53
53
|
sum/setup/infrastructure.py,sha256=lI2kI_odt4JyF6wa0uAmysozGSyDmD6X70rROcNOdJ8,18065
|
|
54
|
-
sum/setup/orchestrator.py,sha256=
|
|
54
|
+
sum/setup/orchestrator.py,sha256=NkJCvsGbdPEridQ2XO7AbAFT1tDJNLca_6HZrp0UM5I,12339
|
|
55
55
|
sum/setup/remote_themes.py,sha256=xc5sY-QwaIiLsP_LqSRE57Kk1hWre1SKxflzhu7cx0c,11685
|
|
56
|
-
sum/setup/scaffold.py,sha256=
|
|
56
|
+
sum/setup/scaffold.py,sha256=MIuS2hBjBbtTobLCG1YXpokn4OVTYCkeEhF2AD_VLbw,17183
|
|
57
57
|
sum/setup/seed.py,sha256=db_5lEVfYbH51h2GuJjWS9lTjUzg88jQF-AodUS-uFQ,3086
|
|
58
|
-
sum/setup/site_orchestrator.py,sha256=
|
|
58
|
+
sum/setup/site_orchestrator.py,sha256=YeCXkezDb0HiNiaLVkHwvLixxU6CZmtWLM4bCxCoDUo,17486
|
|
59
59
|
sum/setup/venv.py,sha256=YttFbI-EZY0w5TFCeAwwO-OFQ1ZviWwa5PnWXXP3Q9s,3150
|
|
60
60
|
sum/utils/__init__.py,sha256=acO546Z2ktZ5TuGcPzR7xMwCFUJ7VIOjzuwh5qKqcIA,630
|
|
61
61
|
sum/utils/django.py,sha256=rqQj6Bwcw_8ihm_tPx6U1AcEtp3kXOVtzfLxC2IN_Yg,3280
|
|
62
|
-
sum/utils/environment.py,sha256=
|
|
62
|
+
sum/utils/environment.py,sha256=Vm9nIeBkKztNQuKwNpa9h0nEyhyD7T5UeVlcHdACixs,2509
|
|
63
63
|
sum/utils/output.py,sha256=jeFHKX1chjJowhNH3NadlNZp8rUvRuuizdwrdDoW49c,2379
|
|
64
64
|
sum/utils/project.py,sha256=27CveU5U0QhgQK_tuI6cGCN9KUskpdwv1_q8t5R_QFY,3375
|
|
65
65
|
sum/utils/prompts.py,sha256=iHDMWpMOj1_xbDE0XB3qF2yw4-EDPG8YbG-uGMXRmnI,1153
|
|
66
|
-
sum/utils/validation.py,sha256=
|
|
67
|
-
sum_cli-3.
|
|
68
|
-
sum_cli-3.
|
|
69
|
-
sum_cli-3.
|
|
70
|
-
sum_cli-3.
|
|
71
|
-
sum_cli-3.
|
|
72
|
-
sum_cli-3.
|
|
66
|
+
sum/utils/validation.py,sha256=6kTMpPt1K0ZxAj24qkvV-fxdMnmCQqLNI42uHkxd6lM,11534
|
|
67
|
+
sum_cli-3.1.0.dist-info/licenses/LICENSE,sha256=RSHp5Au98mOtEsT8N4WMxqxsa-AtcVQCfr3SqoffQoU,1519
|
|
68
|
+
sum_cli-3.1.0.dist-info/METADATA,sha256=36oFrddsZ1sn2-rteo-zjlH3del_1HK0SNfSbUhVQ_Q,4966
|
|
69
|
+
sum_cli-3.1.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
70
|
+
sum_cli-3.1.0.dist-info/entry_points.txt,sha256=cp92-F73mckmgEqj6RKxJ2SII7JfK26A9Kx_LLZElGc,45
|
|
71
|
+
sum_cli-3.1.0.dist-info/top_level.txt,sha256=xfyDwB6SQERSuYZSfSORQMz5pIuI4MJo-_OMLhQp6ck,4
|
|
72
|
+
sum_cli-3.1.0.dist-info/RECORD,,
|