fujin-cli 0.6.0__tar.gz → 0.7.0__tar.gz
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_cli-0.7.0/CHANGELOG.md +18 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/PKG-INFO +26 -14
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/README.md +23 -13
- fujin_cli-0.7.0/docs/changelog.rst +2 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/docs/commands/deploy.rst +12 -11
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/docs/commands/index.rst +1 -1
- fujin_cli-0.6.0/docs/commands/secrets.rst → fujin_cli-0.7.0/docs/commands/printenv.rst +1 -1
- fujin_cli-0.7.0/docs/index.rst +30 -0
- fujin_cli-0.7.0/docs/installation.rst +23 -0
- fujin_cli-0.7.0/docs/requirements.txt +138 -0
- fujin_cli-0.7.0/docs/secrets.rst +50 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/justfile +1 -1
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/pyproject.toml +4 -2
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/src/fujin/__main__.py +6 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/src/fujin/commands/deploy.py +22 -12
- fujin_cli-0.7.0/src/fujin/commands/printenv.py +16 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/src/fujin/commands/redeploy.py +5 -1
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/src/fujin/config.py +33 -1
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/src/fujin/proxies/nginx.py +4 -5
- fujin_cli-0.7.0/src/fujin/secrets/__init__.py +32 -0
- fujin_cli-0.7.0/src/fujin/secrets/bitwarden.py +62 -0
- fujin_cli-0.7.0/src/fujin/secrets/onepassword.py +26 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/uv.lock +156 -1
- fujin_cli-0.6.0/CHANGELOG.md +0 -7
- fujin_cli-0.6.0/docs/changelog.rst +0 -2
- fujin_cli-0.6.0/docs/index.rst +0 -52
- fujin_cli-0.6.0/docs/installation.rst +0 -16
- fujin_cli-0.6.0/docs/requirements.txt +0 -1090
- fujin_cli-0.6.0/src/fujin/commands/secrets.py +0 -11
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/.github/workflows/publish.yml +0 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/.gitignore +0 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/.pre-commit-config.yaml +0 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/.readthedocs.yaml +0 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/LICENSE.txt +0 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/Vagrantfile +0 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/docs/commands/app.rst +0 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/docs/commands/config.rst +0 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/docs/commands/docs.rst +0 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/docs/commands/down.rst +0 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/docs/commands/init.rst +0 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/docs/commands/proxy.rst +0 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/docs/commands/prune.rst +0 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/docs/commands/redeploy.rst +0 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/docs/commands/rollback.rst +0 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/docs/commands/server.rst +0 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/docs/commands/up.rst +0 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/docs/conf.py +0 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/docs/configuration.rst +0 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/docs/hooks.rst +0 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/docs/tutorial.rst +0 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/examples/django/bookstore/README.md +0 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/examples/django/bookstore/bookstore/__init__.py +0 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/examples/django/bookstore/bookstore/__main__.py +0 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/examples/django/bookstore/bookstore/asgi.py +0 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/examples/django/bookstore/bookstore/settings.py +0 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/examples/django/bookstore/bookstore/urls.py +0 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/examples/django/bookstore/bookstore/wsgi.py +0 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/examples/django/bookstore/fujin.toml +0 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/examples/django/bookstore/manage.py +0 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/examples/django/bookstore/pyproject.toml +0 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/examples/django/bookstore/requirements.txt +0 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/examples/golang/pocketbase/.env.prod +0 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/examples/golang/pocketbase/fujin.toml +0 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/src/fujin/__init__.py +0 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/src/fujin/commands/__init__.py +0 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/src/fujin/commands/_base.py +0 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/src/fujin/commands/app.py +0 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/src/fujin/commands/config.py +0 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/src/fujin/commands/docs.py +0 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/src/fujin/commands/down.py +0 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/src/fujin/commands/init.py +0 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/src/fujin/commands/proxy.py +0 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/src/fujin/commands/prune.py +0 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/src/fujin/commands/rollback.py +0 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/src/fujin/commands/server.py +0 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/src/fujin/commands/up.py +0 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/src/fujin/connection.py +0 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/src/fujin/errors.py +0 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/src/fujin/hooks.py +0 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/src/fujin/process_managers/__init__.py +0 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/src/fujin/process_managers/systemd.py +0 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/src/fujin/proxies/__init__.py +0 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/src/fujin/proxies/caddy.py +0 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/src/fujin/proxies/dummy.py +0 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/src/fujin/templates/simple.service +0 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/src/fujin/templates/web.service +0 -0
- {fujin_cli-0.6.0 → fujin_cli-0.7.0}/src/fujin/templates/web.socket +0 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
## [0.7.0] - 2024-11-22
|
|
8
|
+
|
|
9
|
+
### 🚀 Features
|
|
10
|
+
|
|
11
|
+
- Inject secrets via bitwarden and 1password (#29)
|
|
12
|
+
- Add certbot_email configuration for nginx
|
|
13
|
+
|
|
14
|
+
### 🚜 Refactor
|
|
15
|
+
|
|
16
|
+
- Move requirements copy to transfer_files
|
|
17
|
+
|
|
18
|
+
<!-- generated by git-cliff -->
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: fujin-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.0
|
|
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
|
|
@@ -21,36 +21,48 @@ Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
|
21
21
|
Requires-Python: >=3.10
|
|
22
22
|
Requires-Dist: cappa>=0.24
|
|
23
23
|
Requires-Dist: fabric>=3.2.2
|
|
24
|
+
Requires-Dist: gevent[recommended]>=24.11.1
|
|
24
25
|
Requires-Dist: msgspec[toml]>=0.18.6
|
|
26
|
+
Requires-Dist: python-dotenv>=1.0.1
|
|
25
27
|
Requires-Dist: rich>=13.9.2
|
|
26
28
|
Description-Content-Type: text/markdown
|
|
27
29
|
|
|
28
30
|
# fujin
|
|
29
31
|
|
|
32
|
+
> [!IMPORTANT]
|
|
33
|
+
> This tool is currently contains minimal features and is a work-in-progress
|
|
34
|
+
|
|
35
|
+
<!-- content:start -->
|
|
36
|
+
|
|
37
|
+
`fujin` is a simple deployment tool that helps you get your project up and running on a VPS in minutes. It manages your app processes using [systemd](https://systemd.io) and runs your apps behind [caddy](https://caddyserver.com).
|
|
38
|
+
|
|
39
|
+
[](https://github.com/falcopackages/fujin/actions/workflows/publish.yml)
|
|
30
40
|
[](https://pypi.org/project/fujin-cli)
|
|
31
41
|
[](https://pypi.org/project/fujin-cli)
|
|
32
42
|
[](https://github.com/falcopackages/fujin/blob/main/LICENSE.txt)
|
|
33
43
|
[](https://pypi.org/project/fujin-cli)
|
|
34
|
-
-----
|
|
35
44
|
|
|
36
|
-
|
|
37
|
-
> This package currently contains minimal features and is a work-in-progress
|
|
38
|
-
|
|
39
|
-
<!-- content:start -->
|
|
45
|
+
## Features
|
|
40
46
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
47
|
+
- 🚀 One-command server bootstrap
|
|
48
|
+
- 🔄 Rollback broken deployments
|
|
49
|
+
- 🔐 Zero configuration SSL certificates
|
|
50
|
+
- 🔁 Swappable proxy ([caddy](https://caddyserver.com), [nginx](https://nginx.org/en/) and `dummy` to disable proxy)
|
|
51
|
+
- 🛠️ Secrets injection from password managers ([Bitwarden](https://bitwarden.com/), [1Password](https://1password.com), etc.)
|
|
52
|
+
- 📝 Easily customizable `systemd` and `proxy` configurations
|
|
53
|
+
- 👨💻 Remote application management and log streaming
|
|
54
|
+
- 🐍 Supports packaged python apps and self-contained binaries
|
|
44
55
|
|
|
45
|
-
|
|
56
|
+
For more details, check out the [documentation📚](https://fujin.oluwatobi.dev/en/latest/).
|
|
46
57
|
|
|
47
58
|
## Why?
|
|
48
59
|
|
|
49
60
|
I wanted [kamal](https://kamal-deploy.org/) but without Docker, and I thought the idea was fun. At its core, this project automates versions of this [tutorial](https://www.digitalocean.com/community/tutorials/how-to-set-up-django-with-postgres-nginx-and-gunicorn-on-ubuntu). If you've been a Django beginner
|
|
50
|
-
trying to get your app in production, you probably went through this.
|
|
61
|
+
trying to get your app in production, you probably went through this.
|
|
62
|
+
|
|
63
|
+
I'm using `caddy` here by default instead of `nginx` because it's configurable via an API and it's is a no-brainer for SSL certificates. `Systemd` is the default on most Linux distributions and does a good enough job.
|
|
51
64
|
|
|
52
|
-
Fujin was initially planned to be a Python-only project, but the core concepts can be applied to any language that can produce a single distributable file (e.g., Go, Rust).
|
|
53
|
-
I'm currently rocking SQLite in production for my side projects and ths setup is enough for my use case.
|
|
65
|
+
Fujin was initially planned to be a Python-only project, but the core concepts can be applied to any language that can produce a single distributable file (e.g., Go, Rust).
|
|
54
66
|
|
|
55
67
|
The goal is to automate deployment while leaving you in full control of your Linux box. It's not a CLI PaaS - it's simple and expects you to be able to SSH into your server and troubleshoot if necessary. For beginners, it makes the initial deployment easier while you get your hands dirty with Linux.
|
|
56
68
|
If you need a never-break, worry-free, set-it-and-forget-it setup that auto-scales and does all the magic, fujin probably isn't for you.
|
|
@@ -66,4 +78,4 @@ Fujin draws inspiration from the following tools for their developer experience.
|
|
|
66
78
|
|
|
67
79
|
`fujin` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license.
|
|
68
80
|
|
|
69
|
-
<!-- content:end -->
|
|
81
|
+
<!-- content:end -->
|
|
@@ -1,29 +1,39 @@
|
|
|
1
1
|
# fujin
|
|
2
2
|
|
|
3
|
+
> [!IMPORTANT]
|
|
4
|
+
> This tool is currently contains minimal features and is a work-in-progress
|
|
5
|
+
|
|
6
|
+
<!-- content:start -->
|
|
7
|
+
|
|
8
|
+
`fujin` is a simple deployment tool that helps you get your project up and running on a VPS in minutes. It manages your app processes using [systemd](https://systemd.io) and runs your apps behind [caddy](https://caddyserver.com).
|
|
9
|
+
|
|
10
|
+
[](https://github.com/falcopackages/fujin/actions/workflows/publish.yml)
|
|
3
11
|
[](https://pypi.org/project/fujin-cli)
|
|
4
12
|
[](https://pypi.org/project/fujin-cli)
|
|
5
13
|
[](https://github.com/falcopackages/fujin/blob/main/LICENSE.txt)
|
|
6
14
|
[](https://pypi.org/project/fujin-cli)
|
|
7
|
-
-----
|
|
8
15
|
|
|
9
|
-
|
|
10
|
-
> This package currently contains minimal features and is a work-in-progress
|
|
11
|
-
|
|
12
|
-
<!-- content:start -->
|
|
16
|
+
## Features
|
|
13
17
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
18
|
+
- 🚀 One-command server bootstrap
|
|
19
|
+
- 🔄 Rollback broken deployments
|
|
20
|
+
- 🔐 Zero configuration SSL certificates
|
|
21
|
+
- 🔁 Swappable proxy ([caddy](https://caddyserver.com), [nginx](https://nginx.org/en/) and `dummy` to disable proxy)
|
|
22
|
+
- 🛠️ Secrets injection from password managers ([Bitwarden](https://bitwarden.com/), [1Password](https://1password.com), etc.)
|
|
23
|
+
- 📝 Easily customizable `systemd` and `proxy` configurations
|
|
24
|
+
- 👨💻 Remote application management and log streaming
|
|
25
|
+
- 🐍 Supports packaged python apps and self-contained binaries
|
|
17
26
|
|
|
18
|
-
|
|
27
|
+
For more details, check out the [documentation📚](https://fujin.oluwatobi.dev/en/latest/).
|
|
19
28
|
|
|
20
29
|
## Why?
|
|
21
30
|
|
|
22
31
|
I wanted [kamal](https://kamal-deploy.org/) but without Docker, and I thought the idea was fun. At its core, this project automates versions of this [tutorial](https://www.digitalocean.com/community/tutorials/how-to-set-up-django-with-postgres-nginx-and-gunicorn-on-ubuntu). If you've been a Django beginner
|
|
23
|
-
trying to get your app in production, you probably went through this.
|
|
32
|
+
trying to get your app in production, you probably went through this.
|
|
33
|
+
|
|
34
|
+
I'm using `caddy` here by default instead of `nginx` because it's configurable via an API and it's is a no-brainer for SSL certificates. `Systemd` is the default on most Linux distributions and does a good enough job.
|
|
24
35
|
|
|
25
|
-
Fujin was initially planned to be a Python-only project, but the core concepts can be applied to any language that can produce a single distributable file (e.g., Go, Rust).
|
|
26
|
-
I'm currently rocking SQLite in production for my side projects and ths setup is enough for my use case.
|
|
36
|
+
Fujin was initially planned to be a Python-only project, but the core concepts can be applied to any language that can produce a single distributable file (e.g., Go, Rust).
|
|
27
37
|
|
|
28
38
|
The goal is to automate deployment while leaving you in full control of your Linux box. It's not a CLI PaaS - it's simple and expects you to be able to SSH into your server and troubleshoot if necessary. For beginners, it makes the initial deployment easier while you get your hands dirty with Linux.
|
|
29
39
|
If you need a never-break, worry-free, set-it-and-forget-it setup that auto-scales and does all the magic, fujin probably isn't for you.
|
|
@@ -39,4 +49,4 @@ Fujin draws inspiration from the following tools for their developer experience.
|
|
|
39
49
|
|
|
40
50
|
`fujin` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license.
|
|
41
51
|
|
|
42
|
-
<!-- content:end -->
|
|
52
|
+
<!-- content:end -->
|
|
@@ -12,23 +12,25 @@ How it works
|
|
|
12
12
|
|
|
13
13
|
Here's a high-level overview of what happens when you run the ``deploy`` command:
|
|
14
14
|
|
|
15
|
-
1. **
|
|
15
|
+
1. **Resolve secrets**: If you have defined a ``secrets`` configuration, it will be used to retrieve pull the ``secrets`` defined in your ``envfile``.
|
|
16
16
|
|
|
17
|
-
2. **
|
|
17
|
+
2. **Build the Application**: Your application is built using the ``build_command`` specified in your configuration.
|
|
18
18
|
|
|
19
|
-
3. **
|
|
19
|
+
3. **Transfer Files**: The environment variables file (``.env``) and the distribution file are transferred to the remote server. Optionally transfers ``requirements`` file (if specified).
|
|
20
20
|
|
|
21
|
-
4. **
|
|
21
|
+
4. **Install the Project**: Depending on the installation mode (Python package or binary), the project is installed on the remote server. For a Python package, a virtual environment is set up, dependencies are installed, and the distribution file (the wheel file) is then installed. For a binary, the binary file for the latest version is linked to the root of the application directory.
|
|
22
22
|
|
|
23
|
-
5. **
|
|
23
|
+
5. **Application Release**: If a ``release_command`` is specified in the configuration, it is executed at this stage.
|
|
24
24
|
|
|
25
|
-
6. **
|
|
25
|
+
6. **Configure and Start Services**: Configuration files for both ``systemd`` and the ``proxy`` (e.g., Caddy, by default) are generated or copied if previously exported. These configuration files are moved to their appropriate directories. A configuration reload is performed, and all relevant services are restarted.
|
|
26
26
|
|
|
27
|
-
7. **
|
|
27
|
+
7. **Update Version History**: The deployed version is recorded in the ``.versions`` file on the remote server.
|
|
28
28
|
|
|
29
|
-
8. **
|
|
29
|
+
8. **Prune Old Assets**: Old versions of the application are removed based on the ``versions_to_keep`` configuration.
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
9. **Completion**: A success message is displayed, and the URL to access the deployed project is provided.
|
|
32
|
+
|
|
33
|
+
Below is an example of the layout and structure of a deployed application:
|
|
32
34
|
|
|
33
35
|
.. tab-set::
|
|
34
36
|
|
|
@@ -63,5 +65,4 @@ Below is an example file layout and structure of the deployed application:
|
|
|
63
65
|
├── v1.2.2/
|
|
64
66
|
│ └── ...
|
|
65
67
|
└── v1.2.1/
|
|
66
|
-
└── ...
|
|
67
|
-
|
|
68
|
+
└── ...
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
fujin documentation
|
|
2
|
+
===================
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
.. important::
|
|
6
|
+
|
|
7
|
+
This a work in progress, not ready for production use yet.
|
|
8
|
+
|
|
9
|
+
.. raw:: html
|
|
10
|
+
|
|
11
|
+
<script src="https://asciinema.org/a/687274.js" id="asciicast-687274" async="true"></script>
|
|
12
|
+
|
|
13
|
+
.. include:: ../README.md
|
|
14
|
+
:parser: myst_parser.sphinx_
|
|
15
|
+
:start-after: <!-- content:start -->
|
|
16
|
+
:end-before: <!-- content:end -->
|
|
17
|
+
|
|
18
|
+
.. toctree::
|
|
19
|
+
:maxdepth: 2
|
|
20
|
+
:caption: Contents:
|
|
21
|
+
:hidden:
|
|
22
|
+
|
|
23
|
+
installation
|
|
24
|
+
tutorial
|
|
25
|
+
configuration
|
|
26
|
+
commands/index
|
|
27
|
+
secrets
|
|
28
|
+
hooks
|
|
29
|
+
changelog
|
|
30
|
+
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
Installation
|
|
2
|
+
============
|
|
3
|
+
|
|
4
|
+
If you have `uv <https://docs.astral.sh/uv/getting-started/installation/>`_ installed, run:
|
|
5
|
+
|
|
6
|
+
.. code-block:: shell
|
|
7
|
+
|
|
8
|
+
uv tool install fujin-cli
|
|
9
|
+
|
|
10
|
+
If not, download a binary version from the GitHub `releases page <https://github.com/falcopackages/fujin/releases>`_ and move the downloaded file
|
|
11
|
+
to a directory in your PATH.
|
|
12
|
+
|
|
13
|
+
Here is an example of how to install the binary on x86 macOS:
|
|
14
|
+
|
|
15
|
+
.. code-block:: shell
|
|
16
|
+
|
|
17
|
+
curl -LsSfO https://github.com/falcopackages/fujin/releases/download/v0.6.0/fujin_cli-x86_64-osx
|
|
18
|
+
chmod +x fujin_cli-x86_64-osx
|
|
19
|
+
mv fujin_cli-x86_64-osx /usr/local/bin/fujin
|
|
20
|
+
|
|
21
|
+
.. note::
|
|
22
|
+
|
|
23
|
+
If you install ``fujin`` via the GitHub release, you can keep it up to date with ``fujin self update``.
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# This file was autogenerated by uv via the following command:
|
|
2
|
+
# uv export --no-hashes --group docs --format requirements-txt
|
|
3
|
+
-e .
|
|
4
|
+
alabaster==1.0.0
|
|
5
|
+
annotated-types==0.7.0
|
|
6
|
+
anyio==4.6.2.post1
|
|
7
|
+
appnope==0.1.4 ; platform_system == 'Darwin'
|
|
8
|
+
asgiref==3.8.1
|
|
9
|
+
astroid==3.3.5
|
|
10
|
+
asttokens==2.4.1
|
|
11
|
+
attrs==24.2.0
|
|
12
|
+
babel==2.16.0
|
|
13
|
+
bcrypt==4.2.0
|
|
14
|
+
beautifulsoup4==4.12.3
|
|
15
|
+
bleach==6.2.0
|
|
16
|
+
bracex==2.5.post1
|
|
17
|
+
bump-my-version==0.28.0
|
|
18
|
+
cappa==0.24.0
|
|
19
|
+
certifi==2024.8.30
|
|
20
|
+
cffi==1.17.1
|
|
21
|
+
charset-normalizer==3.4.0
|
|
22
|
+
click==8.1.7
|
|
23
|
+
colorama==0.4.6
|
|
24
|
+
comm==0.2.2
|
|
25
|
+
cryptography==43.0.1
|
|
26
|
+
debugpy==1.8.7
|
|
27
|
+
decorator==5.1.1
|
|
28
|
+
defusedxml==0.7.1
|
|
29
|
+
deprecated==1.2.14
|
|
30
|
+
django==5.1.2
|
|
31
|
+
docutils==0.21.2
|
|
32
|
+
exceptiongroup==1.2.2 ; python_full_version < '3.11'
|
|
33
|
+
executing==2.1.0
|
|
34
|
+
fabric==3.2.2
|
|
35
|
+
faker==30.8.1
|
|
36
|
+
fastapi==0.115.4
|
|
37
|
+
fastjsonschema==2.20.0
|
|
38
|
+
git-cliff==2.6.1
|
|
39
|
+
h11==0.14.0
|
|
40
|
+
httpcore==1.0.6
|
|
41
|
+
httpx==0.27.2
|
|
42
|
+
idna==3.10
|
|
43
|
+
imagesize==1.4.1
|
|
44
|
+
invoke==2.2.0
|
|
45
|
+
ipykernel==6.29.5
|
|
46
|
+
ipython==8.18.0
|
|
47
|
+
ipywidgets==8.1.5
|
|
48
|
+
jedi==0.19.1
|
|
49
|
+
jinja2==3.1.4
|
|
50
|
+
jsonschema==4.23.0
|
|
51
|
+
jsonschema-specifications==2024.10.1
|
|
52
|
+
jupyter-client==8.6.3
|
|
53
|
+
jupyter-core==5.7.2
|
|
54
|
+
jupyter-sphinx==0.5.3
|
|
55
|
+
jupyterlab-pygments==0.3.0
|
|
56
|
+
jupyterlab-widgets==3.0.13
|
|
57
|
+
litestar==2.12.1
|
|
58
|
+
markdown-it-py==3.0.0
|
|
59
|
+
markupsafe==3.0.2
|
|
60
|
+
matplotlib-inline==0.1.7
|
|
61
|
+
mdit-py-plugins==0.4.2
|
|
62
|
+
mdurl==0.1.2
|
|
63
|
+
mistune==3.0.2
|
|
64
|
+
msgspec==0.18.6
|
|
65
|
+
multidict==6.1.0
|
|
66
|
+
myst-parser==4.0.0
|
|
67
|
+
nbclient==0.10.0
|
|
68
|
+
nbconvert==7.16.4
|
|
69
|
+
nbformat==5.10.4
|
|
70
|
+
nest-asyncio==1.6.0
|
|
71
|
+
packaging==24.1
|
|
72
|
+
pandocfilters==1.5.1
|
|
73
|
+
paramiko==3.5.0
|
|
74
|
+
parso==0.8.4
|
|
75
|
+
pexpect==4.9.0 ; sys_platform != 'win32'
|
|
76
|
+
platformdirs==4.3.6
|
|
77
|
+
polyfactory==2.17.0
|
|
78
|
+
prompt-toolkit==3.0.36
|
|
79
|
+
psutil==6.1.0
|
|
80
|
+
ptyprocess==0.7.0 ; sys_platform != 'win32'
|
|
81
|
+
pure-eval==0.2.3
|
|
82
|
+
pycparser==2.22
|
|
83
|
+
pydantic==2.9.2
|
|
84
|
+
pydantic-core==2.23.4
|
|
85
|
+
pydantic-settings==2.6.0
|
|
86
|
+
pygments==2.18.0
|
|
87
|
+
pynacl==1.5.0
|
|
88
|
+
python-dateutil==2.9.0.post0
|
|
89
|
+
python-dotenv==1.0.1
|
|
90
|
+
pywin32==308 ; platform_python_implementation != 'PyPy' and sys_platform == 'win32'
|
|
91
|
+
pyyaml==6.0.2
|
|
92
|
+
pyzmq==26.2.0
|
|
93
|
+
questionary==2.0.1
|
|
94
|
+
referencing==0.35.1
|
|
95
|
+
requests==2.32.3
|
|
96
|
+
rich==13.9.2
|
|
97
|
+
rich-click==1.8.3
|
|
98
|
+
rpds-py==0.20.1
|
|
99
|
+
setuptools==75.5.0
|
|
100
|
+
shibuya==2024.10.15
|
|
101
|
+
six==1.16.0
|
|
102
|
+
sniffio==1.3.1
|
|
103
|
+
snowballstemmer==2.2.0
|
|
104
|
+
soupsieve==2.6
|
|
105
|
+
sphinx==8.1.3
|
|
106
|
+
sphinx-autobuild==2024.10.3
|
|
107
|
+
sphinx-autodoc2==0.5.0
|
|
108
|
+
sphinx-copybutton==0.5.2
|
|
109
|
+
sphinx-design==0.6.1
|
|
110
|
+
sphinx-togglebutton==0.3.2
|
|
111
|
+
sphinxcontrib-applehelp==2.0.0
|
|
112
|
+
sphinxcontrib-devhelp==2.0.0
|
|
113
|
+
sphinxcontrib-htmlhelp==2.1.0
|
|
114
|
+
sphinxcontrib-jsmath==1.0.1
|
|
115
|
+
sphinxcontrib-qthelp==2.0.0
|
|
116
|
+
sphinxcontrib-serializinghtml==2.0.0
|
|
117
|
+
sqlparse==0.5.1
|
|
118
|
+
stack-data==0.6.3
|
|
119
|
+
starlette==0.41.0
|
|
120
|
+
tinycss2==1.4.0
|
|
121
|
+
tomli==2.0.2 ; python_full_version < '3.11'
|
|
122
|
+
tomli-w==1.1.0
|
|
123
|
+
tomlkit==0.13.2
|
|
124
|
+
tornado==6.4.1
|
|
125
|
+
traitlets==5.14.3
|
|
126
|
+
type-lens==0.2.3
|
|
127
|
+
typing-extensions==4.12.2
|
|
128
|
+
tzdata==2024.2 ; sys_platform == 'win32'
|
|
129
|
+
urllib3==2.2.3
|
|
130
|
+
uvicorn==0.32.0
|
|
131
|
+
watchfiles==0.24.0
|
|
132
|
+
wcmatch==10.0
|
|
133
|
+
wcwidth==0.2.13
|
|
134
|
+
webencodings==0.5.1
|
|
135
|
+
websockets==13.1
|
|
136
|
+
wheel==0.45.0
|
|
137
|
+
widgetsnbextension==4.0.13
|
|
138
|
+
wrapt==1.16.0
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
Secrets
|
|
2
|
+
=======
|
|
3
|
+
|
|
4
|
+
Bitwarden
|
|
5
|
+
---------
|
|
6
|
+
|
|
7
|
+
First, download and install the `Bitwarden CLI <https://bitwarden.com/help/cli/#download-and-install>`_. Make sure to log in to your account.
|
|
8
|
+
You should be able to run ``bw get password <name_of_secret>`` and get the value for the secret. This is the command that will be executed when pulling your secrets.
|
|
9
|
+
|
|
10
|
+
Add the following to your ``fujin.toml`` file:
|
|
11
|
+
|
|
12
|
+
.. code-block:: toml
|
|
13
|
+
|
|
14
|
+
[secrets]
|
|
15
|
+
adapter = "bitwarden"
|
|
16
|
+
password_env = "BW_PASSWORD"
|
|
17
|
+
|
|
18
|
+
To unlock the Bitwarden vault, the password is required. You can set the ``BW_PASSWORD`` environment variable in your shell, which will be used to unlock the vault.
|
|
19
|
+
When Fujin signs in, it will always sync the vault first.
|
|
20
|
+
|
|
21
|
+
Alternatively, you can set the ``BW_SESSION`` environment variable. If ``BW_SESSION`` is present, Fujin will use it directly without signing in or syncing the vault. In this case, the ``password_env`` configuration is not required.
|
|
22
|
+
|
|
23
|
+
.. code-block:: text
|
|
24
|
+
:caption: Example of an environment file with Bitwarden secrets
|
|
25
|
+
|
|
26
|
+
DEBUG=False
|
|
27
|
+
AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID
|
|
28
|
+
AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY
|
|
29
|
+
|
|
30
|
+
Note the ``$`` sign, which indicates to ``fujin`` that it is a secret.
|
|
31
|
+
|
|
32
|
+
1Password
|
|
33
|
+
---------
|
|
34
|
+
|
|
35
|
+
Download and install the `1Password CLI <https://developer.1password.com/docs/cli>`_, and sign in to your account.
|
|
36
|
+
You need to be actively signed in for Fujin to work with 1Password.
|
|
37
|
+
|
|
38
|
+
Update your ``fujin.toml`` file with the following configuration:
|
|
39
|
+
|
|
40
|
+
.. code-block:: toml
|
|
41
|
+
|
|
42
|
+
[secrets]
|
|
43
|
+
adapter = "1password"
|
|
44
|
+
|
|
45
|
+
.. code-block:: text
|
|
46
|
+
:caption: Example of an environment file with 1Password secrets
|
|
47
|
+
|
|
48
|
+
DEBUG=False
|
|
49
|
+
AWS_ACCESS_KEY_ID=$op://personal/aws-access-key-id/password
|
|
50
|
+
AWS_SECRET_ACCESS_KEY=$op://personal/aws-secret-access-key/password
|
|
@@ -49,7 +49,7 @@ fujin *ARGS:
|
|
|
49
49
|
uv run --group docs sphinx-autobuild docs docs/_build/html --port 8002 --watch src/fujin
|
|
50
50
|
|
|
51
51
|
@docs-requirements:
|
|
52
|
-
uv export --group docs --format requirements-txt > docs/requirements.txt
|
|
52
|
+
uv export --no-hashes --group docs --format requirements-txt > docs/requirements.txt
|
|
53
53
|
|
|
54
54
|
# -------------------------------------------------------------------------
|
|
55
55
|
# RELEASE UTILITIES
|
|
@@ -7,7 +7,7 @@ requires = [
|
|
|
7
7
|
|
|
8
8
|
[project]
|
|
9
9
|
name = "fujin-cli"
|
|
10
|
-
version = "0.
|
|
10
|
+
version = "0.7.0"
|
|
11
11
|
description = "Get your project up and running in a few minutes on your own vps."
|
|
12
12
|
readme = "README.md"
|
|
13
13
|
keywords = [
|
|
@@ -40,7 +40,9 @@ classifiers = [
|
|
|
40
40
|
dependencies = [
|
|
41
41
|
"cappa>=0.24",
|
|
42
42
|
"fabric>=3.2.2",
|
|
43
|
+
"gevent[recommended]>=24.11.1",
|
|
43
44
|
"msgspec[toml]>=0.18.6",
|
|
45
|
+
"python-dotenv>=1.0.1",
|
|
44
46
|
"rich>=13.9.2",
|
|
45
47
|
]
|
|
46
48
|
|
|
@@ -152,7 +154,7 @@ lint.isort.required-imports = [
|
|
|
152
154
|
lint.pyupgrade.keep-runtime-typing = true
|
|
153
155
|
|
|
154
156
|
[tool.bumpversion]
|
|
155
|
-
current_version = "0.
|
|
157
|
+
current_version = "0.7.0"
|
|
156
158
|
parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)"
|
|
157
159
|
serialize = [
|
|
158
160
|
"{major}.{minor}.{patch}",
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
from gevent import monkey
|
|
2
|
+
|
|
3
|
+
monkey.patch_all()
|
|
4
|
+
|
|
1
5
|
import shlex
|
|
2
6
|
import sys
|
|
3
7
|
from pathlib import Path
|
|
@@ -16,6 +20,7 @@ from fujin.commands.redeploy import Redeploy
|
|
|
16
20
|
from fujin.commands.rollback import Rollback
|
|
17
21
|
from fujin.commands.server import Server
|
|
18
22
|
from fujin.commands.up import Up
|
|
23
|
+
from fujin.commands.printenv import Printenv
|
|
19
24
|
|
|
20
25
|
if sys.version_info >= (3, 11):
|
|
21
26
|
import tomllib
|
|
@@ -38,6 +43,7 @@ class Fujin:
|
|
|
38
43
|
| Down
|
|
39
44
|
| Rollback
|
|
40
45
|
| Prune
|
|
46
|
+
| Printenv
|
|
41
47
|
]
|
|
42
48
|
|
|
43
49
|
|
|
@@ -7,6 +7,7 @@ import cappa
|
|
|
7
7
|
|
|
8
8
|
from fujin.commands import BaseCommand
|
|
9
9
|
from fujin.config import InstallationMode
|
|
10
|
+
from fujin.secrets import resolve_secrets
|
|
10
11
|
from fujin.connection import Connection
|
|
11
12
|
|
|
12
13
|
|
|
@@ -15,14 +16,16 @@ from fujin.connection import Connection
|
|
|
15
16
|
)
|
|
16
17
|
class Deploy(BaseCommand):
|
|
17
18
|
def __call__(self):
|
|
19
|
+
parsed_env = self.parse_envfile()
|
|
18
20
|
self.build_app()
|
|
19
21
|
|
|
20
22
|
with self.connection() as conn:
|
|
21
23
|
process_manager = self.create_process_manager(conn)
|
|
22
24
|
conn.run(f"mkdir -p {self.app_dir}")
|
|
25
|
+
conn.run(f"mkdir -p {self.versioned_assets_dir}")
|
|
23
26
|
with conn.cd(self.app_dir):
|
|
24
27
|
self.create_hook_manager(conn).pre_deploy()
|
|
25
|
-
self.transfer_files(conn)
|
|
28
|
+
self.transfer_files(conn, env=parsed_env)
|
|
26
29
|
self.install_project(conn)
|
|
27
30
|
with self.app_environment() as app_conn:
|
|
28
31
|
self.release(app_conn)
|
|
@@ -48,16 +51,31 @@ class Deploy(BaseCommand):
|
|
|
48
51
|
def versioned_assets_dir(self) -> str:
|
|
49
52
|
return f"{self.app_dir}/v{self.config.version}"
|
|
50
53
|
|
|
51
|
-
def
|
|
54
|
+
def parse_envfile(self) -> str:
|
|
52
55
|
if not self.config.host.envfile.exists():
|
|
53
56
|
raise cappa.Exit(f"{self.config.host.envfile} not found", code=1)
|
|
54
|
-
|
|
55
|
-
|
|
57
|
+
if self.config.secret_config:
|
|
58
|
+
self.stdout.output("[blue]Reading secrets....[/blue]")
|
|
59
|
+
return resolve_secrets(self.config.host.envfile, self.config.secret_config)
|
|
60
|
+
return self.config.host.envfile.read_text()
|
|
61
|
+
|
|
62
|
+
def transfer_files(
|
|
63
|
+
self, conn: Connection, env: str, skip_requirements: bool = False
|
|
64
|
+
):
|
|
65
|
+
conn.run(f"echo '{env}' > {self.app_dir}/.env")
|
|
56
66
|
distfile_path = self.config.get_distfile_path()
|
|
57
67
|
conn.put(
|
|
58
68
|
str(distfile_path),
|
|
59
69
|
f"{self.versioned_assets_dir}/{distfile_path.name}",
|
|
60
70
|
)
|
|
71
|
+
if not skip_requirements and self.config.requirements:
|
|
72
|
+
requirements = Path(self.config.requirements)
|
|
73
|
+
if not requirements.exists():
|
|
74
|
+
raise cappa.Exit(f"{self.config.requirements} not found", code=1)
|
|
75
|
+
conn.put(
|
|
76
|
+
Path(self.config.requirements).resolve(),
|
|
77
|
+
f"{self.versioned_assets_dir}/requirements.txt",
|
|
78
|
+
)
|
|
61
79
|
|
|
62
80
|
def install_project(
|
|
63
81
|
self, conn: Connection, version: str | None = None, *, skip_setup: bool = False
|
|
@@ -71,14 +89,6 @@ class Deploy(BaseCommand):
|
|
|
71
89
|
def _install_python_package(
|
|
72
90
|
self, conn: Connection, version: str, skip_setup: bool = False
|
|
73
91
|
):
|
|
74
|
-
if not skip_setup and self.config.requirements:
|
|
75
|
-
requirements = Path(self.config.requirements)
|
|
76
|
-
if not requirements.exists():
|
|
77
|
-
raise cappa.Exit(f"{self.config.requirements} not found", code=1)
|
|
78
|
-
conn.put(
|
|
79
|
-
Path(self.config.requirements).resolve(),
|
|
80
|
-
f"{self.versioned_assets_dir}/requirements.txt",
|
|
81
|
-
)
|
|
82
92
|
appenv = f"""
|
|
83
93
|
set -a # Automatically export all variables
|
|
84
94
|
source .env
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import cappa
|
|
2
|
+
|
|
3
|
+
from fujin.commands import BaseCommand
|
|
4
|
+
from fujin.secrets import resolve_secrets
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@cappa.command(
|
|
8
|
+
help="Print the content of the envfile with extracted secrets (for debugging)"
|
|
9
|
+
)
|
|
10
|
+
class Printenv(BaseCommand):
|
|
11
|
+
def __call__(self):
|
|
12
|
+
if self.config.secret_config:
|
|
13
|
+
result = resolve_secrets(self.config.host.envfile, self.config.secret_config)
|
|
14
|
+
else:
|
|
15
|
+
result = self.config.host.envfile.read_text()
|
|
16
|
+
self.stdout.output(result)
|
|
@@ -15,13 +15,17 @@ from fujin.connection import Connection
|
|
|
15
15
|
class Redeploy(BaseCommand):
|
|
16
16
|
def __call__(self):
|
|
17
17
|
deploy = Deploy()
|
|
18
|
+
parsed_env = deploy.parse_envfile()
|
|
18
19
|
deploy.build_app()
|
|
19
20
|
|
|
20
21
|
with self.app_environment() as conn:
|
|
21
22
|
hook_manager = self.create_hook_manager(conn)
|
|
22
23
|
hook_manager.pre_deploy()
|
|
23
|
-
deploy.
|
|
24
|
+
conn.run(f"mkdir -p {deploy.versioned_assets_dir}")
|
|
24
25
|
requirements_copied = self._copy_requirements_if_needed(conn)
|
|
26
|
+
deploy.transfer_files(
|
|
27
|
+
conn, env=parsed_env, skip_requirements=requirements_copied
|
|
28
|
+
)
|
|
25
29
|
deploy.install_project(conn, skip_setup=requirements_copied)
|
|
26
30
|
deploy.release(conn)
|
|
27
31
|
self.create_process_manager(conn).restart_services()
|