Skip to content

Commit

Permalink
Merge pull request #1 from darrida/dev-click-migration
Browse files Browse the repository at this point in the history
"remove" and "clean-rollback" commands
  • Loading branch information
darrida authored Jul 8, 2024
2 parents c0020fd + 97ec3a8 commit a996166
Show file tree
Hide file tree
Showing 18 changed files with 543 additions and 217 deletions.
Empty file added .gitkeep
Empty file.
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "shinylive-deploy"
version = "2024.7.1"
version = "2024.7.8"
authors = [
{name="darrida", email="darrida.py@gmail.com"}
]
Expand All @@ -27,7 +27,7 @@ tests = [
]

[project.scripts]
shinylive_deploy = "shinylive_deploy.app:main"
shinylive_deploy = "shinylive_deploy.app:cli"

[project.urls]
Homepage = "https://github.com/darrida/py-shinylive-deploy-webserver"
Expand Down
9 changes: 0 additions & 9 deletions src/shinylive_deploy/data.py → shinylive_deploy.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
from pathlib import Path

toml_text = """
[general]
app_name = "app1"

Expand All @@ -24,10 +22,3 @@
port = 2222
directory = "shinyapps"
base_url = "http://localhost:5000"
"""


def create_config_file():
filepath = Path.cwd() / "shinylive_deploy.toml"
with open(filepath, "w") as f:
f.write(toml_text)
99 changes: 36 additions & 63 deletions src/shinylive_deploy/app.py
Original file line number Diff line number Diff line change
@@ -1,63 +1,36 @@
import sys
from getpass import getpass
from pathlib import Path

from pydantic import SecretStr
from shinylive_deploy.models import LocalShinyDeploy, ServerShinyDeploy, toml


def main():
config_file = Path.cwd() / "shinylive_deploy.toml"
if not config_file.exists():
from shinylive_deploy.data import create_config_file
create_config_file()
return

mode, rollback = _parse_arguments()
shinylive_ = _initialize_configuration(mode)

if rollback is True:
shinylive_.rollback()
else:
shinylive_.deploy()


def _parse_arguments(test_argvs: list = None) -> tuple[str, bool]:
if test_argvs:
sys.argv = test_argvs
if len(sys.argv) < 2 or sys.argv[1] not in ("test", "beta", "prod", "local"):
raise ValueError("\nERROR: One of the following arguments is required -> [ local | test | beta | prod ]\n")
deploy_mode = sys.argv[1]
try:
rollback = sys.argv[2]
if rollback not in ("-r", "--rollback"):
raise ValueError("2nd optional argument must be `-r` or `--rollback`")
rollback = True
except IndexError:
rollback = False
return deploy_mode, rollback


def _initialize_configuration(deploy_mode: str) -> LocalShinyDeploy | ServerShinyDeploy:
if deploy_mode in ("test", "beta", "prod"):
config = toml["deploy"]["server"]
return ServerShinyDeploy(
mode=deploy_mode,
base_url=config["base_url"],
dir_deployment=config["directory"],
host=config["host"],
user=config["user"],
port=config.get("port", 22),
password=SecretStr(value=getpass(f"SSH password for [{config["user"]}]: "))
)
else: # local
config = toml["deploy"]["local"]
return LocalShinyDeploy(
mode=deploy_mode,
base_url=config["base_url"],
dir_deployment=config["directory"]
)


if __name__ == "__main__":
main()
import click

from .process import initialize


@click.group()
def cli():
...


@cli.command()
@click.argument("deploy_mode")
def deploy(deploy_mode: str):
shinylive_ = initialize(deploy_mode)
shinylive_.deploy()


@cli.command()
@click.argument("deploy_mode")
def rollback(deploy_mode: str):
shinylive_ = initialize(deploy_mode)
shinylive_.rollback()


@cli.command()
@click.argument("deploy_mode")
def clean_rollback(deploy_mode: str):
shinylive_ = initialize(deploy_mode)
shinylive_.clean_rollback()


