fujin-cli 0.8.0__py3-none-any.whl → 0.9.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.
Potentially problematic release.
This version of fujin-cli might be problematic. Click here for more details.
- fujin/commands/_base.py +2 -12
- fujin/commands/config.py +10 -2
- fujin/commands/deploy.py +7 -7
- fujin/commands/init.py +2 -2
- fujin/commands/printenv.py +2 -2
- fujin/commands/redeploy.py +2 -2
- fujin/config.py +28 -17
- fujin/proxies/caddy.py +2 -1
- fujin/secrets/__init__.py +13 -10
- fujin/{process_managers/systemd.py → systemd.py} +59 -24
- {fujin_cli-0.8.0.dist-info → fujin_cli-0.9.1.dist-info}/METADATA +1 -1
- {fujin_cli-0.8.0.dist-info → fujin_cli-0.9.1.dist-info}/RECORD +15 -16
- fujin/process_managers/__init__.py +0 -40
- {fujin_cli-0.8.0.dist-info → fujin_cli-0.9.1.dist-info}/WHEEL +0 -0
- {fujin_cli-0.8.0.dist-info → fujin_cli-0.9.1.dist-info}/entry_points.txt +0 -0
- {fujin_cli-0.8.0.dist-info → fujin_cli-0.9.1.dist-info}/licenses/LICENSE.txt +0 -0
fujin/commands/_base.py
CHANGED
|
@@ -10,8 +10,8 @@ from fujin.connection import Connection
|
|
|
10
10
|
from fujin.connection import host_connection
|
|
11
11
|
from fujin.errors import ImproperlyConfiguredError
|
|
12
12
|
from fujin.hooks import HookManager
|
|
13
|
-
from fujin.process_managers import ProcessManager
|
|
14
13
|
from fujin.proxies import WebProxy
|
|
14
|
+
from fujin.systemd import ProcessManager
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
@dataclass
|
|
@@ -66,15 +66,5 @@ class BaseCommand:
|
|
|
66
66
|
def create_web_proxy(self, conn: Connection) -> WebProxy:
|
|
67
67
|
return self.web_proxy_class.create(conn=conn, config=self.config)
|
|
68
68
|
|
|
69
|
-
@cached_property
|
|
70
|
-
def process_manager_class(self) -> type[ProcessManager]:
|
|
71
|
-
module = importlib.import_module(self.config.process_manager)
|
|
72
|
-
try:
|
|
73
|
-
return getattr(module, "ProcessManager")
|
|
74
|
-
except KeyError as e:
|
|
75
|
-
raise ImproperlyConfiguredError(
|
|
76
|
-
f"Missing ProcessManager class in {self.config.process_manager}"
|
|
77
|
-
) from e
|
|
78
|
-
|
|
79
69
|
def create_process_manager(self, conn: Connection) -> ProcessManager:
|
|
80
|
-
return
|
|
70
|
+
return ProcessManager.create(conn=conn, config=self.config)
|
fujin/commands/config.py
CHANGED
|
@@ -37,9 +37,17 @@ class ConfigCMD(BaseCommand):
|
|
|
37
37
|
)
|
|
38
38
|
)
|
|
39
39
|
|
|
40
|
+
host_config = {
|
|
41
|
+
f: getattr(self.config.host, f) for f in self.config.host.__struct_fields__
|
|
42
|
+
}
|
|
43
|
+
host_config.pop("_key_filename")
|
|
44
|
+
host_config.pop("_env_file")
|
|
45
|
+
host_config.pop("env_content")
|
|
46
|
+
if self.config.host.key_filename:
|
|
47
|
+
host_config["key_filename"] = self.config.host.key_filename
|
|
48
|
+
host_config["env_content"] = self.config.host.env_content
|
|
40
49
|
host_config_text = "\n".join(
|
|
41
|
-
f"[dim]{key}:[/dim] {value}"
|
|
42
|
-
for key, value in self.config.host.to_dict().items()
|
|
50
|
+
f"[dim]{key}:[/dim] {value}" for key, value in host_config.items()
|
|
43
51
|
)
|
|
44
52
|
console.print(
|
|
45
53
|
Panel(
|
fujin/commands/deploy.py
CHANGED
|
@@ -52,15 +52,15 @@ class Deploy(BaseCommand):
|
|
|
52
52
|
return f"{self.app_dir}/v{self.config.version}"
|
|
53
53
|
|
|
54
54
|
def parse_envfile(self) -> str:
|
|
55
|
-
if not self.config.host.envfile.exists():
|
|
56
|
-
raise cappa.Exit(f"{self.config.host.envfile} not found", code=1)
|
|
57
55
|
if self.config.secret_config:
|
|
58
56
|
self.stdout.output("[blue]Reading secrets....[/blue]")
|
|
59
|
-
return resolve_secrets(
|
|
60
|
-
|
|
57
|
+
return resolve_secrets(
|
|
58
|
+
self.config.host.env_content, self.config.secret_config
|
|
59
|
+
)
|
|
60
|
+
return self.config.host.env_content
|
|
61
61
|
|
|
62
62
|
def transfer_files(
|
|
63
|
-
|
|
63
|
+
self, conn: Connection, env: str, skip_requirements: bool = False
|
|
64
64
|
):
|
|
65
65
|
conn.run(f"echo '{env}' > {self.app_dir}/.env")
|
|
66
66
|
distfile_path = self.config.get_distfile_path()
|
|
@@ -78,7 +78,7 @@ class Deploy(BaseCommand):
|
|
|
78
78
|
)
|
|
79
79
|
|
|
80
80
|
def install_project(
|
|
81
|
-
|
|
81
|
+
self, conn: Connection, version: str | None = None, *, skip_setup: bool = False
|
|
82
82
|
):
|
|
83
83
|
version = version or self.config.version
|
|
84
84
|
if self.config.installation_mode == InstallationMode.PY_PACKAGE:
|
|
@@ -87,7 +87,7 @@ class Deploy(BaseCommand):
|
|
|
87
87
|
self._install_binary(conn, version)
|
|
88
88
|
|
|
89
89
|
def _install_python_package(
|
|
90
|
-
|
|
90
|
+
self, conn: Connection, version: str, skip_setup: bool = False
|
|
91
91
|
):
|
|
92
92
|
appenv = f"""
|
|
93
93
|
set -a # Automatically export all variables
|
fujin/commands/init.py
CHANGED
|
@@ -31,7 +31,7 @@ class Init(BaseCommand):
|
|
|
31
31
|
}
|
|
32
32
|
app_name = Path().resolve().stem.replace("-", "_").replace(" ", "_").lower()
|
|
33
33
|
config = profile_to_func[self.profile](app_name)
|
|
34
|
-
fujin_toml.write_text(tomli_w.dumps(config))
|
|
34
|
+
fujin_toml.write_text(tomli_w.dumps(config, multiline_strings=True))
|
|
35
35
|
self.stdout.output(
|
|
36
36
|
"[green]Sample configuration file generated successfully![/green]"
|
|
37
37
|
)
|
|
@@ -57,7 +57,7 @@ def simple_config(app_name) -> dict:
|
|
|
57
57
|
"host": {
|
|
58
58
|
"user": "root",
|
|
59
59
|
"domain_name": f"{app_name}.com",
|
|
60
|
-
"
|
|
60
|
+
"env_content": f"DEBUG=False\nALLOWED_HOSTS={app_name}.com\n",
|
|
61
61
|
},
|
|
62
62
|
}
|
|
63
63
|
if not Path(".python-version").exists():
|
fujin/commands/printenv.py
CHANGED
|
@@ -11,8 +11,8 @@ class Printenv(BaseCommand):
|
|
|
11
11
|
def __call__(self):
|
|
12
12
|
if self.config.secret_config:
|
|
13
13
|
result = resolve_secrets(
|
|
14
|
-
self.config.host.
|
|
14
|
+
self.config.host.env_content, self.config.secret_config
|
|
15
15
|
)
|
|
16
16
|
else:
|
|
17
|
-
result = self.config.host.
|
|
17
|
+
result = self.config.host.env_content
|
|
18
18
|
self.stdout.output(result)
|
fujin/commands/redeploy.py
CHANGED
|
@@ -34,8 +34,8 @@ class Redeploy(BaseCommand):
|
|
|
34
34
|
|
|
35
35
|
def _copy_requirements_if_needed(self, conn: Connection) -> bool:
|
|
36
36
|
if (
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
not self.config.requirements
|
|
38
|
+
or self.config.installation_mode == InstallationMode.BINARY
|
|
39
39
|
):
|
|
40
40
|
return False
|
|
41
41
|
local_requirements = hashlib.md5(
|
fujin/config.py
CHANGED
|
@@ -116,6 +116,11 @@ Example:
|
|
|
116
116
|
.. note::
|
|
117
117
|
|
|
118
118
|
Commands are relative to your ``app_dir``. When generating systemd service files, the full path is automatically constructed.
|
|
119
|
+
Here are the templates for the service files:
|
|
120
|
+
|
|
121
|
+
- `web.service <https://github.com/falcopackages/fujin/blob/main/src/fujin/templates/web.service>`_
|
|
122
|
+
- `web.socket <https://github.com/falcopackages/fujin/blob/main/src/fujin/templates/web.socket>`_
|
|
123
|
+
- `simple.service <https://github.com/falcopackages/fujin/blob/main/src/fujin/templates/simple.service>`_ (for all additional processes)
|
|
119
124
|
|
|
120
125
|
Host Configuration
|
|
121
126
|
-------------------
|
|
@@ -136,10 +141,18 @@ The login user for running remote tasks. Should have passwordless sudo access fo
|
|
|
136
141
|
|
|
137
142
|
You can create a user with these requirements using the ``fujin server create-user`` command.
|
|
138
143
|
|
|
139
|
-
|
|
140
|
-
|
|
144
|
+
env_file
|
|
145
|
+
~~~~~~~~
|
|
141
146
|
Path to the production environment file that will be copied to the host.
|
|
142
147
|
|
|
148
|
+
env_content
|
|
149
|
+
~~~~~~~~~~~
|
|
150
|
+
A string containing the production environment variables, ideal for scenarios where most variables are retrieved from secrets and you prefer not to use a separate file.
|
|
151
|
+
|
|
152
|
+
.. important::
|
|
153
|
+
|
|
154
|
+
``env_file`` and ``env_content`` are mutually exclusive—you can define only one.
|
|
155
|
+
|
|
143
156
|
apps_dir
|
|
144
157
|
~~~~~~~~
|
|
145
158
|
|
|
@@ -178,7 +191,7 @@ Example:
|
|
|
178
191
|
|
|
179
192
|
hooks
|
|
180
193
|
-----
|
|
181
|
-
Run custom scripts at specific points with hooks. Check out the `
|
|
194
|
+
Run custom scripts at specific points with hooks. Check out the `hooks </hooks.html>`_ page for more information.
|
|
182
195
|
|
|
183
196
|
"""
|
|
184
197
|
|
|
@@ -227,7 +240,6 @@ class Config(msgspec.Struct, kw_only=True):
|
|
|
227
240
|
aliases: dict[str, str] = msgspec.field(default_factory=dict)
|
|
228
241
|
host: HostConfig
|
|
229
242
|
processes: dict[str, str] = msgspec.field(default_factory=dict)
|
|
230
|
-
process_manager: str = "fujin.process_managers.systemd"
|
|
231
243
|
webserver: Webserver
|
|
232
244
|
requirements: str | None = None
|
|
233
245
|
hooks: HooksDict = msgspec.field(default_factory=dict)
|
|
@@ -271,28 +283,27 @@ class HostConfig(msgspec.Struct, kw_only=True):
|
|
|
271
283
|
ip: str | None = None
|
|
272
284
|
domain_name: str
|
|
273
285
|
user: str
|
|
274
|
-
|
|
286
|
+
_env_file: str = msgspec.field(name="envfile", default="")
|
|
287
|
+
env_content: str = ""
|
|
275
288
|
apps_dir: str = ".local/share/fujin"
|
|
276
289
|
password_env: str | None = None
|
|
277
290
|
ssh_port: int = 22
|
|
278
291
|
_key_filename: str | None = msgspec.field(name="key_filename", default=None)
|
|
279
292
|
|
|
280
293
|
def __post_init__(self):
|
|
294
|
+
if self._env_file and self.env_content:
|
|
295
|
+
raise ImproperlyConfiguredError(
|
|
296
|
+
"Cannot set both 'env_content' and 'env_file' properties."
|
|
297
|
+
)
|
|
298
|
+
if not self.env_content:
|
|
299
|
+
envfile = Path(self._env_file)
|
|
300
|
+
if not envfile.exists():
|
|
301
|
+
raise ImproperlyConfiguredError(f"{self._env_file} not found")
|
|
302
|
+
self.env_content = envfile.read_text()
|
|
303
|
+
self.env_content = self.env_content.strip()
|
|
281
304
|
self.apps_dir = f"/home/{self.user}/{self.apps_dir}"
|
|
282
305
|
self.ip = self.ip or self.domain_name
|
|
283
306
|
|
|
284
|
-
def to_dict(self):
|
|
285
|
-
d = {f: getattr(self, f) for f in self.__struct_fields__}
|
|
286
|
-
d.pop("_key_filename")
|
|
287
|
-
d.pop("_envfile")
|
|
288
|
-
d["key_filename"] = self.key_filename
|
|
289
|
-
d["envfile"] = self.envfile
|
|
290
|
-
return d
|
|
291
|
-
|
|
292
|
-
@property
|
|
293
|
-
def envfile(self) -> Path:
|
|
294
|
-
return Path(self._envfile)
|
|
295
|
-
|
|
296
307
|
@property
|
|
297
308
|
def key_filename(self) -> Path | None:
|
|
298
309
|
if self._key_filename:
|
fujin/proxies/caddy.py
CHANGED
|
@@ -166,7 +166,8 @@ class WebProxy(msgspec.Struct):
|
|
|
166
166
|
new_routes = [r for r in existing_routes if r.get("group") != self.app_name]
|
|
167
167
|
current_config["routes"] = new_routes
|
|
168
168
|
self.conn.run(
|
|
169
|
-
f"curl localhost:2019/config/apps/http/servers/srv0 -H 'Content-Type: application/json' -d '{json.dumps(current_config)}'"
|
|
169
|
+
f"curl localhost:2019/config/apps/http/servers/srv0 -H 'Content-Type: application/json' -d '{json.dumps(current_config)}'",
|
|
170
|
+
hide="out",
|
|
170
171
|
)
|
|
171
172
|
|
|
172
173
|
def start(self) -> None:
|
fujin/secrets/__init__.py
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from
|
|
4
|
-
from
|
|
3
|
+
from contextlib import closing
|
|
4
|
+
from io import StringIO
|
|
5
|
+
from typing import Callable, ContextManager
|
|
5
6
|
|
|
6
7
|
import gevent
|
|
7
8
|
from dotenv import dotenv_values
|
|
8
9
|
|
|
9
|
-
from .bitwarden import bitwarden
|
|
10
|
-
from .onepassword import one_password
|
|
11
10
|
from fujin.config import SecretAdapter
|
|
12
11
|
from fujin.config import SecretConfig
|
|
13
|
-
|
|
12
|
+
from .bitwarden import bitwarden
|
|
13
|
+
from .onepassword import one_password
|
|
14
14
|
|
|
15
15
|
secret_reader = Callable[[str], str]
|
|
16
|
-
secret_adapter_context = Callable[[SecretConfig], secret_reader]
|
|
16
|
+
secret_adapter_context = Callable[[SecretConfig], ContextManager[secret_reader]]
|
|
17
17
|
|
|
18
18
|
adapter_to_context: dict[SecretAdapter, secret_adapter_context] = {
|
|
19
19
|
SecretAdapter.BITWARDEN: bitwarden,
|
|
@@ -21,14 +21,17 @@ adapter_to_context: dict[SecretAdapter, secret_adapter_context] = {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
|
|
24
|
-
def resolve_secrets(
|
|
25
|
-
|
|
24
|
+
def resolve_secrets(env_content: str, secret_config: SecretConfig) -> str:
|
|
25
|
+
with closing(StringIO(env_content)) as buffer:
|
|
26
|
+
env_dict = dotenv_values(stream=buffer)
|
|
26
27
|
secrets = {key: value for key, value in env_dict.items() if value.startswith("$")}
|
|
28
|
+
if not secrets:
|
|
29
|
+
return env_content
|
|
27
30
|
adapter_context = adapter_to_context[secret_config.adapter]
|
|
28
31
|
parsed_secrets = {}
|
|
29
|
-
with adapter_context(secret_config) as
|
|
32
|
+
with adapter_context(secret_config) as reader:
|
|
30
33
|
for key, secret in secrets.items():
|
|
31
|
-
parsed_secrets[key] = gevent.spawn(
|
|
34
|
+
parsed_secrets[key] = gevent.spawn(reader, secret[1:])
|
|
32
35
|
gevent.joinall(parsed_secrets.values())
|
|
33
36
|
env_dict.update({key: thread.value for key, thread in parsed_secrets.items()})
|
|
34
37
|
return "\n".join(f'{key}="{value}"' for key, value in env_dict.items())
|
|
@@ -4,6 +4,8 @@ import importlib.util
|
|
|
4
4
|
from dataclasses import dataclass
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
|
|
7
|
+
import gevent
|
|
8
|
+
|
|
7
9
|
from fujin.config import Config
|
|
8
10
|
from fujin.connection import Connection
|
|
9
11
|
|
|
@@ -54,11 +56,23 @@ class ProcessManager:
|
|
|
54
56
|
hide="out",
|
|
55
57
|
)
|
|
56
58
|
|
|
59
|
+
threads = []
|
|
57
60
|
for name in self.processes:
|
|
58
61
|
if name == "web" and self.is_using_unix_socket:
|
|
59
|
-
|
|
62
|
+
threads.append(
|
|
63
|
+
gevent.spawn(
|
|
64
|
+
self.run_pty,
|
|
65
|
+
f"sudo systemctl enable --now {self.app_name}.socket",
|
|
66
|
+
)
|
|
67
|
+
)
|
|
60
68
|
else:
|
|
61
|
-
|
|
69
|
+
threads.append(
|
|
70
|
+
gevent.spawn(
|
|
71
|
+
self.run_pty,
|
|
72
|
+
f"sudo systemctl enable {self.get_service_name(name)}",
|
|
73
|
+
)
|
|
74
|
+
)
|
|
75
|
+
gevent.joinall(threads)
|
|
62
76
|
|
|
63
77
|
def get_configuration_files(
|
|
64
78
|
self, ignore_local: bool = False
|
|
@@ -105,47 +119,68 @@ class ProcessManager:
|
|
|
105
119
|
|
|
106
120
|
def uninstall_services(self) -> None:
|
|
107
121
|
self.stop_services()
|
|
108
|
-
|
|
109
|
-
self.run_pty
|
|
110
|
-
|
|
122
|
+
threads = [
|
|
123
|
+
gevent.spawn(self.run_pty, f"sudo systemctl disable {name}", warn=True)
|
|
124
|
+
for name in self.service_names
|
|
125
|
+
]
|
|
126
|
+
gevent.joinall(threads)
|
|
127
|
+
files_to_delete = [f"/etc/systemd/system/{name}" for name in self.service_names]
|
|
128
|
+
self.run_pty(f"sudo rm {' '.join(files_to_delete)}", warn=True)
|
|
111
129
|
|
|
112
130
|
def start_services(self, *names) -> None:
|
|
113
131
|
names = names or self.service_names
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
132
|
+
threads = [
|
|
133
|
+
gevent.spawn(self.run_pty, f"sudo systemctl start {name}")
|
|
134
|
+
for name in names
|
|
135
|
+
if name in self.service_names
|
|
136
|
+
]
|
|
137
|
+
gevent.joinall(threads)
|
|
117
138
|
|
|
118
139
|
def restart_services(self, *names) -> None:
|
|
119
140
|
names = names or self.service_names
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
141
|
+
threads = [
|
|
142
|
+
gevent.spawn(self.run_pty, f"sudo systemctl restart {name}")
|
|
143
|
+
for name in names
|
|
144
|
+
if name in self.service_names
|
|
145
|
+
]
|
|
146
|
+
gevent.joinall(threads)
|
|
123
147
|
|
|
124
148
|
def stop_services(self, *names) -> None:
|
|
125
149
|
names = names or self.service_names
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
150
|
+
threads = [
|
|
151
|
+
gevent.spawn(self.run_pty, f"sudo systemctl stop {name}")
|
|
152
|
+
for name in names
|
|
153
|
+
if name in self.service_names
|
|
154
|
+
]
|
|
155
|
+
gevent.joinall(threads)
|
|
129
156
|
|
|
130
157
|
def is_enabled(self, *names) -> dict[str, bool]:
|
|
131
158
|
names = names or self.service_names
|
|
132
|
-
|
|
133
|
-
name:
|
|
134
|
-
f"sudo systemctl is-enabled {name}", warn=True, hide=True
|
|
135
|
-
)
|
|
136
|
-
== "enabled"
|
|
159
|
+
threads = {
|
|
160
|
+
name: gevent.spawn(
|
|
161
|
+
self.run_pty, f"sudo systemctl is-enabled {name}", warn=True, hide=True
|
|
162
|
+
)
|
|
137
163
|
for name in names
|
|
138
164
|
}
|
|
165
|
+
gevent.joinall(threads.values())
|
|
166
|
+
return {
|
|
167
|
+
name: thread.value.stdout.strip() == "enabled"
|
|
168
|
+
for name, thread in threads.items()
|
|
169
|
+
}
|
|
139
170
|
|
|
140
171
|
def is_active(self, *names) -> dict[str, bool]:
|
|
141
172
|
names = names or self.service_names
|
|
142
|
-
|
|
143
|
-
name:
|
|
144
|
-
f"sudo systemctl is-active {name}", warn=True, hide=True
|
|
145
|
-
)
|
|
146
|
-
== "active"
|
|
173
|
+
threads = {
|
|
174
|
+
name: gevent.spawn(
|
|
175
|
+
self.run_pty, f"sudo systemctl is-active {name}", warn=True, hide=True
|
|
176
|
+
)
|
|
147
177
|
for name in names
|
|
148
178
|
}
|
|
179
|
+
gevent.joinall(threads.values())
|
|
180
|
+
return {
|
|
181
|
+
name: thread.value.stdout.strip() == "active"
|
|
182
|
+
for name, thread in threads.items()
|
|
183
|
+
}
|
|
149
184
|
|
|
150
185
|
def service_logs(self, name: str, follow: bool = False):
|
|
151
186
|
# TODO: add more options here
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: fujin-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.9.1
|
|
4
4
|
Summary: Get your project up and running in a few minutes on your own vps.
|
|
5
5
|
Project-URL: Documentation, https://github.com/falcopackages/fujin#readme
|
|
6
6
|
Project-URL: Issues, https://github.com/falcopackages/fujin/issues
|
|
@@ -1,38 +1,37 @@
|
|
|
1
1
|
fujin/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
2
|
fujin/__main__.py,sha256=VJMBzuQuxkQaAKNEySktGnms944bkRsrIAjj-XeaWR8,1813
|
|
3
|
-
fujin/config.py,sha256=
|
|
3
|
+
fujin/config.py,sha256=jE5iAu1ouHgv2eTut6CRBiKIzK-vxJqVtZuOr6NICEM,11365
|
|
4
4
|
fujin/connection.py,sha256=LL7LhX9p0X9FmiGdlSroD3Ht216QY0Kd51xkSrXmM3s,2479
|
|
5
5
|
fujin/errors.py,sha256=74Rh-Sgql1YspPdR_akQ2G3xZ48zecyafYCptpaFo1A,73
|
|
6
6
|
fujin/hooks.py,sha256=EDVYNozlDJ5kc1xHtZrXgtuKplUMEMPTp65TLMP02Ek,1449
|
|
7
|
+
fujin/systemd.py,sha256=aWfN3CDl_NACjsfzyiVmO2h0-VTgc7y3g8RMG2jVsb4,6640
|
|
7
8
|
fujin/commands/__init__.py,sha256=g0b13vzidPUbxne_Zo_Wv5jpEmwCiSK4AokCinH9uo4,39
|
|
8
|
-
fujin/commands/_base.py,sha256=
|
|
9
|
+
fujin/commands/_base.py,sha256=2k9HGcxskFZQ7qYw8hcJh_73EZ_qVngLot7lOo2hQuw,2193
|
|
9
10
|
fujin/commands/app.py,sha256=mazb4dCTdR5juh79bL3a9b68Nd6O8u_nR9IgYqQlqWE,5279
|
|
10
|
-
fujin/commands/config.py,sha256=
|
|
11
|
-
fujin/commands/deploy.py,sha256=
|
|
11
|
+
fujin/commands/config.py,sha256=WymGla-H2yduhLcYE1Nb6IJaFIj0S0KIhV9fBDCKsAw,2779
|
|
12
|
+
fujin/commands/deploy.py,sha256=AylcVNX47ekf-AFfQrYcSh86I3qltADEgjPtV9lQAfE,5831
|
|
12
13
|
fujin/commands/docs.py,sha256=b5FZ8AgoAfn4q4BueEQvM2w5HCuh8-rwBqv_CRFVU8E,349
|
|
13
14
|
fujin/commands/down.py,sha256=aw_mxl_TMC66plKTXwlYP1W2XQBmHeROltQqOpQssyE,1577
|
|
14
|
-
fujin/commands/init.py,sha256=
|
|
15
|
-
fujin/commands/printenv.py,sha256=
|
|
15
|
+
fujin/commands/init.py,sha256=AHAZeYkSk-zShF288Zs4rCfVwalCUqeMXWR6GTks2v0,4046
|
|
16
|
+
fujin/commands/printenv.py,sha256=bS2mIgk7zd_w3yDhZLTa1PkHIzuSbBWjnYyM8CUjX0k,523
|
|
16
17
|
fujin/commands/proxy.py,sha256=ajXwboS0gDDiMWW7b9rtWU6WPF1h7JYYeycDyU-hQfg,3053
|
|
17
18
|
fujin/commands/prune.py,sha256=C2aAN6AUS84jgRg1eiCroyiuZyaZDmf5yvGAQY9xkcg,1517
|
|
18
|
-
fujin/commands/redeploy.py,sha256=
|
|
19
|
+
fujin/commands/redeploy.py,sha256=491Mzz0qiaHqcI7BFUtZq-M34WQkiBd2ZgQbLRLp8T8,2355
|
|
19
20
|
fujin/commands/rollback.py,sha256=JsocJzQcdQelSnYD94klhjBh8UKkkdiRD9shfUfo4FI,2032
|
|
20
21
|
fujin/commands/server.py,sha256=-3-PyBNR0fGm-RYE3fz50kP-LSDYGU9BzUxrbGZEghc,3312
|
|
21
22
|
fujin/commands/up.py,sha256=OEK_n-6-mnnIUffFpR7QtVunr1V1F04pxlAAS1U62BY,419
|
|
22
|
-
fujin/process_managers/__init__.py,sha256=MhhfTBhm64zWRAKgjvsZRIToOUJus60vGScbAjqpQ6Y,994
|
|
23
|
-
fujin/process_managers/systemd.py,sha256=qG_4Ew8SEWtaTFOAW_XZXsMO2WjFWZ4dp5nBwAPBObk,5603
|
|
24
23
|
fujin/proxies/__init__.py,sha256=UuWYU175tkdaz1WWRCDDpQgGfFVYYNR9PBxA3lTCNr0,695
|
|
25
|
-
fujin/proxies/caddy.py,sha256=
|
|
24
|
+
fujin/proxies/caddy.py,sha256=H52D7cGEEGcxDaXxvClnny9lAat_h1G9dYlIlf6gwKo,7933
|
|
26
25
|
fujin/proxies/dummy.py,sha256=qBKSn8XNEA9SVwB7GzRNX2l9Iw6tUjo2CFqZjWi0FjY,465
|
|
27
26
|
fujin/proxies/nginx.py,sha256=BNJNLxLLRVAmBIGVCk8pb16iiSJsOI9jXOZhdSQGtX8,4151
|
|
28
|
-
fujin/secrets/__init__.py,sha256=
|
|
27
|
+
fujin/secrets/__init__.py,sha256=hiNZIBtOwM0WQbvAF3GhuMMCsQCjXjiSr5gTLAGN-VI,1375
|
|
29
28
|
fujin/secrets/bitwarden.py,sha256=01GZL5hYwZzL6yXy5ab3L3kgBFBeOT8i3Yg9GC8YwFU,2008
|
|
30
29
|
fujin/secrets/onepassword.py,sha256=6Xj3XWttKfcjMbcoMZvXVpJW1KHxlD785DysmX_mqvk,654
|
|
31
30
|
fujin/templates/simple.service,sha256=-lyKjmSyfHGucP4O_vRQE1NNaHq0Qjsc0twdwoRLgI0,321
|
|
32
31
|
fujin/templates/web.service,sha256=NZ7ZeaFvV_MZTBn8QqRQeu8PIrWHf3aWYWNzjOQeqCw,685
|
|
33
32
|
fujin/templates/web.socket,sha256=2lJsiOHlMJL0YlN7YBLLnr5zqsytPEt81yP34nk0dmc,173
|
|
34
|
-
fujin_cli-0.
|
|
35
|
-
fujin_cli-0.
|
|
36
|
-
fujin_cli-0.
|
|
37
|
-
fujin_cli-0.
|
|
38
|
-
fujin_cli-0.
|
|
33
|
+
fujin_cli-0.9.1.dist-info/METADATA,sha256=5mtskwHoka7b0M4fs897ic7prKs3NgTBrdeMSWlfKBk,4576
|
|
34
|
+
fujin_cli-0.9.1.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
|
|
35
|
+
fujin_cli-0.9.1.dist-info/entry_points.txt,sha256=Y_TBtKt3j11qhwquMexZR5yqnDEqOBDACtresqQFE-s,46
|
|
36
|
+
fujin_cli-0.9.1.dist-info/licenses/LICENSE.txt,sha256=0QF8XfuH0zkIHhSet6teXfiCze6JSdr8inRkmLLTDyo,1099
|
|
37
|
+
fujin_cli-0.9.1.dist-info/RECORD,,
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from typing import Protocol
|
|
4
|
-
from typing import TYPE_CHECKING
|
|
5
|
-
|
|
6
|
-
from fujin.connection import Connection
|
|
7
|
-
|
|
8
|
-
if TYPE_CHECKING:
|
|
9
|
-
from fujin.config import Config
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class ProcessManager(Protocol):
|
|
13
|
-
service_names: list[str]
|
|
14
|
-
|
|
15
|
-
@classmethod
|
|
16
|
-
def create(cls, config: Config, conn: Connection) -> ProcessManager: ...
|
|
17
|
-
|
|
18
|
-
def get_service_name(self, process_name: str): ...
|
|
19
|
-
|
|
20
|
-
def install_services(self) -> None: ...
|
|
21
|
-
|
|
22
|
-
def uninstall_services(self) -> None: ...
|
|
23
|
-
|
|
24
|
-
def start_services(self, *names) -> None: ...
|
|
25
|
-
|
|
26
|
-
def restart_services(self, *names) -> None: ...
|
|
27
|
-
|
|
28
|
-
def stop_services(self, *names) -> None: ...
|
|
29
|
-
|
|
30
|
-
def is_enabled(self, *names) -> dict[str, bool]: ...
|
|
31
|
-
|
|
32
|
-
def is_active(self, *names) -> dict[str, bool]: ...
|
|
33
|
-
|
|
34
|
-
def service_logs(self, name: str, follow: bool = False): ...
|
|
35
|
-
|
|
36
|
-
def reload_configuration(self) -> None: ...
|
|
37
|
-
|
|
38
|
-
def get_configuration_files(
|
|
39
|
-
self, ignore_local: bool = False
|
|
40
|
-
) -> list[tuple[str, str]]: ...
|
|
File without changes
|
|
File without changes
|
|
File without changes
|