fujin-cli 0.10.0__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/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,32 +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``
58
- - ``doppler``
54
+ The secret management service to use. The currently available options are *bitwarden*, *1password*, *doppler*
59
55
 
60
56
  password_env
61
57
  ~~~~~~~~~~~~
@@ -64,35 +60,39 @@ Environment variable containing the password for the service account. This is on
64
60
  Webserver
65
61
  ---------
66
62
 
63
+ Web server configurations.
64
+
67
65
  type
68
66
  ~~~~
69
67
  The reverse proxy implementation to use. Available options:
70
68
 
71
- - ``fujin.proxies.caddy`` (default)
72
- - ``fujin.proxies.nginx``
73
- - ``fujin.proxies.dummy`` (disables proxy functionality)
69
+ - *fujin.proxies.caddy* (default)
70
+ - *fujin.proxies.nginx*
71
+ - *fujin.proxies.dummy* (disables proxy)
74
72
 
75
73
  upstream
76
74
  ~~~~~~~~
77
75
  The address where your web application listens for requests. Supports any value compatible with your chosen web proxy:
78
76
 
79
- - HTTP address (e.g., ``localhost:8000``)
80
- - 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* )
81
80
 
82
81
  certbot_email
83
82
  ~~~~~~~~~~~~~
84
- 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.
85
84
 
86
85
  statics
87
86
  ~~~~~~~
88
87
 
89
88
  Defines the mapping of URL paths to local directories for serving static files. The syntax and support for static
90
89
  file serving depend on the selected reverse proxy. The directories you map should be accessible by the web server, meaning
91
- 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**.
92
91
 
93
92
  Example:
94
93
 
95
94
  .. code-block:: toml
95
+ :caption: fujin.toml
96
96
 
97
97
  [webserver]
98
98
  upstream = "unix//run/project.sock"
@@ -103,12 +103,12 @@ processes
103
103
  ---------
104
104
 
105
105
  A mapping of process names to commands that will be managed by the process manager. Define as many processes as needed, but
106
- when using any proxy other than ``fujin.proxies.dummy``, a ``web`` process must be declared. Refer to the ``apps_dir``
107
- 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.
108
107
 
109
108
  Example:
110
109
 
111
110
  .. code-block:: toml
111
+ :caption: fujin.toml
112
112
 
113
113
  [processes]
114
114
  web = ".venv/bin/gunicorn myproject.wsgi:application"
@@ -116,7 +116,8 @@ Example:
116
116
 
117
117
  .. note::
118
118
 
119
- 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.
120
121
  Here are the templates for the service files:
121
122
 
122
123
  - `web.service <https://github.com/falcopackages/fujin/blob/main/src/fujin/templates/web.service>`_
@@ -128,7 +129,7 @@ Host Configuration
128
129
 
129
130
  ip
130
131
  ~~
131
- 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*.
132
133
 
133
134
  domain_name
134
135
  ~~~~~~~~~~~
@@ -142,23 +143,23 @@ The login user for running remote tasks. Should have passwordless sudo access fo
142
143
 
143
144
  You can create a user with these requirements using the ``fujin server create-user`` command.
144
145
 
145
- env_file
146
- ~~~~~~~~
146
+ envfile
147
+ ~~~~~~~
147
148
  Path to the production environment file that will be copied to the host.
148
149
 
149
- env_content
150
- ~~~~~~~~~~~
150
+ env
151
+ ~~~
151
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.
152
153
 
153
154
  .. important::
154
155
 
155
- ``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.
156
157
 
157
158
  apps_dir
158
159
  ~~~~~~~~
159
160
 
160
161
  Base directory for project storage on the host. Path is relative to user's home directory.
161
- 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}**.
162
163
 
163
164
  password_env
164
165
  ~~~~~~~~~~~~
@@ -168,8 +169,7 @@ Environment variable containing the user's password. Only needed if the user can
168
169
  ssh_port
169
170
  ~~~~~~~~
170
171
 
171
- SSH port for connecting to the host.
172
- Default: ``22``
172
+ SSH port for connecting to the host. Default to **22**.
173
173
 
174
174
  key_filename
175
175
  ~~~~~~~~~~~~
@@ -184,6 +184,7 @@ A mapping of shortcut names to Fujin commands. Allows you to create convenient s
184
184
  Example:
185
185
 
186
186
  .. code-block:: toml
187
+ :caption: fujin.toml
187
188
 
188
189
  [aliases]
189
190
  console = "app exec -i shell_plus" # open an interactive django shell
@@ -223,6 +224,7 @@ class SecretAdapter(StrEnum):
223
224
  BITWARDEN = "bitwarden"
224
225
  ONE_PASSWORD = "1password"
225
226
  DOPPLER = "doppler"
227
+ SYSTEM = "system"
226
228
 
227
229
 
228
230
  class SecretConfig(msgspec.Struct):
@@ -246,7 +248,10 @@ class Config(msgspec.Struct, kw_only=True):
246
248
  requirements: str | None = None
247
249
  hooks: HooksDict = msgspec.field(default_factory=dict)
248
250
  local_config_dir: Path = Path(".fujin")
249
- 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
+ )
250
255
 
251
256
  def __post_init__(self):
252
257
  if self.installation_mode == InstallationMode.PY_PACKAGE:
@@ -285,8 +290,8 @@ class HostConfig(msgspec.Struct, kw_only=True):
285
290
  ip: str | None = None
286
291
  domain_name: str
287
292
  user: str