@cli.command()
@click.argument("deploy_mode")
def remove(deploy_mode: str):
shinylive_ = initialize(deploy_mode)
shinylive_.remove()
64 changes: 64 additions & 0 deletions src/shinylive_deploy/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import os
from pathlib import Path

import tomllib

CONFIG_FILEPATH = os.environ.get("SHINYLIVE_DEPLOY_CONFIG", Path.cwd() / "shinylive_deploy.toml")


toml_text = """
[general]
app_name = "app1"
[development]
directory = "src"
[deploy.gitbranch]
prod = "main"
beta = "main"
[deploy.staging]
directory = "staging"
[deploy.local]
directory = "src_test_webserver/shinyapps/"
base_url = "http://localhost:8000/apps"
[deploy.server]
host = "127.0.0.1"
user = "shinylive"
port = 2222
directory = "shinyapps"
base_url = "http://localhost:5000"
"""


def create_config():
if not CONFIG_FILEPATH.exists():
with open(CONFIG_FILEPATH, "w") as f:
f.write(toml_text)


def read_config() -> dict:
if not CONFIG_FILEPATH.exists():
create_config()
print(f"\n>>> WARNING <<<: {CONFIG_FILEPATH.name} did not yet exist. Default config file created. Please update, then run deploy again.\n")
exit()
with open(CONFIG_FILEPATH, "rb") as f:
return tomllib.load(f)


# if not CONFIG_FILEPATH.exists():
# create_config()
toml = read_config()


class Config:
app_name = toml["general"]["app_name"]
deploy_local = toml["deploy"]["local"]
deploy_server = toml["deploy"]["server"]
development: dict = toml.get("development", {})
staging: dict = toml["deploy"].get("staging", {})
gitbranch: dict = toml["deploy"].get("gitbranch", {})

config = Config()
5 changes: 0 additions & 5 deletions src/shinylive_deploy/models/__init__.py

This file was deleted.

35 changes: 35 additions & 0 deletions src/shinylive_deploy/process/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from getpass import getpass

from pydantic import SecretStr
from shinylive_deploy.config import CONFIG_FILEPATH, create_config
from shinylive_deploy.config import config as loaded_config

from .local import LocalShinyDeploy
from .server import ServerShinyDeploy


def initialize(deploy_mode: str) -> LocalShinyDeploy | ServerShinyDeploy:
if deploy_mode not in ("local", "test", "beta", "prod"):
raise ValueError('`DEPLOY_MODE` must be on of the following: "local", "test", "beta", "prod"')

if not CONFIG_FILEPATH.exists():
create_config()
return
if deploy_mode in ("test", "beta", "prod"):
config = loaded_config.deploy_server
return ServerShinyDeploy(
mode=deploy_mode,
base_url=config["base_url"],
dir_deployment=config["directory"],
host=config["host"],
user=config["user"],
port=config.get("port", 22),
password=SecretStr(value=getpass(f"SSH password for [{config["user"]}]: "))
)
else: # local
config = loaded_config.deploy_local
return LocalShinyDeploy(
mode=deploy_mode,
base_url=config["base_url"],
dir_deployment=config["directory"]
)
Original file line number Diff line number Diff line change
@@ -1,42 +1,29 @@
# ruff: noqa: S602
import subprocess
from dataclasses import dataclass
from pathlib import Path
from typing import Literal

import git
import tomllib

filepath = Path.cwd() / "shinylive_deploy.toml"
if not filepath.exists():
from shinylive_deploy.data import create_config_file
create_config_file()

with open("shinylive_deploy.toml", "rb") as f:
toml = tomllib.load(f)

deploy_local = toml["deploy"]["local"]
deploy_server = toml["deploy"]["server"]
development: dict = toml.get("development", {})
staging: dict = toml["deploy"].get("staging", {})
gitbranch: dict = toml["deploy"].get("gitbranch", {})
from shinylive_deploy.config import config


