fujin-cli 0.9.1__py3-none-any.whl → 0.11.4__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/__main__.py CHANGED
@@ -50,9 +50,9 @@ class Fujin:
50
50
  def main():
51
51
  alias_cmd = _parse_aliases()
52
52
  if alias_cmd:
53
- cappa.invoke(Fujin, argv=alias_cmd)
53
+ cappa.invoke(Fujin, argv=alias_cmd, version="0.11.4")
54
54
  else:
55
- cappa.invoke(Fujin)
55
+ cappa.invoke(Fujin, version="0.11.4")
56
56
 
57
57
 
58
58
  def _parse_aliases() -> list[str] | None:
fujin/commands/init.py CHANGED
@@ -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
- "env_content": f"DEBUG=False\nALLOWED_HOSTS={app_name}.com\n",
60
+ "envfile": ".env.prod",
61
61
  },
62
62
  }
63
63
  if not Path(".python-version").exists():
fujin/commands/prune.py CHANGED
@@ -8,7 +8,7 @@ from fujin.commands import BaseCommand
8
8
 
9
9
 
10
10
  @cappa.command(
11
- help="Prune old version artifacts, keeping only the specified number of recent versions"
11
+ help="Prune old artifacts, keeping only the specified number of recent versions"
12
12
  )
13
13
  @dataclass
14
14
  class Prune(BaseCommand):
