mirror of
https://github.com/cupcakearmy/drone-deploy.git
synced 2024-10-09 11:13:39 +02:00
130 lines
4.4 KiB
Python
130 lines
4.4 KiB
Python
import io
|
|
import os
|
|
import random
|
|
import string
|
|
import subprocess
|
|
import warnings
|
|
from os.path import abspath, join, dirname
|
|
from typing import List
|
|
|
|
import paramiko
|
|
from paramiko import SSHClient
|
|
|
|
VERSION = '1.0.3'
|
|
|
|
|
|
def get_random_string(length: int = 128) -> str:
|
|
return ''.join(random.choices(string.ascii_lowercase + string.digits, k=length))
|
|
|
|
|
|
def execute(c: SSHClient, cmd: str, path: str = None, env: dict = None) -> str:
|
|
if path is not None:
|
|
cmd = 'cd {}; {}'.format(path, cmd)
|
|
stdin, stdout, stderr = c.exec_command(cmd, environment=env)
|
|
return stdout.read().decode('utf-8').strip()
|
|
|
|
|
|
def main():
|
|
print(f'> Version: {VERSION}')
|
|
print('> Deployment started 🚀')
|
|
host = os.environ.get('PLUGIN_HOST')
|
|
port = os.environ.get('PLUGIN_PORT', 22)
|
|
user = os.environ.get('PLUGIN_USER')
|
|
|
|
password = os.environ.get('PLUGIN_PASSWORD')
|
|
key = os.environ.get('PLUGIN_KEY')
|
|
|
|
# Takes a string, splits it at the comma and removes empty elements
|
|
def clean_array(s: str) -> List[str]:
|
|
return list(filter(None, s.split(',')))
|
|
|
|
commands = clean_array(os.environ.get('PLUGIN_COMMANDS', ''))
|
|
sources = clean_array(os.environ.get('PLUGIN_SOURCES', ''))
|
|
deletes = clean_array(os.environ.get('PLUGIN_DELETE', ''))
|
|
target = os.environ.get('PLUGIN_TARGET')
|
|
|
|
# Check for host, port and user
|
|
for env in [host, port, user]:
|
|
if env is None:
|
|
raise Exception('Missing host, port or user env variable')
|
|
|
|
# Check if there is a possible authentication method
|
|
if len(list(filter(lambda x: x is not None, [password, key]))) == 0:
|
|
raise Exception('No authentication method provided')
|
|
|
|
# Check if target is set
|
|
if len(sources) != 0 and target is None:
|
|
raise Exception('Target not set')
|
|
|
|
# Remote Envs
|
|
envs_raw = os.environ.get('PLUGIN_ENVS')
|
|
envs = None
|
|
if envs_raw is not None:
|
|
prefix = 'PLUGIN_'
|
|
# Take only the envs that start with PLUGIN_ and remove the prefix
|
|
envs = {k[len(prefix):]: v for k, v in os.environ.items() if k.startswith(prefix)}
|
|
|
|
if 'all' != envs_raw or ',' in envs_raw:
|
|
# Make them uppercase
|
|
selected = [x.upper() for x in clean_array(envs_raw)]
|
|
# Select only the envs that where specified inside of envs
|
|
envs = {k: v for k, v in envs.items() if k in selected}
|
|
|
|
ssh: SSHClient = paramiko.SSHClient()
|
|
try:
|
|
k = paramiko.RSAKey.from_private_key(io.StringIO(key))
|
|
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
|
print(host, user, port)
|
|
ssh.connect(hostname=host, username=user, pkey=k, port=port, password=password, timeout=3)
|
|
|
|
# If a target is set, make sure the directory is created and writable
|
|
if target is not None:
|
|
try:
|
|
execute(ssh, 'mkdir -p {}'.format(target))
|
|
tmp_file = get_random_string()
|
|
execute(ssh, 'touch {}; rm {}'.format(tmp_file, tmp_file), target)
|
|
except Exception:
|
|
raise Exception('Could not create directory')
|
|
|
|
sftp = ssh.open_sftp()
|
|
try:
|
|
# DELETE
|
|
for delete in deletes:
|
|
sftp.remove(join(target, delete))
|
|
|
|
# COPY
|
|
if len(sources) != 0:
|
|
archive = get_random_string(64) + '.tar.gz' # Keep the max file name length under 128 chars
|
|
archive_local = abspath(archive)
|
|
archive_remote = join(target, archive)
|
|
|
|
# Compress
|
|
cmd = ['tar', '-czf', archive, '-C', dirname(archive_local), *sources]
|
|
run = subprocess.run(cmd, capture_output=True)
|
|
if run.returncode != 0:
|
|
raise Exception('Error while compressing locally. {}'.format(run.stderr.decode('utf-8').strip()))
|
|
|
|
# Upload
|
|
sftp.put(archive_local, archive_remote)
|
|
# Extract
|
|
execute(ssh, 'tar -xzf {}'.format(archive), target)
|
|
|
|
# Delete Archives
|
|
sftp.remove(archive_remote)
|
|
subprocess.run(['rm', archive_local], capture_output=True)
|
|
finally:
|
|
sftp.close()
|
|
|
|
for command in commands:
|
|
output = execute(ssh, command, target, envs)
|
|
print(command)
|
|
print(output)
|
|
|
|
finally:
|
|
ssh.close()
|
|
|
|
|
|
with warnings.catch_warnings():
|
|
warnings.simplefilter('ignore')
|
|
main()
|