Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automatic install scripts for #11 #23

Merged
merged 24 commits into from
Oct 2, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions go.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/bin/bash

MANAGE_SCRIPT_URL="https://raw.githubusercontent.com/dwyl/smart-home-security-system/install/manage.py"

check_for_python() {
if ! command -v python3 >/dev/null
then
echo "Can't find a Python 3 install, which is needed for the setup script."
exit 1
else
echo "Python3 found, continuing..."
fi

}

install() {
check_for_python
echo "Downloading install manager..."
curl -sL $MANAGE_SCRIPT_URL -o manage.py
chmod +x manage.py
echo "Running installer..."
python3 manage.py install
}

install
276 changes: 276 additions & 0 deletions manage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,276 @@
#!/usr/bin/python3
# Management script for dwyl smart home
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@th0mas totally get that this is "WIP", but it's looking good so far and thanks for your conscientiousness! 🙌


import argparse
import subprocess
import os
import sys
import shlex

from contextlib import contextmanager

# Global verbose flag to toggle on/off verbose output
VERBOSE=True

# Declare global constants
HUB_SERVER_REPO="https://github.com/dwyl/smart-home-auth-server.git"
FIRMWARE_REPO="https://github.com/dwyl/smart-home-firmware.git"

# Declare the start message here so we don't clog up the rest of the code
START_MESSAGE="""
Smart home is now setup for development.

To setup your development evironment:
source .env

To run the hub server:

cd smart-home-auth-server
mix phx.server

To run the firmware development build:
cd smart-home-firmware
iex -S mix

If these error, please run:
./manage.py clean
./manage.py --verbose install
and file and issue with the output.
"""

# Emulate a shells `cd`
# https://stackoverflow.com/questions/431684/
@contextmanager
def cd(newdir):
prevdir = os.getcwd()
os.chdir(os.path.expanduser(newdir))
try:
yield
finally:
os.chdir(prevdir)

# Helper function to allow for same line I/O without repeating ourselves
def out(line):
print(line, end="", flush=True)

# Run a command, and display the result based on wether VERBOSE is set or not
def run(cmd):
return subprocess.run(shlex.split(cmd), capture_output=not VERBOSE)

def write_env(key):
lines = []
try:
with open(".env", "r+") as f:
keys = filter(
lambda x: not "AUTH_API_KEY=" in x,
f.readlines()
)
lines = [x for x in keys]
except FileNotFoundError:
pass

lines.append("AUTH_API_KEY=" + key + "\n")

with open(".env", "w") as f:
th0mas marked this conversation as resolved.
Show resolved Hide resolved
f.writelines(lines)

# Dowload required files from github.
def download():
out("Downloading Hub Server...")
process = run("git clone " + HUB_SERVER_REPO)
out("OK\n")

out("Downloading firmware...")
process = run("git clone " + FIRMWARE_REPO)
out("OK\n")

# Prompt user for an auth API key so we can finish setup
def get_api_key():
print("AUTH_API_KEY not set!\n")
print("No Auth API key set, found out how at:")
print("https://git.io/JJ6sS")
print("")
print("Please enter your API key once your done to continue setup")
key = input(">")
os.environ["AUTH_API_KEY"] = key

return key

# Check if we have an auth API key set before continuing
def check_for_api_key():
key = os.environ.get("AUTH_API_KEY", None)
if key:
pass
else:
key = get_api_key()

write_env(key)

# Get and display a local API development token for the user
def gen_token():
with cd("./smart-home-auth-server"):
proc = subprocess.run(["mix", "smart_home.gen_token"], capture_output=True)
print("Your development API Bearer token: \n")
print(proc.stdout.decode(sys.stdout.encoding))
print("\nSet this as the authorization header in your favourite API development tool.")

# Install brew, otherwise exit
def must_install_brew():
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is so much more maintainable than a bash script. 👍

if input("Homebrew is needed to install dependencies, install? (y/N)")[0] == "y":
run("/bin/bash -c \"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)\"")
else:
exit(1)