288
- _env_file: str = msgspec.field(name="envfile", default="")
289
- 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)
290
295
  apps_dir: str = ".local/share/fujin"
291
296
  password_env: str | None = None
292
297
  ssh_port: int = 22
@@ -295,14 +300,14 @@ class HostConfig(msgspec.Struct, kw_only=True):
295
300
  def __post_init__(self):
296
301
  if self._env_file and self.env_content:
297
302
  raise ImproperlyConfiguredError(
298
- "Cannot set both 'env_content' and 'env_file' properties."
303
+ "Cannot set both 'env' and 'envfile' properties."
299
304
  )
300
- if not self.env_content:
305
+ if self._env_file:
301
306
  envfile = Path(self._env_file)
302
307
  if not envfile.exists():
303
308
  raise ImproperlyConfiguredError(f"{self._env_file} not found")
304
309
  self.env_content = envfile.read_text()
305
- self.env_content = self.env_content.strip()
310
+ self.env_content = self.env_content.strip() if self.env_content else ""
306
311
  self.apps_dir = f"/home/{self.user}/{self.apps_dir}"
307
312
  self.ip = self.ip or self.domain_name
308
313
 
fujin/secrets/__init__.py CHANGED
@@ -12,11 +12,13 @@ from fujin.config import SecretConfig
12
12
  from .bitwarden import bitwarden
13
13
  from .dopppler import doppler
14
14
  from .onepassword import one_password
15
+ from .system import system
15
16
 
16
17
  secret_reader = Callable[[str], str]
17
18
  secret_adapter_context = Callable[[SecretConfig], ContextManager[secret_reader]]
18
19
 
19
20
  adapter_to_context: dict[SecretAdapter, secret_adapter_context] = {
21
+ SecretAdapter.SYSTEM: system,
20
22
  SecretAdapter.BITWARDEN: bitwarden,
21
23
  SecretAdapter.ONE_PASSWORD: one_password,
22
24
  SecretAdapter.DOPPLER: doppler,
@@ -24,6 +26,8 @@ adapter_to_context: dict[SecretAdapter, secret_adapter_context] = {
24
26
 
25
27
 
26
28
  def resolve_secrets(env_content: str, secret_config: SecretConfig) -> str:
29
+ if not env_content: # this is really for empty string
30
+ return ""
27
31
  with closing(StringIO(env_content)) as buffer:
28
32
  env_dict = dotenv_values(stream=buffer)
29
33
  secrets = {key: value for key, value in env_dict.items() if value.startswith("$")}
@@ -33,7 +37,9 @@ def resolve_secrets(env_content: str, secret_config: SecretConfig) -> str:
33
37
  parsed_secrets = {}
34
38
  with adapter_context(secret_config) as reader:
35
39
  for key, secret in secrets.items():
36
- parsed_secrets[key] = gevent.spawn(reader, secret[1:])
40
+ parsed_secrets[key] = gevent.spawn(
41
+ reader, secret[1:]
42
+ ) # remove the leading $
37
43
  gevent.joinall(parsed_secrets.values())
38
44
  env_dict.update({key: thread.value for key, thread in parsed_secrets.items()})
39
45
  return "\n".join(f'{key}="{value}"' for key, value in env_dict.items())
@@ -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.10.0
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=xh7yAStKcCx2bcIgzK0-ocLMtiqsgUtar9xqpqKQKww,11403
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,7 +12,7 @@ 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
18
  fujin/commands/prune.py,sha256=3Y0ZpgoI8eb_FVD6XMmZK9lEp2delKUeJdK09kv9_08,1509
@@ -24,15 +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=WkdCtnBXjB7y_HHUWySf53LEe15Z7CZbE3cfDGxZiA8,1441
27
+ fujin/secrets/__init__.py,sha256=nicjIkcEX22rSD-Fleg0jG2IaBtvEq1dbkcfZ7lGlGI,1633
28
28
  fujin/secrets/bitwarden.py,sha256=01GZL5hYwZzL6yXy5ab3L3kgBFBeOT8i3Yg9GC8YwFU,2008
29
29
  fujin/secrets/dopppler.py,sha256=t5SGfyuA0RxsD9uvrAu4cG2TBDIUgB5gb6XYXPrd61Y,724
30
30
  fujin/secrets/onepassword.py,sha256=6Xj3XWttKfcjMbcoMZvXVpJW1KHxlD785DysmX_mqvk,654
31
- fujin/templates/simple.service,sha256=-lyKjmSyfHGucP4O_vRQE1NNaHq0Qjsc0twdwoRLgI0,321
32
- fujin/templates/web.service,sha256=NZ7ZeaFvV_MZTBn8QqRQeu8PIrWHf3aWYWNzjOQeqCw,685
33
- fujin/templates/web.socket,sha256=2lJsiOHlMJL0YlN7YBLLnr5zqsytPEt81yP34nk0dmc,173
34
- fujin_cli-0.10.0.dist-info/METADATA,sha256=MBl9vT2C2Iq8pxVkuC9vgfmV7lhWBoZxjKxRf854QkQ,4577
35
- fujin_cli-0.10.0.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
36
- fujin_cli-0.10.0.dist-info/entry_points.txt,sha256=Y_TBtKt3j11qhwquMexZR5yqnDEqOBDACtresqQFE-s,46
37
- fujin_cli-0.10.0.dist-info/licenses/LICENSE.txt,sha256=0QF8XfuH0zkIHhSet6teXfiCze6JSdr8inRkmLLTDyo,1099
38
- fujin_cli-0.10.0.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