fujin-cli 0.1.0__py3-none-any.whl → 0.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of fujin-cli might be problematic. Click here for more details.
- fujin/__init__.py +0 -2
- fujin/__main__.py +71 -0
- fujin/commands/__init__.py +2 -0
- fujin/commands/_base.py +76 -0
- fujin/commands/app.py +138 -0
- fujin/commands/config.py +65 -0
- fujin/commands/deploy.py +116 -0
- fujin/commands/docs.py +16 -0
- fujin/commands/down.py +48 -0
- fujin/commands/init.py +82 -0
- fujin/commands/proxy.py +71 -0
- fujin/commands/prune.py +42 -0
- fujin/commands/redeploy.py +48 -0
- fujin/commands/rollback.py +49 -0
- fujin/commands/secrets.py +11 -0
- fujin/commands/server.py +92 -0
- fujin/commands/up.py +15 -0
- fujin/config.py +290 -0
- fujin/connection.py +75 -0
- fujin/errors.py +5 -0
- fujin/hooks.py +55 -0
- fujin/process_managers/__init__.py +40 -0
- fujin/process_managers/systemd.py +155 -0
- fujin/proxies/__init__.py +35 -0
- fujin/proxies/caddy.py +214 -0
- fujin/proxies/dummy.py +29 -0
- fujin/proxies/nginx.py +132 -0
- fujin/templates/simple.service +14 -0
- fujin/templates/web.service +25 -0
- fujin/templates/web.socket +11 -0
- fujin_cli-0.3.0.dist-info/METADATA +66 -0
- fujin_cli-0.3.0.dist-info/RECORD +35 -0
- fujin_cli-0.3.0.dist-info/entry_points.txt +2 -0
- fujin/fabfile.py +0 -202
- fujin/utils.py +0 -60
- fujin_cli-0.1.0.dist-info/METADATA +0 -41
- fujin_cli-0.1.0.dist-info/RECORD +0 -8
- fujin_cli-0.1.0.dist-info/entry_points.txt +0 -2
- {fujin_cli-0.1.0.dist-info → fujin_cli-0.3.0.dist-info}/WHEEL +0 -0
- {fujin_cli-0.1.0.dist-info → fujin_cli-0.3.0.dist-info}/licenses/LICENSE.txt +0 -0
|
@@ -0,0 +1,25 @@
|
|
|
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
|
+
Requires={app_name}.socket
|
|
6
|
+
After=network.target
|
|
7
|
+
|
|
8
|
+
[Service]
|
|
9
|
+
#Type=notify
|
|
10
|
+
#NotifyAccess=main
|
|
11
|
+
User={user}
|
|
12
|
+
Group={user}
|
|
13
|
+
RuntimeDirectory={app_name}
|
|
14
|
+
WorkingDirectory={app_dir}
|
|
15
|
+
ExecStart={app_dir}/{command}
|
|
16
|
+
EnvironmentFile={app_dir}/.env
|
|
17
|
+
ExecReload=/bin/kill -s HUP $MAINPID
|
|
18
|
+
KillMode=mixed
|
|
19
|
+
TimeoutStopSec=5
|
|
20
|
+
PrivateTmp=true
|
|
21
|
+
# if your app does not need administrative capabilities, let systemd know
|
|
22
|
+
ProtectSystem=strict
|
|
23
|
+
|
|
24
|
+
[Install]
|
|
25
|
+
WantedBy=multi-user.target
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: fujin-cli
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: Add your description here
|
|
5
|
+
Project-URL: Documentation, https://github.com/falcopackages/fujin#readme
|
|
6
|
+
Project-URL: Issues, https://github.com/falcopackages/fujin/issues
|
|
7
|
+
Project-URL: Source, https://github.com/falcopackages/fujin
|
|
8
|
+
Author-email: Tobi DEGNON <tobidegnon@proton.me>
|
|
9
|
+
License-File: LICENSE.txt
|
|
10
|
+
Keywords: caddy,deployment,django,fastapi,litestar,python,systemd
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Natural Language :: English
|
|
14
|
+
Classifier: Programming Language :: Python
|
|
15
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
21
|
+
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Requires-Dist: cappa>=0.24
|
|
24
|
+
Requires-Dist: fabric>=3.2.2
|
|
25
|
+
Requires-Dist: msgspec[toml]>=0.18.6
|
|
26
|
+
Requires-Dist: rich>=13.9.2
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
|
|
29
|
+
# fujin
|
|
30
|
+
|
|
31
|
+
[](https://pypi.org/project/fujin-cli)
|
|
32
|
+
[](https://pypi.org/project/fujin-cli)
|
|
33
|
+
[](https://github.com/falcopackages/fujin/blob/main/LICENSE.txt)
|
|
34
|
+
[](https://pypi.org/project/fujin-cli)
|
|
35
|
+
-----
|
|
36
|
+
|
|
37
|
+
> [!IMPORTANT]
|
|
38
|
+
> This package currently contains minimal features and is a work-in-progress
|
|
39
|
+
|
|
40
|
+
`fujin` is a simple deployment tool that helps you get your project up and running on a VPS in a few minutes. It manages your app processes using `systemd` and runs your apps behind [caddy](https://caddyserver.com/). For Python projects,
|
|
41
|
+
it expects your app to be a packaged Python application ideally with a CLI entry point defined. For other languages, you need to provide a self-contained single executable file with all necessary dependencies.
|
|
42
|
+
The main job of `fujin` is to bootstrap your server (installing caddy, etc.), copy the files onto the server with a structure that supports rollback, and automatically generate configs for systemd and caddy that you can manually edit if needed.
|
|
43
|
+
|
|
44
|
+
Check out the [documentation📚](https://fujin.readthedocs.io/en/latest/) for installation, features, and usage guides.
|
|
45
|
+
|
|
46
|
+
## Why?
|
|
47
|
+
|
|
48
|
+
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
|
|
49
|
+
trying to get your app in production, you probably went through this. I'm using caddy instead of nginx because the configuration file simpler and it's is a no-brainer for SSL certificates. Systemd is the default on most Linux distributions and does a good enough job.
|
|
50
|
+
|
|
51
|
+
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). I wanted to recreate kamal's nice local-to-remote app management API, but I'm skipping Docker to keep things simple.
|
|
52
|
+
I'm currently rocking SQLite in production for my side projects and ths setup is enough for my use case.
|
|
53
|
+
|
|
54
|
+
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.
|
|
55
|
+
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.
|
|
56
|
+
|
|
57
|
+
## Inspiration and alternatives
|
|
58
|
+
|
|
59
|
+
Fujin draws inspiration from the following tools for their developer experience. These are better alternatives if you need a more robust, set-and-forget solution
|
|
60
|
+
|
|
61
|
+
- [fly.io](https://fly.io/)
|
|
62
|
+
- [kamal](https://kamal-deploy.org/) (you probably can't just forget this one)
|
|
63
|
+
|
|
64
|
+
## License
|
|
65
|
+
|
|
66
|
+
`fujin` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
fujin/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
fujin/__main__.py,sha256=St0VnEWhRRw_ukAddAwDGFliLqQT3xlone-9JIONlDI,1702
|
|
3
|
+
fujin/config.py,sha256=OYcQPhYYcLJ2ImVP-BNodWvaJ6Y5mRIeB9R3cohbV94,8678
|
|
4
|
+
fujin/connection.py,sha256=ZkYaNykRFj9Yr-K-vOrZtVVGUDurDm6W7OQrgct71CA,2428
|
|
5
|
+
fujin/errors.py,sha256=74Rh-Sgql1YspPdR_akQ2G3xZ48zecyafYCptpaFo1A,73
|
|
6
|
+
fujin/hooks.py,sha256=QHIqxLxujG2U70UkN1BpUplE6tTqn7pFJP5oHde1tUQ,1350
|
|
7
|
+
fujin/commands/__init__.py,sha256=uIGGXt8YofL5RZn8KIy153ioWGoCl32ffHtqOhB-6ZM,78
|
|
8
|
+
fujin/commands/_base.py,sha256=o3R4-c3XeFWTIW3stiUdrcCPwdjzfjUVIpZy2L1-gZ4,2525
|
|
9
|
+
fujin/commands/app.py,sha256=LUC4mmvkp1F4a96UP9hgd5y_R9HA8Xc_TtNAsYtXvvs,5153
|
|
10
|
+
fujin/commands/config.py,sha256=HAiea_ebhvBzUG7vddMH3ldjZKRHU1KFaZlDZI1XmMg,2322
|
|
11
|
+
fujin/commands/deploy.py,sha256=mUvlDF6yv05wnQqhsFC-LCPVvaJVfnQnK97CFB_ttVY,4427
|
|
12
|
+
fujin/commands/docs.py,sha256=b5FZ8AgoAfn4q4BueEQvM2w5HCuh8-rwBqv_CRFVU8E,349
|
|
13
|
+
fujin/commands/down.py,sha256=JrwDZjcqyx5Mx7IzcAkzq1zAPBD5MQMIS4gzBEUz3JM,1721
|
|
14
|
+
fujin/commands/init.py,sha256=nkIRCmk5F1jfoBlPI1FSnKZJ_jn4zroBZMtbqmelXtI,2887
|
|
15
|
+
fujin/commands/proxy.py,sha256=w8XOhirD5lLnmW3SC27bahoIt2XtgydQ-8LxOP-DpYM,2552
|
|
16
|
+
fujin/commands/prune.py,sha256=C2aAN6AUS84jgRg1eiCroyiuZyaZDmf5yvGAQY9xkcg,1517
|
|
17
|
+
fujin/commands/redeploy.py,sha256=suSr6S54oLsnKGjJgq3uHxCRFcrCX5C-5BkmH5MzQks,1864
|
|
18
|
+
fujin/commands/rollback.py,sha256=BN9vOTEBcSSpFIfck9nzWvMVO7asVC20lQbcNrxRchg,2009
|
|
19
|
+
fujin/commands/secrets.py,sha256=1xZQVkvbopsAcWUocLstxPKxsvmGoE2jWip5hdTrP50,162
|
|
20
|
+
fujin/commands/server.py,sha256=n0FZbNI4IO5AOMJTIVfJqgb32-QSrWEu0IVwfREsRbY,3637
|
|
21
|
+
fujin/commands/up.py,sha256=DgDN-1mc_mMHJRCIvcB947Cd5a7phunu9NpXloGK0UU,419
|
|
22
|
+
fujin/process_managers/__init__.py,sha256=MhhfTBhm64zWRAKgjvsZRIToOUJus60vGScbAjqpQ6Y,994
|
|
23
|
+
fujin/process_managers/systemd.py,sha256=qG_4Ew8SEWtaTFOAW_XZXsMO2WjFWZ4dp5nBwAPBObk,5603
|
|
24
|
+
fujin/proxies/__init__.py,sha256=UuWYU175tkdaz1WWRCDDpQgGfFVYYNR9PBxA3lTCNr0,695
|
|
25
|
+
fujin/proxies/caddy.py,sha256=9pbpNatiNcTHjmflyz1KijagGYQkpAafc_IV06wxY8w,7130
|
|
26
|
+
fujin/proxies/dummy.py,sha256=qBKSn8XNEA9SVwB7GzRNX2l9Iw6tUjo2CFqZjWi0FjY,465
|
|
27
|
+
fujin/proxies/nginx.py,sha256=8AkbJAjj6B0fxgv671mGDbx3LY_dY5wxFov80XmSfUY,4139
|
|
28
|
+
fujin/templates/simple.service,sha256=-lyKjmSyfHGucP4O_vRQE1NNaHq0Qjsc0twdwoRLgI0,321
|
|
29
|
+
fujin/templates/web.service,sha256=NZ7ZeaFvV_MZTBn8QqRQeu8PIrWHf3aWYWNzjOQeqCw,685
|
|
30
|
+
fujin/templates/web.socket,sha256=2lJsiOHlMJL0YlN7YBLLnr5zqsytPEt81yP34nk0dmc,173
|
|
31
|
+
fujin_cli-0.3.0.dist-info/METADATA,sha256=_RdZ9gqiLRxAuxGp-Y4qPelvKI7Opus5VVsKJXoFUK8,4396
|
|
32
|
+
fujin_cli-0.3.0.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
|
|
33
|
+
fujin_cli-0.3.0.dist-info/entry_points.txt,sha256=Y_TBtKt3j11qhwquMexZR5yqnDEqOBDACtresqQFE-s,46
|
|
34
|
+
fujin_cli-0.3.0.dist-info/licenses/LICENSE.txt,sha256=0QF8XfuH0zkIHhSet6teXfiCze6JSdr8inRkmLLTDyo,1099
|
|
35
|
+
fujin_cli-0.3.0.dist-info/RECORD,,
|
fujin/fabfile.py
DELETED
|
@@ -1,202 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import secrets
|
|
3
|
-
|
|
4
|
-
from fabric import Connection
|
|
5
|
-
from fabric import task
|
|
6
|
-
from falco.fabutils import curl_binary_download_cmd
|
|
7
|
-
from falco.fabutils import with_progress
|
|
8
|
-
|
|
9
|
-
# Configuration
|
|
10
|
-
PROJECT_NAME = "torch"
|
|
11
|
-
GITHUB_USERNAME = os.getenv("GITHUB_USERNAME", default="tobi")
|
|
12
|
-
GITHUB_TOKEN = os.getenv("GITHUB_TOKEN")
|
|
13
|
-
SERVER_USER = "tobi"
|
|
14
|
-
SERVER_HOST = os.getenv("SERVER_HOST")
|
|
15
|
-
SERVER_PASSWORD = os.getenv("SERVER_PASSWORD")
|
|
16
|
-
SERVER_PROJECT_DIR = f"/home/{SERVER_USER}/djangoprojects/{PROJECT_NAME}"
|
|
17
|
-
SERVER_PROJECT_BINARY = f"{SERVER_PROJECT_DIR}/{PROJECT_NAME}"
|
|
18
|
-
DOMAIN = "torch.com"
|
|
19
|
-
CERTBOT_EMAIL = "tobidegnon@proton.me"
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
def get_connection(use_root=False):
|
|
23
|
-
"""Establish a connection to the server."""
|
|
24
|
-
user = "root" if use_root else SERVER_USER
|
|
25
|
-
return Connection(host=SERVER_HOST, user=user, connect_kwargs={"password": SERVER_PASSWORD})
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
# Deployment Tasks
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
@task
|
|
32
|
-
@with_progress("Provisioning server")
|
|
33
|
-
def provision(_, set_password=False, progress=None):
|
|
34
|
-
"""Set up the server with necessary dependencies and create a new user."""
|
|
35
|
-
conn = get_connection(use_root=True)
|
|
36
|
-
|
|
37
|
-
progress("Updating system and installing dependencies")
|
|
38
|
-
conn.sudo("apt update && apt upgrade -y")
|
|
39
|
-
conn.sudo("apt install -y nginx libpq-dev python3-dev python3-certbot-nginx sqlite3")
|
|
40
|
-
|
|
41
|
-
progress(f"Creating and configuring user: {SERVER_USER}")
|
|
42
|
-
conn.sudo(f"adduser --disabled-password --gecos '' {SERVER_USER}")
|
|
43
|
-
if set_password:
|
|
44
|
-
password = secrets.token_urlsafe(16)
|
|
45
|
-
conn.sudo(f"echo '{SERVER_USER}:{password}' | chpasswd")
|
|
46
|
-
print(f"Generated password for {SERVER_USER}: {password}")
|
|
47
|
-
print("Please store this password securely and change it upon first login.")
|
|
48
|
-
|
|
49
|
-
# Set up SSH and sudo access
|
|
50
|
-
conn.sudo(f"mkdir -p /home/{SERVER_USER}/.ssh")
|
|
51
|
-
conn.sudo(f"cp ~/.ssh/authorized_keys /home/{SERVER_USER}/.ssh/")
|
|
52
|
-
conn.sudo(f"chown -R {SERVER_USER}:{SERVER_USER} /home/{SERVER_USER}/.ssh")
|
|
53
|
-
conn.sudo(f"chmod 700 /home/{SERVER_USER}/.ssh")
|
|
54
|
-
conn.sudo(f"chmod 600 /home/{SERVER_USER}/.ssh/authorized_keys")
|
|
55
|
-
conn.sudo(f"echo '{SERVER_USER} ALL=(ALL) NOPASSWD:ALL' | sudo tee -a /etc/sudoers")
|
|
56
|
-
|
|
57
|
-
progress("Creating project directory")
|
|
58
|
-
conn.run(f"mkdir -p {SERVER_PROJECT_DIR}")
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
@task
|
|
62
|
-
@with_progress("Performing initial deployment")
|
|
63
|
-
def up(c, progress=None):
|
|
64
|
-
"""Perform the initial deployment of the project."""
|
|
65
|
-
conn = get_connection()
|
|
66
|
-
|
|
67
|
-
progress("Transferring files to server")
|
|
68
|
-
transfer_files(c)
|
|
69
|
-
conn.sudo(f"ln -sf /etc/nginx/sites-available/{PROJECT_NAME} /etc/nginx/sites-enabled/{PROJECT_NAME}")
|
|
70
|
-
|
|
71
|
-
progress("Setting up project and services")
|
|
72
|
-
apprun(c, "setup")
|
|
73
|
-
reload_services(c)
|
|
74
|
-
conn.sudo(f"systemctl enable --now {PROJECT_NAME}.socket")
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
@task
|
|
78
|
-
@with_progress("Deploying code changes")
|
|
79
|
-
def deploy(c, progress=None):
|
|
80
|
-
"""Deploy code changes to the server."""
|
|
81
|
-
progress("Transferring updated files")
|
|
82
|
-
transfer_files(c, code_only=True)
|
|
83
|
-
|
|
84
|
-
progress("Updating project setup")
|
|
85
|
-
apprun(c, "setup")
|
|
86
|
-
|
|
87
|
-
progress("Restarting wsgi service")
|
|
88
|
-
get_connection().sudo(f"systemctl restart {PROJECT_NAME}")
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
# Maintenance Tasks
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
@task
|
|
95
|
-
@with_progress("Refreshing application")
|
|
96
|
-
def refresh(c, progress=None):
|
|
97
|
-
"""Refresh the application by reloading configurations and restarting services."""
|
|
98
|
-
progress("Updating all files")
|
|
99
|
-
transfer_files(c)
|
|
100
|
-
|
|
101
|
-
progress("Reloading configurations and restarting services")
|
|
102
|
-
reload_services(c)
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
@task
|
|
106
|
-
@with_progress("Setting up domain with SSL")
|
|
107
|
-
def setup_domain(_, progress=None):
|
|
108
|
-
"""Set up a domain with SSL using Certbot."""
|
|
109
|
-
conn = get_connection()
|
|
110
|
-
|
|
111
|
-
progress("Obtaining SSL certificate")
|
|
112
|
-
conn.sudo(f"certbot --nginx -d {DOMAIN} --non-interactive --agree-tos --email {CERTBOT_EMAIL} --redirect")
|
|
113
|
-
|
|
114
|
-
progress("Updating local Nginx configuration")
|
|
115
|
-
conn.get(
|
|
116
|
-
f"/etc/nginx/sites-available/{PROJECT_NAME}",
|
|
117
|
-
f"deploy/etc/nginx/sites-available/{PROJECT_NAME}",
|
|
118
|
-
)
|
|
119
|
-
|
|
120
|
-
progress("Enabling certificate auto-renewal")
|
|
121
|
-
conn.sudo("systemctl enable certbot.timer")
|
|
122
|
-
conn.sudo("systemctl start certbot.timer")
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
# Utility Tasks
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
@task
|
|
129
|
-
def transfer_files(_, code_only=False):
|
|
130
|
-
"""Transfer project files to the server."""
|
|
131
|
-
conn = get_connection()
|
|
132
|
-
|
|
133
|
-
# Download and set up binary
|
|
134
|
-
binary_download_cmd = curl_binary_download_cmd(owner=GITHUB_USERNAME, repo=PROJECT_NAME, token=GITHUB_TOKEN)
|
|
135
|
-
conn.run(f"{binary_download_cmd} -o {SERVER_PROJECT_BINARY}")
|
|
136
|
-
conn.run(f"chmod +x {SERVER_PROJECT_BINARY}")
|
|
137
|
-
conn.put(".env.prod", f"{SERVER_PROJECT_DIR}/.env")
|
|
138
|
-
|
|
139
|
-
if not code_only:
|
|
140
|
-
# Transfer configuration files
|
|
141
|
-
config_files = [
|
|
142
|
-
(
|
|
143
|
-
f"deploy/etc/nginx/sites-available/{PROJECT_NAME}",
|
|
144
|
-
f"/etc/nginx/sites-available/{PROJECT_NAME}",
|
|
145
|
-
),
|
|
146
|
-
(
|
|
147
|
-
f"deploy/etc/systemd/system/{PROJECT_NAME}.socket",
|
|
148
|
-
f"/etc/systemd/system/{PROJECT_NAME}.socket",
|
|
149
|
-
),
|
|
150
|
-
(
|
|
151
|
-
f"deploy/etc/systemd/system/{PROJECT_NAME}.service",
|
|
152
|
-
f"/etc/systemd/system/{PROJECT_NAME}.service",
|
|
153
|
-
),
|
|
154
|
-
]
|
|
155
|
-
for src, dest in config_files:
|
|
156
|
-
filename = os.path.basename(src)
|
|
157
|
-
conn.put(src, f"{SERVER_PROJECT_DIR}/{filename}")
|
|
158
|
-
conn.sudo(f"mv {SERVER_PROJECT_DIR}/{filename} {dest}")
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
@task
|
|
162
|
-
def apprun(_, cmd, pty=False):
|
|
163
|
-
"""Run a command in the application environment."""
|
|
164
|
-
with get_connection().cd(SERVER_PROJECT_DIR):
|
|
165
|
-
get_connection().run(f"source .env && ./{PROJECT_NAME} '{cmd}'", pty=pty)
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
@task
|
|
169
|
-
def console(c, shell_type="bash"):
|
|
170
|
-
"""Open an interactive shell on the server."""
|
|
171
|
-
conn = get_connection()
|
|
172
|
-
if shell_type == "python":
|
|
173
|
-
apprun(c, "manage shell_plus", pty=True)
|
|
174
|
-
elif shell_type == "db":
|
|
175
|
-
apprun(c, "manage dbshell", pty=True)
|
|
176
|
-
else:
|
|
177
|
-
conn.run("bash", pty=True)
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
@task
|
|
181
|
-
def reload_services(_):
|
|
182
|
-
"""Reload Nginx and systemd configurations, and restart the project service."""
|
|
183
|
-
conn = get_connection()
|
|
184
|
-
conn.sudo("systemctl daemon-reload")
|
|
185
|
-
conn.sudo("systemctl reload nginx")
|
|
186
|
-
conn.sudo(f"systemctl restart {PROJECT_NAME}")
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
@task
|
|
190
|
-
def logs(_, follow=False):
|
|
191
|
-
"""View the last n lines of the project's log file."""
|
|
192
|
-
get_connection().sudo(f"sudo journalctl -u {PROJECT_NAME} -r {'-f' if follow else ''}")
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
if __name__ == "__main__":
|
|
196
|
-
from fabric.main import Program, Collection
|
|
197
|
-
|
|
198
|
-
ns = Collection(
|
|
199
|
-
provision, up, deploy, refresh, setup_domain, transfer_files, apprun, console, reload_services, logs
|
|
200
|
-
)
|
|
201
|
-
program = Program(namespace=ns)
|
|
202
|
-
program.run()
|
fujin/utils.py
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import json
|
|
2
|
-
import urllib.request
|
|
3
|
-
from functools import wraps
|
|
4
|
-
|
|
5
|
-
from rich.progress import Progress
|
|
6
|
-
from rich.progress import SpinnerColumn
|
|
7
|
-
from rich.progress import TextColumn
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
def with_progress(description, pass_progress=True):
|
|
11
|
-
def decorator(func):
|
|
12
|
-
@wraps(func)
|
|
13
|
-
def wrapper(*args, **kwargs):
|
|
14
|
-
with Progress(
|
|
15
|
-
SpinnerColumn(),
|
|
16
|
-
TextColumn("[cyan]{task.description}"),
|
|
17
|
-
transient=True,
|
|
18
|
-
) as progress:
|
|
19
|
-
task = progress.add_task(description, total=None)
|
|
20
|
-
if pass_progress:
|
|
21
|
-
kwargs["progress"] = lambda desc: progress.update(task, description=desc)
|
|
22
|
-
result = func(*args, **kwargs)
|
|
23
|
-
progress.update(task, completed=True)
|
|
24
|
-
progress.console.print(f"[green]{description} completed!")
|
|
25
|
-
return result
|
|
26
|
-
|
|
27
|
-
return wrapper
|
|
28
|
-
|
|
29
|
-
return decorator
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
def _get_asset_id(token, owner, repo, project_name):
|
|
33
|
-
url = f"https://api.github.com/repos/{owner}/{repo}/releases/latest"
|
|
34
|
-
headers = {
|
|
35
|
-
"Accept": "application/vnd.github+json",
|
|
36
|
-
"Authorization": f"Bearer {token}",
|
|
37
|
-
"X-GitHub-Api-Version": "2022-11-28",
|
|
38
|
-
}
|
|
39
|
-
req = urllib.request.Request(url, headers=headers)
|
|
40
|
-
with urllib.request.urlopen(req) as response:
|
|
41
|
-
data = json.loads(response.read().decode())
|
|
42
|
-
assets = data["assets"]
|
|
43
|
-
for asset in assets:
|
|
44
|
-
if asset["name"] == f"{project_name}-x86_64-linux":
|
|
45
|
-
return asset["id"]
|
|
46
|
-
msg = f"Asset not found with name {project_name}-x86_64-linux"
|
|
47
|
-
raise Exception(msg)
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
def curl_binary_download_cmd(*, owner, repo, project_name=None, token=None):
|
|
51
|
-
curl = "curl -L -H 'Accept: application/octet-stream' "
|
|
52
|
-
project_name = project_name or repo
|
|
53
|
-
if not token:
|
|
54
|
-
return f"{curl} https://github.com/{owner}/{repo}/releases/latest/download/{project_name}-x86_64-linux"
|
|
55
|
-
asset_id = _get_asset_id(token, owner, repo, project_name)
|
|
56
|
-
return (
|
|
57
|
-
f"{curl} -H 'Authorization: Bearer {token}' "
|
|
58
|
-
f"-H 'X-GitHub-Api-Version: 2022-11-28' "
|
|
59
|
-
f"https://api.github.com/repos/{owner}/{repo}/releases/assets/{asset_id}"
|
|
60
|
-
)
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.3
|
|
2
|
-
Name: fujin-cli
|
|
3
|
-
Version: 0.1.0
|
|
4
|
-
Summary: Add your description here
|
|
5
|
-
Project-URL: Documentation, https://github.com/falcopackages/fujin#readme
|
|
6
|
-
Project-URL: Issues, https://github.com/falcopackages/fujin/issues
|
|
7
|
-
Project-URL: Source, https://github.com/falcopackages/fujin
|
|
8
|
-
Author-email: Tobi DEGNON <tobidegnon@proton.me>
|
|
9
|
-
License-File: LICENSE.txt
|
|
10
|
-
Requires-Python: >=3.10
|
|
11
|
-
Requires-Dist: fabric
|
|
12
|
-
Requires-Dist: rich
|
|
13
|
-
Requires-Dist: tomlkit
|
|
14
|
-
Description-Content-Type: text/markdown
|
|
15
|
-
|
|
16
|
-
# fujin
|
|
17
|
-
|
|
18
|
-
[](https://pypi.org/project/fujin)
|
|
19
|
-
[](https://pypi.org/project/fujin)
|
|
20
|
-
|
|
21
|
-
-----
|
|
22
|
-
|
|
23
|
-
> [!IMPORTANT]
|
|
24
|
-
> This package currently contains no features and is a work-in-progress
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
**Table of Contents**
|
|
28
|
-
|
|
29
|
-
- [fujin](#fujin)
|
|
30
|
-
- [Installation](#installation)
|
|
31
|
-
- [License](#license)
|
|
32
|
-
|
|
33
|
-
## Installation
|
|
34
|
-
|
|
35
|
-
```console
|
|
36
|
-
pip install fujin
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
## License
|
|
40
|
-
|
|
41
|
-
`fujin` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license.
|
fujin_cli-0.1.0.dist-info/RECORD
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
fujin/__init__.py,sha256=ShHd5zQuVmEI29BKLgfu3WOGbCJBzeirwGy0pRl5Xag,51
|
|
2
|
-
fujin/fabfile.py,sha256=pIz4uQ9n1zWmiMmD6_1OJKk1PrKi6tlNgE4w4_wvEng,6633
|
|
3
|
-
fujin/utils.py,sha256=lVAIsnjDtpLBSOTQKt5wJlvdccBznSrch0FsNBp4vO8,2197
|
|
4
|
-
fujin_cli-0.1.0.dist-info/METADATA,sha256=Z-42ZEQq6LyNR1pxLe3qbYfhGFjJozhfDsJZAberYYk,1062
|
|
5
|
-
fujin_cli-0.1.0.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
|
|
6
|
-
fujin_cli-0.1.0.dist-info/entry_points.txt,sha256=KHtzGtsbA7ZhWWVcglfITQvvi9awumQ5hgXO-5iBH3Y,37
|
|
7
|
-
fujin_cli-0.1.0.dist-info/licenses/LICENSE.txt,sha256=0QF8XfuH0zkIHhSet6teXfiCze6JSdr8inRkmLLTDyo,1099
|
|
8
|
-
fujin_cli-0.1.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|