def check_software(software):
exists = True
try:
subprocess.run(shlex.split(software), capture_output=True)
except FileNotFoundError:
exists = False
return exists

# Check that necessary dependencies are installed
def check_deps():
deps = {"elixir": False, "brew": False, "psql": False, "node": False}
# Check for brew
out("Checking for homebrew (to install dependencies)...")
if check_software("brew"):
out("OK\n")
deps["brew"]=True
else:
out("Error - You may need to install dependencies manually!\n")

# Check for elixir
out("Checking for Elixir...")
if check_software("elixir -v"):
out("OK!\n")
deps["elixir"] = True
else:
out("ERROR\n")

# check for postgres
out("Checking for PostgreSQL...")
if check_software("postgres -V"):
out("OK\n")
deps["psql"] = True
else:
out("ERROR\n")

out("Checking for node.js...")
if check_software("node --version"):
out("OK\n")
deps["node"] = True
else:
out("ERROR")

return deps

def install_deps(brew, missing):
if not brew:
must_install_brew()

if input("Do you want to install missing dependencies using brew? (y/N)")[0] == "y":
out("Attempting to install...\n")
else:
out("\nPlease ensure dependenices are installed or there could be errors!\n")
return # Implicit return seems nicer here...

if missing["elixir"]:
out("Installing Elixir...\n")
run("brew install elixir")

if missing["psql"]:
out("Installing PostgreSQL...\n")
run("brew install postgres")

if missing["node"]:
out("Installing node...\n")
run("brew install node")

def install_nerves_dependencies():
if input("Do you want to install build dependencies for nerves? (fwup squashfs coreutils xz pkg-config)? (y/N)")[0] == "y":
run("brew install fwup squashfs coreutils xz pkg-config")
else:
out("NERVES WILL FAIL TO BUILD WITHOUT DEPS INSTALLED \n")
out("https://hexdocs.pm/nerves/installation.html#content\n")

def pre_install():
deps = check_deps()
brew = deps["brew"]

del deps["brew"]

missing = [ key for key in deps if deps[key] == False ]

if len(missing) > 0:
out("Missing dependencies!\n Would you like to install them now?")

if input("(y/N)")[0] == "y":
install_deps(brew, missing)
else:
out("Please install: " + " ,".join(missing) + "\n Then continue with download.")
exit(1)


# Setup deps etc. for firmware
def setup():
# Setup firmware
with cd("./smart-home-firmware"):
out("Installing firmware deps...")
install_nerves_dependencies()
run("mix archive.install hex nerves_bootstrap")
run("mix deps.get")
out("OK\n")

with cd("./smart-home-auth-server"):
out("Installing hub server deps...")
run("mix setup")
out("OK\n")


# Run necessary install functions
def install():
download()
pre_install()
check_for_api_key()
setup()
gen_token()

print(START_MESSAGE)

# Clean up everything we've installed
def clean():
out("Cleaning up...")
run("rm -rf ./smart-home-firmware")
run("rm -rf ./smart-home-auth-server")
run("rm .env")
out("OK\n")

# Declare our command parsers and run the intended function
def main():
parser = argparse.ArgumentParser(description="Manage Dwyl smart home install")
subparsers = parser.add_subparsers(help="Commands")
subparsers.default = "help"

install_parser = subparsers.add_parser("install", help="Install the smart home system")
install_parser.set_defaults(func=install)

clean_parser = subparsers.add_parser("clean", help="Clean up everything")
clean_parser.set_defaults(func=clean)

gen_token_parser = subparsers.add_parser("gen-token", help="Genereate a JWT token for development")
gen_token_parser.set_defaults(func=gen_token)

if len(sys.argv)==1:
parser.print_usage(sys.stderr)
sys.exit(1)

args = parser.parse_args()


if args.func:
args.func()

# Don't run main if we're called from another script.
if __name__ == "__main__":
main()