mirror of
https://github.com/frappe/bench.git
synced 2025-01-24 07:28:25 +00:00
style: chmod+x, format, space -> tabs
This commit is contained in:
parent
80b58d9999
commit
a987c1e9ae
535
easy-install.py
Normal file → Executable file
535
easy-install.py
Normal file → Executable file
@ -1,94 +1,95 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import subprocess
|
import logging
|
||||||
import os
|
import os
|
||||||
|
import platform
|
||||||
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
import urllib.request
|
import urllib.request
|
||||||
import logging
|
from shutil import move, unpack_archive, which
|
||||||
import platform
|
|
||||||
from shutil import which, unpack_archive, move
|
|
||||||
from hashlib import sha224
|
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
|
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
filename="easy-install.log",
|
filename="easy-install.log",
|
||||||
filemode="w",
|
filemode="w",
|
||||||
format="%(asctime)s - %(levelname)s - %(message)s",
|
format="%(asctime)s - %(levelname)s - %(message)s",
|
||||||
level=logging.INFO,
|
level=logging.INFO,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def cprint(*args, level: int = 1):
|
def cprint(*args, level: int = 1):
|
||||||
"""
|
"""
|
||||||
logs colorful messages
|
logs colorful messages
|
||||||
level = 1 : RED
|
level = 1 : RED
|
||||||
level = 2 : GREEN
|
level = 2 : GREEN
|
||||||
level = 3 : YELLOW
|
level = 3 : YELLOW
|
||||||
|
|
||||||
default level = 1
|
default level = 1
|
||||||
"""
|
"""
|
||||||
CRED = "\033[31m"
|
CRED = "\033[31m"
|
||||||
CGRN = "\33[92m"
|
CGRN = "\33[92m"
|
||||||
CYLW = "\33[93m"
|
CYLW = "\33[93m"
|
||||||
reset = "\033[0m"
|
reset = "\033[0m"
|
||||||
message = " ".join(map(str, args))
|
message = " ".join(map(str, args))
|
||||||
if level == 1:
|
if level == 1:
|
||||||
print(CRED, message, reset)
|
print(CRED, message, reset)
|
||||||
if level == 2:
|
if level == 2:
|
||||||
print(CGRN, message, reset)
|
print(CGRN, message, reset)
|
||||||
if level == 3:
|
if level == 3:
|
||||||
print(CYLW, message, reset)
|
print(CYLW, message, reset)
|
||||||
|
|
||||||
|
|
||||||
def clone_frappe_docker_repo() -> None:
|
def clone_frappe_docker_repo() -> None:
|
||||||
try:
|
try:
|
||||||
urllib.request.urlretrieve(
|
urllib.request.urlretrieve(
|
||||||
"https://github.com/frappe/frappe_docker/archive/refs/heads/main.zip",
|
"https://github.com/frappe/frappe_docker/archive/refs/heads/main.zip",
|
||||||
"frappe_docker.zip",
|
"frappe_docker.zip",
|
||||||
)
|
)
|
||||||
logging.info("Downloaded frappe_docker zip file from GitHub")
|
logging.info("Downloaded frappe_docker zip file from GitHub")
|
||||||
unpack_archive(
|
unpack_archive(
|
||||||
"frappe_docker.zip", "."
|
"frappe_docker.zip", "."
|
||||||
) # Unzipping the frappe_docker.zip creates a folder "frappe_docker-main"
|
) # Unzipping the frappe_docker.zip creates a folder "frappe_docker-main"
|
||||||
move("frappe_docker-main", "frappe_docker")
|
move("frappe_docker-main", "frappe_docker")
|
||||||
logging.info("Unzipped and Renamed frappe_docker")
|
logging.info("Unzipped and Renamed frappe_docker")
|
||||||
os.remove("frappe_docker.zip")
|
os.remove("frappe_docker.zip")
|
||||||
logging.info("Removed the downloaded zip file")
|
logging.info("Removed the downloaded zip file")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error("Download and unzip failed", exc_info=True)
|
logging.error("Download and unzip failed", exc_info=True)
|
||||||
cprint("\nCloning frappe_docker Failed\n\n", "[ERROR]: ", e, level=1)
|
cprint("\nCloning frappe_docker Failed\n\n", "[ERROR]: ", e, level=1)
|
||||||
|
|
||||||
def get_from_env(dir,file) -> Dict:
|
|
||||||
env_vars ={}
|
def get_from_env(dir, file) -> Dict:
|
||||||
with open(os.path.join(dir,file)) as f:
|
env_vars = {}
|
||||||
for line in f:
|
with open(os.path.join(dir, file)) as f:
|
||||||
if line.startswith('#') or not line.strip():
|
for line in f:
|
||||||
continue
|
if line.startswith("#") or not line.strip():
|
||||||
key, value = line.strip().split('=', 1)
|
continue
|
||||||
env_vars[key] = value
|
key, value = line.strip().split("=", 1)
|
||||||
return env_vars
|
env_vars[key] = value
|
||||||
|
return env_vars
|
||||||
|
|
||||||
|
|
||||||
def write_to_env(wd: str, site: str, db_pass: str, admin_pass: str, email: str) -> None:
|
def write_to_env(wd: str, site: str, db_pass: str, admin_pass: str, email: str) -> None:
|
||||||
site_name = site or ""
|
site_name = site or ""
|
||||||
example_env = get_from_env(wd,"example.env")
|
example_env = get_from_env(wd, "example.env")
|
||||||
with open(os.path.join(wd, ".env"), "w") as f:
|
with open(os.path.join(wd, ".env"), "w") as f:
|
||||||
f.writelines(
|
f.writelines(
|
||||||
[
|
[
|
||||||
f"FRAPPE_VERSION={example_env['FRAPPE_VERSION']}\n", # Defaults to latest version of Frappe
|
f"FRAPPE_VERSION={example_env['FRAPPE_VERSION']}\n", # Defaults to latest version of Frappe
|
||||||
f"ERPNEXT_VERSION={example_env['ERPNEXT_VERSION']}\n", # defaults to latest version of ERPNext
|
f"ERPNEXT_VERSION={example_env['ERPNEXT_VERSION']}\n", # defaults to latest version of ERPNext
|
||||||
f"DB_PASSWORD={db_pass}\n",
|
f"DB_PASSWORD={db_pass}\n",
|
||||||
"DB_HOST=db\n",
|
"DB_HOST=db\n",
|
||||||
"DB_PORT=3306\n",
|
"DB_PORT=3306\n",
|
||||||
"REDIS_CACHE=redis-cache:6379\n",
|
"REDIS_CACHE=redis-cache:6379\n",
|
||||||
"REDIS_QUEUE=redis-queue:6379\n",
|
"REDIS_QUEUE=redis-queue:6379\n",
|
||||||
"REDIS_SOCKETIO=redis-socketio:6379\n",
|
"REDIS_SOCKETIO=redis-socketio:6379\n",
|
||||||
f"LETSENCRYPT_EMAIL={email}\n",
|
f"LETSENCRYPT_EMAIL={email}\n",
|
||||||
f"FRAPPE_SITE_NAME_HEADER={site_name}\n",
|
f"FRAPPE_SITE_NAME_HEADER={site_name}\n",
|
||||||
f"SITE_ADMIN_PASS={admin_pass}",
|
f"SITE_ADMIN_PASS={admin_pass}",
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def generate_pass(length: int = 12) -> str:
|
def generate_pass(length: int = 12) -> str:
|
||||||
@ -103,221 +104,217 @@ def generate_pass(length: int = 12) -> str:
|
|||||||
|
|
||||||
|
|
||||||
def check_repo_exists() -> bool:
|
def check_repo_exists() -> bool:
|
||||||
return os.path.exists(os.path.join(os.getcwd(), "frappe_docker"))
|
return os.path.exists(os.path.join(os.getcwd(), "frappe_docker"))
|
||||||
|
|
||||||
|
|
||||||
def setup_prod(project: str, sitename: str, email: str) -> None:
|
def setup_prod(project: str, sitename: str, email: str) -> None:
|
||||||
if check_repo_exists():
|
if check_repo_exists():
|
||||||
compose_file_name = os.path.join(
|
compose_file_name = os.path.join(os.path.expanduser("~"), f"{project}-compose.yml")
|
||||||
os.path.expanduser("~"), f"{project}-compose.yml"
|
docker_repo_path = os.path.join(os.getcwd(), "frappe_docker")
|
||||||
)
|
cprint(
|
||||||
docker_repo_path = os.path.join(os.getcwd(), "frappe_docker")
|
"\nPlease refer to .example.env file in the frappe_docker folder to know which keys to set\n\n",
|
||||||
cprint(
|
level=3,
|
||||||
"\nPlease refer to .example.env file in the frappe_docker folder to know which keys to set\n\n",
|
)
|
||||||
level=3,
|
admin_pass = ""
|
||||||
)
|
db_pass = ""
|
||||||
admin_pass = ""
|
with open(compose_file_name, "w") as f:
|
||||||
db_pass = ""
|
# Writing to compose file
|
||||||
with open(compose_file_name, "w") as f:
|
if not os.path.exists(os.path.join(docker_repo_path, ".env")):
|
||||||
# Writing to compose file
|
admin_pass = generate_pass()
|
||||||
if not os.path.exists(os.path.join(docker_repo_path, ".env")):
|
db_pass = generate_pass(9)
|
||||||
admin_pass = generate_pass()
|
write_to_env(docker_repo_path, sitename, db_pass, admin_pass, email)
|
||||||
db_pass = generate_pass(9)
|
cprint(
|
||||||
write_to_env(docker_repo_path, sitename, db_pass, admin_pass, email)
|
"\nA .env file is generated with basic configs. Please edit it to fit to your needs \n",
|
||||||
cprint(
|
level=3,
|
||||||
"\nA .env file is generated with basic configs. Please edit it to fit to your needs \n",
|
)
|
||||||
level=3,
|
with open(os.path.join(os.path.expanduser("~"), "passwords.txt"), "w") as en:
|
||||||
)
|
en.writelines(f"ADMINISTRATOR_PASSWORD={admin_pass}\n")
|
||||||
with open(
|
en.writelines(f"MARIADB_ROOT_PASSWORD={db_pass}\n")
|
||||||
os.path.join(os.path.expanduser("~"), "passwords.txt"), "w"
|
else:
|
||||||
) as en:
|
env = get_from_env(docker_repo_path, ".env")
|
||||||
en.writelines(f"ADMINISTRATOR_PASSWORD={admin_pass}\n")
|
admin_pass = env["SITE_ADMIN_PASS"]
|
||||||
en.writelines(f"MARIADB_ROOT_PASSWORD={db_pass}\n")
|
db_pass = env["DB_PASSWORD"]
|
||||||
else:
|
try:
|
||||||
env = get_from_env(docker_repo_path,".env")
|
# TODO: Include flags for non-https and non-erpnext installation
|
||||||
admin_pass = env['SITE_ADMIN_PASS']
|
subprocess.run(
|
||||||
db_pass = env['DB_PASSWORD']
|
[
|
||||||
try:
|
which("docker"),
|
||||||
# TODO: Include flags for non-https and non-erpnext installation
|
"compose",
|
||||||
subprocess.run(
|
"--project-name",
|
||||||
[
|
project,
|
||||||
which("docker"),
|
"-f",
|
||||||
"compose",
|
"compose.yaml",
|
||||||
"--project-name",
|
"-f",
|
||||||
project,
|
"overrides/compose.mariadb.yaml",
|
||||||
"-f",
|
"-f",
|
||||||
"compose.yaml",
|
"overrides/compose.redis.yaml",
|
||||||
"-f",
|
# "-f", "overrides/compose.noproxy.yaml", TODO: Add support for local proxying without HTTPs
|
||||||
"overrides/compose.mariadb.yaml",
|
"-f",
|
||||||
"-f",
|
"overrides/compose.erpnext.yaml",
|
||||||
"overrides/compose.redis.yaml",
|
"-f",
|
||||||
# "-f", "overrides/compose.noproxy.yaml", TODO: Add support for local proxying without HTTPs
|
"overrides/compose.https.yaml",
|
||||||
"-f",
|
"--env-file",
|
||||||
"overrides/compose.erpnext.yaml",
|
".env",
|
||||||
"-f",
|
"config",
|
||||||
"overrides/compose.https.yaml",
|
],
|
||||||
"--env-file",
|
cwd=docker_repo_path,
|
||||||
".env",
|
stdout=f,
|
||||||
"config",
|
check=True,
|
||||||
],
|
)
|
||||||
cwd=docker_repo_path,
|
|
||||||
stdout=f,
|
|
||||||
check=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception:
|
||||||
logging.error("Docker Compose generation failed", exc_info=True)
|
logging.error("Docker Compose generation failed", exc_info=True)
|
||||||
cprint("\nGenerating Compose File failed\n")
|
cprint("\nGenerating Compose File failed\n")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
try:
|
try:
|
||||||
# Starting with generated compose file
|
# Starting with generated compose file
|
||||||
subprocess.run(
|
subprocess.run(
|
||||||
[
|
[
|
||||||
which("docker"),
|
which("docker"),
|
||||||
"compose",
|
"compose",
|
||||||
"-p",
|
"-p",
|
||||||
project,
|
project,
|
||||||
"-f",
|
"-f",
|
||||||
compose_file_name,
|
compose_file_name,
|
||||||
"up",
|
"up",
|
||||||
"-d",
|
"-d",
|
||||||
],
|
],
|
||||||
check=True,
|
check=True,
|
||||||
)
|
)
|
||||||
logging.info(f"Docker Compose file generated at ~/{project}-compose.yml")
|
logging.info(f"Docker Compose file generated at ~/{project}-compose.yml")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error("Prod docker-compose failed", exc_info=True)
|
logging.error("Prod docker-compose failed", exc_info=True)
|
||||||
cprint(" Docker Compose failed, please check the container logs\n", e)
|
cprint(" Docker Compose failed, please check the container logs\n", e)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
cprint(f"\nCreating site: {sitename} \n", level=3)
|
cprint(f"\nCreating site: {sitename} \n", level=3)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
subprocess.run(
|
subprocess.run(
|
||||||
[
|
[
|
||||||
which("docker"),
|
which("docker"),
|
||||||
"compose",
|
"compose",
|
||||||
"-p",
|
"-p",
|
||||||
project,
|
project,
|
||||||
"exec",
|
"exec",
|
||||||
"backend",
|
"backend",
|
||||||
"bench",
|
"bench",
|
||||||
"new-site",
|
"new-site",
|
||||||
sitename,
|
sitename,
|
||||||
"--db-root-password",
|
"--db-root-password",
|
||||||
db_pass,
|
db_pass,
|
||||||
"--admin-password",
|
"--admin-password",
|
||||||
admin_pass,
|
admin_pass,
|
||||||
"--install-app",
|
"--install-app",
|
||||||
"erpnext",
|
"erpnext",
|
||||||
"--set-default"
|
"--set-default",
|
||||||
],
|
],
|
||||||
check=True,
|
check=True,
|
||||||
)
|
)
|
||||||
logging.info("New site creation completed")
|
logging.info("New site creation completed")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error("Bench site creation failed", exc_info=True)
|
logging.error("Bench site creation failed", exc_info=True)
|
||||||
cprint("Bench Site creation failed\n", e)
|
cprint("Bench Site creation failed\n", e)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
else:
|
else:
|
||||||
install_docker()
|
install_docker()
|
||||||
clone_frappe_docker_repo()
|
clone_frappe_docker_repo()
|
||||||
setup_prod(project, sitename, email) # Recursive
|
setup_prod(project, sitename, email) # Recursive
|
||||||
|
|
||||||
|
|
||||||
def setup_dev_instance(project: str):
|
def setup_dev_instance(project: str):
|
||||||
if check_repo_exists():
|
if check_repo_exists():
|
||||||
try:
|
try:
|
||||||
subprocess.run(
|
subprocess.run(
|
||||||
[
|
[
|
||||||
"docker",
|
"docker",
|
||||||
"compose",
|
"compose",
|
||||||
"-f",
|
"-f",
|
||||||
"devcontainer-example/docker-compose.yml",
|
"devcontainer-example/docker-compose.yml",
|
||||||
"--project-name",
|
"--project-name",
|
||||||
project,
|
project,
|
||||||
"up",
|
"up",
|
||||||
"-d",
|
"-d",
|
||||||
],
|
],
|
||||||
cwd=os.path.join(os.getcwd(), "frappe_docker"),
|
cwd=os.path.join(os.getcwd(), "frappe_docker"),
|
||||||
check=True,
|
check=True,
|
||||||
)
|
)
|
||||||
cprint(
|
cprint(
|
||||||
"Please go through the Development Documentation: https://github.com/frappe/frappe_docker/tree/main/development to fully complete the setup.",
|
"Please go through the Development Documentation: https://github.com/frappe/frappe_docker/tree/main/development to fully complete the setup.",
|
||||||
level=2,
|
level=2,
|
||||||
)
|
)
|
||||||
logging.info("Development Setup completed")
|
logging.info("Development Setup completed")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error("Dev Environment setup failed", exc_info=True)
|
logging.error("Dev Environment setup failed", exc_info=True)
|
||||||
cprint("Setting Up Development Environment Failed\n", e)
|
cprint("Setting Up Development Environment Failed\n", e)
|
||||||
else:
|
else:
|
||||||
install_docker()
|
install_docker()
|
||||||
clone_frappe_docker_repo()
|
clone_frappe_docker_repo()
|
||||||
setup_dev_instance(project) # Recursion on goes brrrr
|
setup_dev_instance(project) # Recursion on goes brrrr
|
||||||
|
|
||||||
|
|
||||||
def install_docker():
|
def install_docker():
|
||||||
if which("docker") is not None:
|
if which("docker") is not None:
|
||||||
return
|
return
|
||||||
cprint("Docker is not installed, Installing Docker...", level=3)
|
cprint("Docker is not installed, Installing Docker...", level=3)
|
||||||
logging.info("Docker not found, installing Docker")
|
logging.info("Docker not found, installing Docker")
|
||||||
if platform.system() == "Darwin" or platform.system() == "Windows":
|
if platform.system() == "Darwin" or platform.system() == "Windows":
|
||||||
print(
|
print(
|
||||||
f"""
|
f"""
|
||||||
This script doesn't install Docker on {"Mac" if platform.system()=="Darwin" else "Windows"}.
|
This script doesn't install Docker on {"Mac" if platform.system()=="Darwin" else "Windows"}.
|
||||||
|
|
||||||
Please go through the Docker Installation docs for your system and run this script again"""
|
Please go through the Docker Installation docs for your system and run this script again"""
|
||||||
)
|
)
|
||||||
logging.debug("Docker setup failed due to platform is not Linux")
|
logging.debug("Docker setup failed due to platform is not Linux")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
try:
|
try:
|
||||||
ps = subprocess.run(
|
ps = subprocess.run(
|
||||||
["curl", "-fsSL", "https://get.docker.com"],
|
["curl", "-fsSL", "https://get.docker.com"],
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
check=True,
|
check=True,
|
||||||
)
|
)
|
||||||
subprocess.run(["/bin/bash"], input=ps.stdout, capture_output=True)
|
subprocess.run(["/bin/bash"], input=ps.stdout, capture_output=True)
|
||||||
subprocess.run(
|
subprocess.run(
|
||||||
["sudo", "usermod", "-aG", "docker", str(os.getenv("USER"))], check=True
|
["sudo", "usermod", "-aG", "docker", str(os.getenv("USER"))], check=True
|
||||||
)
|
)
|
||||||
cprint("Waiting Docker to start", level=3)
|
cprint("Waiting Docker to start", level=3)
|
||||||
time.sleep(10)
|
time.sleep(10)
|
||||||
subprocess.run(["sudo", "systemctl", "restart", "docker.service"], check=True)
|
subprocess.run(["sudo", "systemctl", "restart", "docker.service"], check=True)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error("Installing Docker failed", exc_info=True)
|
logging.error("Installing Docker failed", exc_info=True)
|
||||||
cprint("Failed to Install Docker\n", e)
|
cprint("Failed to Install Docker\n", e)
|
||||||
cprint("\n Try Installing Docker Manually and re-run this script again\n")
|
cprint("\n Try Installing Docker Manually and re-run this script again\n")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
parser = argparse.ArgumentParser(description="Install Frappe with Docker")
|
parser = argparse.ArgumentParser(description="Install Frappe with Docker")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-p", "--prod", help="Setup Production System", action="store_true"
|
"-p", "--prod", help="Setup Production System", action="store_true"
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-d", "--dev", help="Setup Development System", action="store_true"
|
"-d", "--dev", help="Setup Development System", action="store_true"
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-s",
|
"-s",
|
||||||
"--sitename",
|
"--sitename",
|
||||||
help="The Site Name for your production site",
|
help="The Site Name for your production site",
|
||||||
default="site1.local",
|
default="site1.local",
|
||||||
)
|
)
|
||||||
parser.add_argument("-n", "--project", help="Project Name", default="frappe")
|
parser.add_argument("-n", "--project", help="Project Name", default="frappe")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--email", help="Add email for the SSL.", required="--prod" in sys.argv
|
"--email", help="Add email for the SSL.", required="--prod" in sys.argv
|
||||||
)
|
)
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
if args.dev:
|
if args.dev:
|
||||||
cprint("\nSetting Up Development Instance\n", level=2)
|
cprint("\nSetting Up Development Instance\n", level=2)
|
||||||
logging.info("Running Development Setup")
|
logging.info("Running Development Setup")
|
||||||
setup_dev_instance(args.project)
|
setup_dev_instance(args.project)
|
||||||
elif args.prod:
|
elif args.prod:
|
||||||
cprint("\nSetting Up Production Instance\n", level=2)
|
cprint("\nSetting Up Production Instance\n", level=2)
|
||||||
logging.info("Running Production Setup")
|
logging.info("Running Production Setup")
|
||||||
if "example.com" in args.email:
|
if "example.com" in args.email:
|
||||||
cprint("Emails with example.com not acceptable", level=1)
|
cprint("Emails with example.com not acceptable", level=1)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
setup_prod(args.project, args.sitename, args.email)
|
setup_prod(args.project, args.sitename, args.email)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user