@dataclass
class ShinyDeploy:
base_url: str = None
app_name: str = toml["general"]["app_name"]
app_name: str = config.app_name
dir_deployment: str = None
dir_development: str = development.get("directory", "src")
dir_staging: str = staging.get("directory", "staging")
prod_branch: str = gitbranch.get("prod", "main")
beta_branch: str = gitbranch.get("beta", "main")
dir_development: str = config.development.get("directory", "src")
dir_staging: str = config.staging.get("directory", "staging")
prod_branch: str = config.gitbranch.get("prod", "main")
beta_branch: str = config.gitbranch.get("beta", "main")
mode: Literal["local", "test", "beta", "prod"] = None

@property
def deploy_name(self):
modes = {"prod": "", "beta": "-beta", "test": "-test", "local": ""}
return self.app_name + modes[self.mode]

def _message(self):
print(
"\n##################################"
Expand All @@ -63,4 +50,4 @@ def _compile(self):


class DeployException(Exception):
pass
pass
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# ruff: noqa: S602 S603
import subprocess
from pathlib import Path

Expand All @@ -24,13 +25,13 @@ def deploy(self):
f"\n- Available at {self.base_url}/{self.deploy_name}"
)
if has_backup:
print(f"\n- Backup available at {self.base_url}/{self.deploy_name}-backup")
print(f"- Backup available at {self.base_url}/{self.deploy_name}-backup")

def rollback(self):
self._check_git_requirements()
existing_deploy_dir = Path(self.dir_deployment) / self.deploy_name
if not self._deployed_dir_exists():
print("\n>>> WARNING <<<: Backback STOPPED. No app directory exists to rollback from.\n")
print("\n>>> WARNING <<<: Backback STOPPED. No app directory exists to rollback to.\n")
return
if not self._backup_dir_exists():
print("\n>>> WARNING <<<: Backback STOPPED. No backup directory exists for rollback.\n")
Expand All @@ -46,6 +47,27 @@ def rollback(self):
f"\n- Available at {self.base_url}/{self.deploy_name}"
)

def clean_rollback(self):
self._check_git_requirements()
existing_deploy_dir = Path(self.dir_deployment) / self.deploy_name
if not self._backup_dir_exists():
print("\n>>> WARNING <<<: Rollback cleanup STOPPED. No backup directory exists to remove.\n")
return
subprocess.run(f"rm -r {existing_deploy_dir}-backup", **subprocess_config)
print(f"\nRemoved `{self.base_url}/{self.deploy_name}-backup`")
print("\nROLLBACK CLEANUP COMPLETE")

def remove(self):
self._check_git_requirements()
self._check_git_requirements()
existing_deploy_dir = Path(self.dir_deployment) / self.deploy_name
if not self._deployed_dir_exists():
print("\n>>> WARNING <<<: App removal STOPPED. No app directory exists to remove.\n")
return
subprocess.run(f"rm -r {existing_deploy_dir}", **subprocess_config)
print(f"\nRemoved `{self.deploy_name}`")
print("\nAPPLICATION REMOVAL COMPLETE")

def _deployed_dir_exists(self):
deploy_dirs = [x.name for x in Path(self.dir_deployment).iterdir()]
# result = subprocess.run(f"ls {self.dir_deployment}", **subprocess_config)
Expand All @@ -66,7 +88,11 @@ def _manage_backup(self):
existing_deploy_dir = Path(self.dir_deployment) / self.deploy_name
if self._deployed_dir_exists():
if self._backup_dir_exists():
print("\n>>> WARNING <<<: Deployment STOPPED. Backup directory already exists. Delete backup directory, or rollback before redeploying.\n")
print(
"\n>>> WARNING <<<: Deployment STOPPED. Backup directory already exists. "
"Delete current backup directory using `shinylive_deploy <mode> --clean-rollback`, "
"or rollback before redeploying using `shinylive_deploy <mode> --rollback`.\n"
)
return None
subprocess.run(f"mv {existing_deploy_dir} {existing_deploy_dir}-backup", shell=True)
return True
Expand Down
Loading

0 comments on commit a996166

Please sign in to comment.