fujin/config.py CHANGED
@@ -1,5 +1,5 @@
1
1
  """
2
- Fujin uses a ``fujin.toml`` file at the root of your project for configuration. Below are all available configuration options.
2
+ Fujin uses a **fujin.toml** file at the root of your project for configuration. Below are all available configuration options.
3
3
 
4
4
  app
5
5
  ---
@@ -7,16 +7,16 @@ The name of your project or application. Must be a valid Python package name.
7
7
 
8
8
  version
9
9
  --------
10
- The version of your project to build and deploy. If not specified, automatically parsed from ``pyproject.toml`` under ``project.version``.
10
+ The version of your project to build and deploy. If not specified, automatically parsed from **pyproject.toml** under *project.version*.
11
11
 
12
12
  python_version
13
13
  --------------
14
- The Python version for your virtualenv. If not specified, automatically parsed from ``.python-version`` file. This is only
15
- required if the installation mode is set to ``python-package``
14
+ The Python version for your virtualenv. If not specified, automatically parsed from **.python-version** file. This is only
15
+ required if the installation mode is set to **python-package**
16
16
 
17
17
  requirements
18
18
  ------------
19
- Optional path to your requirements file. This will only be used when the installation mode is set to ``python-package``
19
+ Optional path to your requirements file. This will only be used when the installation mode is set to *python-package*
20
20
 
21
21
  versions_to_keep
22
22
  ----------------
@@ -30,31 +30,28 @@ The command to use to build your project's distribution file.
30
30
  distfile
31
31
  --------
32
32
  Path to your project's distribution file. This should be the main artifact containing everything needed to run your project on the server.
33
- Supports version placeholder, e.g., ``dist/app_name-{version}-py3-none-any.whl``
33
+ Supports version placeholder, e.g., **dist/app_name-{version}-py3-none-any.whl**
34
34
 
35
35
  installation_mode
36
36
  -----------------
37
37
 
38
- Indicates whether the ``distfile`` is a Python package or a self-contained executable. The possible values are ``python-package`` and ``binary``.
39
- The ``binary`` option disables specific Python-related features, such as virtual environment creation and requirements installation. ``fujin`` will assume the provided
40
- ``distfile`` already contains all the necessary dependencies to run your program.
38
+ Indicates whether the *distfile* is a Python package or a self-contained executable. The possible values are *python-package* and *binary*.
39
+ The *binary* option disables specific Python-related features, such as virtual environment creation and requirements installation. ``fujin`` will assume the provided
40
+ *distfile* already contains all the necessary dependencies to run your program.
41
41
 
42
42
  release_command
43
43
  ---------------
44
- Optional command to run at the end of deployment (e.g., database migrations).
44
+ Optional command to run at the end of deployment (e.g., database migrations) before your application is started.
45
45
 
46
46
  secrets
47
47
  -------
48
48
 
49
- Optional secrets configuration. If set, Fujin will load secrets from the specified secret management service.
49
+ Optional secrets configuration. If set, ``fujin`` will load secrets from the specified secret management service.
50
50
  Check out the `secrets </secrets.html>`_ page for more information.
51
51
 
52
52
  adapter
53
53
  ~~~~~~~
54
- The secret management service to use. Available options:
55
-
56
- - ``bitwarden``
57
- - ``1password``
54
+ The secret management service to use. The currently available options are *bitwarden*, *1password*, *doppler*
58
55
 
59
56
  password_env
60
57
  ~~~~~~~~~~~~
@@ -63,35 +60,39 @@ Environment variable containing the password for the service account. This is on
63
60
  Webserver
64
61
  ---------
65
62
 
63
+ Web server configurations.
64
+
66
65
  type
67
66
  ~~~~
68
67
  The reverse proxy implementation to use. Available options:
69
68
 
70
- - ``fujin.proxies.caddy`` (default)
71
- - ``fujin.proxies.nginx``
72
- - ``fujin.proxies.dummy`` (disables proxy functionality)
69
+ - *fujin.proxies.caddy* (default)
70
+ - *fujin.proxies.nginx*
71
+ - *fujin.proxies.dummy* (disables proxy)
73
72
 
74
73
  upstream
75
74
  ~~~~~~~~
76
75
  The address where your web application listens for requests. Supports any value compatible with your chosen web proxy:
77
76
 
78
- - HTTP address (e.g., ``localhost:8000``)
79
- - Unix socket (e.g., ``unix//run/project.sock``)
77
+ - HTTP address (e.g., *localhost:8000* )
78
+ - Unix socket caddy (e.g., *unix//run/project.sock* )
79
+ - Unix socket nginx (e.g., *http://unix:/run/project.sock* )
80
80
 
81
81
  certbot_email
82
82
  ~~~~~~~~~~~~~
83
- Required when Nginx is used as a proxy, to obtain SSL certificates.
83
+ Required when Nginx is used as a proxy to obtain SSL certificates.
84
84
 
85
85
  statics
86
86
  ~~~~~~~
87
87
 
88
88
  Defines the mapping of URL paths to local directories for serving static files. The syntax and support for static
89
89
  file serving depend on the selected reverse proxy. The directories you map should be accessible by the web server, meaning
90
- with read permissions for the ``www-data`` group; a reliable choice is ``/var/www``.
90
+ with read permissions for the *www-data* group; a reliable choice is **/var/www**.
91
91
 
92
92
  Example:
93
93
 
94
94
  .. code-block:: toml
95
+ :caption: fujin.toml
95
96
 
96
97
  [webserver]
97
98
  upstream = "unix//run/project.sock"
@@ -102,12 +103,12 @@ processes
102
103
  ---------
103
104
 
104
105
  A mapping of process names to commands that will be managed by the process manager. Define as many processes as needed, but
105
- when using any proxy other than ``fujin.proxies.dummy``, a ``web`` process must be declared. Refer to the ``apps_dir``
106
- setting on the host to understand how ``app_dir`` is determined.
106
+ when using any proxy other than *fujin.proxies.dummy*, a *web* process must be declared.
107
107
 
108
108
  Example:
109
109
 
110
110
  .. code-block:: toml
111
+ :caption: fujin.toml
111
112
 
112
113
  [processes]
113
114
  web = ".venv/bin/gunicorn myproject.wsgi:application"
@@ -115,7 +116,8 @@ Example:
115
116
 
116
117
  .. note::
117
118
 
118
- Commands are relative to your ``app_dir``. When generating systemd service files, the full path is automatically constructed.
119
+ Commands are relative to your *app_dir*. When generating systemd service files, the full path is automatically constructed.
120
+ Refer to the *apps_dir* setting on the host to understand how *app_dir* is determined.
119
121
  Here are the templates for the service files:
120
122
 
121
123
  - `web.service <https://github.com/falcopackages/fujin/blob/main/src/fujin/templates/web.service>`_
@@ -127,7 +129,7 @@ Host Configuration
127
129
 
128
130
  ip
129
131
  ~~
130
- The IP address or anything that resolves to the remote host IP's. This is use to communicate via ssh with the server, if omitted it's value will default to the one of the ``domain_name``.
132
+ The IP address or anything that resolves to the remote host IP's. This is use to communicate via ssh with the server, if omitted it's value will default to the one of the *domain_name*.
131
133
 
132
134
  domain_name
133
135
  ~~~~~~~~~~~
@@ -141,23 +143,23 @@ The login user for running remote tasks. Should have passwordless sudo access fo
141
143
 
142
144
  You can create a user with these requirements using the ``fujin server create-user`` command.
143
145
 
144
- env_file
145
- ~~~~~~~~
146
+ envfile
147
+ ~~~~~~~
146
148
  Path to the production environment file that will be copied to the host.
147
149
 
148
- env_content
149
- ~~~~~~~~~~~
150
+ env
151
+ ~~~
150
152
  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
153
 
152
154
  .. important::
153
155
 
154
- ``env_file`` and ``env_content`` are mutually exclusive—you can define only one.
156
+ *envfile* and *env* are mutually exclusive—you can define only one.
155
157
 
156
158
  apps_dir
157
159
  ~~~~~~~~
158
160
 
159
161
  Base directory for project storage on the host. Path is relative to user's home directory.
160
- Default: ``.local/share/fujin``. This value determines your project's ``app_dir``, which is ``{apps_dir}/{app}``.
162
+ Default: **.local/share/fujin**. This value determines your project's **app_dir**, which is **{apps_dir}/{app}**.
161
163
 
162
164
  password_env
163
165
  ~~~~~~~~~~~~
@@ -167,8 +169,7 @@ Environment variable containing the user's password. Only needed if the user can
167
169
  ssh_port
168
170
  ~~~~~~~~
169
171
 
170
- SSH port for connecting to the host.
171
- Default: ``22``
172
+ SSH port for connecting to the host. Default to **22**.
172
173
 
173
174
  key_filename
174
175
  ~~~~~~~~~~~~
@@ -183,6 +184,7 @@ A mapping of shortcut names to Fujin commands. Allows you to create convenient s
183
184
  Example:
184
185
 
185
186
  .. code-block:: toml
187
+ :caption: fujin.toml
186
188
 
187
189
  [aliases]
188
190
  console = "app exec -i shell_plus" # open an interactive django shell
@@ -221,6 +223,8 @@ class InstallationMode(StrEnum):
221
223
  class SecretAdapter(StrEnum):
222
224
  BITWARDEN = "bitwarden"
223
225
  ONE_PASSWORD = "1password"
226
+ DOPPLER = "doppler"
227
+ SYSTEM = "system"
224
228
 
225
229
 
226
230
  class SecretConfig(msgspec.Struct):
@@ -244,7 +248,10 @@ class Config(msgspec.Struct, kw_only=True):
244
248
  requirements: str | None = None
245
249
  hooks: HooksDict = msgspec.field(default_factory=dict)
246
250
  local_config_dir: Path = Path(".fujin")
247
- secret_config: SecretConfig | None = msgspec.field(name="secrets", default=None)
251
+ secret_config: SecretConfig | None = msgspec.field(
252
+ name="secrets",
253
+ default_factory=lambda: SecretConfig(adapter=SecretAdapter.SYSTEM),
254
+ )
248
255
 
249
256
  def __post_init__(self):
250
257
  if self.installation_mode == InstallationMode.PY_PACKAGE:
@@ -283,8 +290,8 @@ class HostConfig(msgspec.Struct, kw_only=True):
283
290
  ip: str | None = None
284
291
  domain_name: str
285
292
  user: str
286
- _env_file: str = msgspec.field(name="envfile", default="")
287
- env_content: str = ""
293
+ _env_file: str | None = msgspec.field(name="envfile", default=None)
294
+ env_content: str | None = msgspec.field(name="env", default=None)
288
295
  apps_dir: str = ".local/share/fujin"
289
296
  password_env: str | None = None
290
297
  ssh_port: int = 22
@@ -293,14 +300,14 @@ class HostConfig(msgspec.Struct, kw_only=True):
293
300
  def __post_init__(self):
294
301
  if self._env_file and self.env_content:
295
302
  raise ImproperlyConfiguredError(
296
- "Cannot set both 'env_content' and 'env_file' properties."
303
+ "Cannot set both 'env' and 'envfile' properties."
297
304
  )
298
- if not self.env_content:
305
+ if self._env_file:
299
306
  envfile = Path(self._env_file)
300
307
  if not envfile.exists():
301
308
  raise ImproperlyConfiguredError(f"{self._env_file} not found")
302
309
  self.env_content = envfile.read_text()
303
- self.env_content = self.env_content.strip()
310
+ self.env_content = self.env_content.strip() if self.env_content else ""
304
311
  self.apps_dir = f"/home/{self.user}/{self.apps_dir}"
305
312
  self.ip = self.ip or self.domain_name
306
313
 
fujin/secrets/__init__.py CHANGED
@@ -10,18 +10,24 @@ from dotenv import dotenv_values
10
10
  from fujin.config import SecretAdapter
11
11
  from fujin.config import SecretConfig
12
12
  from .bitwarden import bitwarden
13
+ from .dopppler import doppler
13
14
  from .onepassword import one_password
15
+ from .system import system
14
16
 
15
17
  secret_reader = Callable[[str], str]
16
18
  secret_adapter_context = Callable[[SecretConfig], ContextManager[secret_reader]]
17
19
 
18
20
  adapter_to_context: dict[SecretAdapter, secret_adapter_context] = {
21
+ SecretAdapter.SYSTEM: system,
19
22
  SecretAdapter.BITWARDEN: bitwarden,
20
23
  SecretAdapter.ONE_PASSWORD: one_password,
24
+ SecretAdapter.DOPPLER: doppler,
21
25
  }
22
26
 
23
27
 
24
28
  def resolve_secrets(env_content: str, secret_config: SecretConfig) -> str:
29
+ if not env_content: # this is really for empty string
30
+ return ""
25
31
  with closing(StringIO(env_content)) as buffer:
26
32
  env_dict = dotenv_values(stream=buffer)
27
33
  secrets = {key: value for key, value in env_dict.items() if value.startswith("$")}
@@ -31,7 +37,9 @@ def resolve_secrets(env_content: str, secret_config: SecretConfig) -> str:
31
37
  parsed_secrets = {}
32
38
  with adapter_context(secret_config) as reader:
33
39
  for key, secret in secrets.items():
34
- parsed_secrets[key] = gevent.spawn(reader, secret[1:])
40
+ parsed_secrets[key] = gevent.spawn(
41
+ reader, secret[1:]
42
+ ) # remove the leading $
35
43
  gevent.joinall(parsed_secrets.values())
36
44
  env_dict.update({key: thread.value for key, thread in parsed_secrets.items()})
37
45
  return "\n".join(f'{key}="{value}"' for key, value in env_dict.items())
@@ -0,0 +1,31 @@
1
+ from __future__ import annotations
2
+
3
+ import subprocess
4
+ from contextlib import contextmanager
5
+ from typing import Generator
6
+ from typing import TYPE_CHECKING
7
+
8
+ import cappa
9
+
10
+ from fujin.config import SecretConfig
11
+
12
+ if TYPE_CHECKING:
13
+ from . import secret_reader
14
+
15
+
16
+ @contextmanager
17
+ def doppler(_: SecretConfig) -> Generator[secret_reader, None, None]:
18
+ def read_secret(name: str) -> str:
19
+ result = subprocess.run(
20
+ ["doppler", "run", "--command", f"echo ${name}"],
21
+ capture_output=True,
22
+ text=True,
23
+ )
24
+ if result.returncode != 0:
25
+ raise cappa.Exit(result.stderr)
26
+ return result.stdout.strip()
27
+
28
+ try:
29
+ yield read_secret
30
+ finally:
31
+ pass
@@ -0,0 +1,19 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ from contextlib import contextmanager
5
+ from typing import Generator
6
+ from typing import TYPE_CHECKING
7
+
8
+ from fujin.config import SecretConfig
9
+
10
+ if TYPE_CHECKING:
11
+ from . import secret_reader
12
+
13
+
14
+ @contextmanager
15
+ def system(_: SecretConfig) -> Generator[secret_reader, None, None]:
16
+ try:
17
+ yield os.getenv
18
+ finally:
19
+ pass
@@ -0,0 +1,22 @@
1
+ # All options are documented here https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html
2
+ # Inspiration was taken from here https://docs.gunicorn.org/en/stable/deploy.html#systemd
3
+ [Unit]
4
+ Description={app_name} daemon
5
+ After=network.target
6
+
7
+ [Service]
8
+ User={user}
9
+ Group={user}
10
+ RuntimeDirectory={app_name}
11
+ WorkingDirectory={app_dir}
12
+ ExecStart={app_dir}/{command}
13
+ EnvironmentFile={app_dir}/.env
14
+ ExecReload=/bin/kill -s HUP $MAINPID
15
+ KillMode=mixed
16
+ TimeoutStopSec=5
17
+ PrivateTmp=true
18
+ # if your app does not need administrative capabilities, let systemd know
19
+ ProtectSystem=strict
20
+
21
+ [Install]
22
+ WantedBy=multi-user.target
@@ -6,8 +6,8 @@ Requires={app_name}.socket
6
6
  After=network.target
7
7
 
8
8
  [Service]
9
- #Type=notify
10
- #NotifyAccess=main
9
+ Type=notify
10
+ NotifyAccess=main
11
11
  User={user}
12
12
  Group={user}
13
13
  RuntimeDirectory={app_name}
@@ -1,6 +1,7 @@
1
1
  # All options are documented here https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html
2
2
  [Unit]
3
- Description={app_name} Worker
3
+ Description={app_name} {process_name}
4
+ After=network.target
4
5
 
5
6
  [Service]
6
7
  User={user}
@@ -1,11 +1,12 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: fujin-cli
3
- Version: 0.9.1
3
+ Version: 0.11.4
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
7
7
  Project-URL: Source, https://github.com/falcopackages/fujin
8
8
  Author-email: Tobi DEGNON <tobidegnon@proton.me>
9
+ License-File: LICENSE.txt
9
10
  Keywords: caddy,deployment,django,fastapi,litestar,python,systemd
10
11
  Classifier: Development Status :: 3 - Alpha
11
12
  Classifier: Intended Audience :: Developers
@@ -1,6 +1,6 @@
1
1
  fujin/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- fujin/__main__.py,sha256=VJMBzuQuxkQaAKNEySktGnms944bkRsrIAjj-XeaWR8,1813
3
- fujin/config.py,sha256=jE5iAu1ouHgv2eTut6CRBiKIzK-vxJqVtZuOr6NICEM,11365
2
+ fujin/__main__.py,sha256=qnRqZD-ZGTGeRTcPeHVzlvNNUj3exzftC_KILAlx-Hs,1849
3
+ fujin/config.py,sha256=t7UnhQMl-wYDyFCIcOC5lLdfpSmYWDIRPHvozoSoVME,11707
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
@@ -12,10 +12,10 @@ fujin/commands/config.py,sha256=WymGla-H2yduhLcYE1Nb6IJaFIj0S0KIhV9fBDCKsAw,2779
12
12
  fujin/commands/deploy.py,sha256=AylcVNX47ekf-AFfQrYcSh86I3qltADEgjPtV9lQAfE,5831
13
13
  fujin/commands/docs.py,sha256=b5FZ8AgoAfn4q4BueEQvM2w5HCuh8-rwBqv_CRFVU8E,349
14
14
  fujin/commands/down.py,sha256=aw_mxl_TMC66plKTXwlYP1W2XQBmHeROltQqOpQssyE,1577
15
- fujin/commands/init.py,sha256=AHAZeYkSk-zShF288Zs4rCfVwalCUqeMXWR6GTks2v0,4046
15
+ fujin/commands/init.py,sha256=9yrja4YYFlJqEarN0ekg6yPd5niifPwvQ4i4wnC55a0,4007
16
16
  fujin/commands/printenv.py,sha256=bS2mIgk7zd_w3yDhZLTa1PkHIzuSbBWjnYyM8CUjX0k,523
17
17
  fujin/commands/proxy.py,sha256=ajXwboS0gDDiMWW7b9rtWU6WPF1h7JYYeycDyU-hQfg,3053
18
- fujin/commands/prune.py,sha256=C2aAN6AUS84jgRg1eiCroyiuZyaZDmf5yvGAQY9xkcg,1517
18
+ fujin/commands/prune.py,sha256=3Y0ZpgoI8eb_FVD6XMmZK9lEp2delKUeJdK09kv9_08,1509
19
19
  fujin/commands/redeploy.py,sha256=491Mzz0qiaHqcI7BFUtZq-M34WQkiBd2ZgQbLRLp8T8,2355
20
20
  fujin/commands/rollback.py,sha256=JsocJzQcdQelSnYD94klhjBh8UKkkdiRD9shfUfo4FI,2032
21
21
  fujin/commands/server.py,sha256=-3-PyBNR0fGm-RYE3fz50kP-LSDYGU9BzUxrbGZEghc,3312
@@ -24,14 +24,17 @@ fujin/proxies/__init__.py,sha256=UuWYU175tkdaz1WWRCDDpQgGfFVYYNR9PBxA3lTCNr0,695
24
24
  fujin/proxies/caddy.py,sha256=H52D7cGEEGcxDaXxvClnny9lAat_h1G9dYlIlf6gwKo,7933
25
25
  fujin/proxies/dummy.py,sha256=qBKSn8XNEA9SVwB7GzRNX2l9Iw6tUjo2CFqZjWi0FjY,465
26
26
  fujin/proxies/nginx.py,sha256=BNJNLxLLRVAmBIGVCk8pb16iiSJsOI9jXOZhdSQGtX8,4151
27
- fujin/secrets/__init__.py,sha256=hiNZIBtOwM0WQbvAF3GhuMMCsQCjXjiSr5gTLAGN-VI,1375
27
+ fujin/secrets/__init__.py,sha256=nicjIkcEX22rSD-Fleg0jG2IaBtvEq1dbkcfZ7lGlGI,1633
28
28
  fujin/secrets/bitwarden.py,sha256=01GZL5hYwZzL6yXy5ab3L3kgBFBeOT8i3Yg9GC8YwFU,2008
29
+ fujin/secrets/dopppler.py,sha256=t5SGfyuA0RxsD9uvrAu4cG2TBDIUgB5gb6XYXPrd61Y,724
29
30
  fujin/secrets/onepassword.py,sha256=6Xj3XWttKfcjMbcoMZvXVpJW1KHxlD785DysmX_mqvk,654
30
- fujin/templates/simple.service,sha256=-lyKjmSyfHGucP4O_vRQE1NNaHq0Qjsc0twdwoRLgI0,321
31
- fujin/templates/web.service,sha256=NZ7ZeaFvV_MZTBn8QqRQeu8PIrWHf3aWYWNzjOQeqCw,685
32
- fujin/templates/web.socket,sha256=2lJsiOHlMJL0YlN7YBLLnr5zqsytPEt81yP34nk0dmc,173
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,,
31
+ fujin/secrets/system.py,sha256=Z5uNc2V3rcR75ffBnOsJywndoWuDcih88O9nPXIJ3U0,382
32
+ fujin/templates/simple.service,sha256=ySlLqF354UCvlQaJcEVj4jf4hK7aKAq9qseE6pmtXJI,350
33
+ fujin/templates/granian/web.service,sha256=D3vHOHdP3wrZueKuhlhkB4syOCVvGSciDsNsmU_Bp68,626
34
+ fujin/templates/gunicorn/web.service,sha256=EfTJQP4VvwlOTDyVCch5Y7iVhpPvZVaRWE6MHI4_z4k,683
35
+ fujin/templates/gunicorn/web.socket,sha256=2lJsiOHlMJL0YlN7YBLLnr5zqsytPEt81yP34nk0dmc,173
36
+ fujin_cli-0.11.4.dist-info/METADATA,sha256=MOALo-mvFTBlNaK-mnrEUSY0aZJSIqHL28m0CYM97KQ,4603
37
+ fujin_cli-0.11.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
38
+ fujin_cli-0.11.4.dist-info/entry_points.txt,sha256=Y_TBtKt3j11qhwquMexZR5yqnDEqOBDACtresqQFE-s,46
39
+ fujin_cli-0.11.4.dist-info/licenses/LICENSE.txt,sha256=0QF8XfuH0zkIHhSet6teXfiCze6JSdr8inRkmLLTDyo,1099
40
+ fujin_cli-0.11.4.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.26.3
2
+ Generator: hatchling 1.27.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
File without changes