mirror of
https://github.com/mozilla/DeepSpeech.git
synced 2025-10-26 11:19:39 +00:00
190 lines
6.6 KiB
Python
190 lines
6.6 KiB
Python
import os
|
|
import paramiko
|
|
import pysftp
|
|
import sys
|
|
|
|
from bs4 import BeautifulSoup
|
|
from log import merge_logs
|
|
|
|
def parse_for_deps(filename):
|
|
"""
|
|
This takes an HTML file as input and output a list of existing depenencies.
|
|
Empty output means something was wrong.
|
|
Dependency is one of: js script loaded, css stylesheet loaded
|
|
"""
|
|
|
|
with open(filename, 'r') as code:
|
|
soup = BeautifulSoup(code.read(), 'html.parser')
|
|
|
|
external_links = filter(lambda x: x is not None, [ link.get('href') for link in soup.find_all('link') ])
|
|
external_scripts = filter(lambda x: x is not None, [ script.get('src') for script in soup.find_all('script') ])
|
|
|
|
# Verify with stat()
|
|
try:
|
|
all_resources = map(lambda x: os.stat(x), external_links + external_scripts)
|
|
except OSError as ex:
|
|
if ex.errno == 2:
|
|
print "Missing dependency", ex
|
|
return []
|
|
raise ex
|
|
|
|
# Try to read the file
|
|
try:
|
|
readable_resources = map(lambda x: os.open(x, os.O_RDONLY), external_links + external_scripts)
|
|
map(lambda x: os.close(x), readable_resources)
|
|
except OSError as ex:
|
|
print "Unreadable dependency", ex
|
|
return []
|
|
|
|
# It should be all good.
|
|
return external_links + external_scripts
|
|
|
|
def get_ssh(auth_infos):
|
|
"""
|
|
Connect to SSH server
|
|
"""
|
|
cinfo = {'host': auth_infos['ds_website_server_fqdn'],
|
|
'username': auth_infos['ds_website_username'],
|
|
'private_key': auth_infos['ds_website_privkey'],
|
|
'port': auth_infos['ds_website_server_port']}
|
|
return pysftp.Connection(**cinfo)
|
|
|
|
def verify_ssh_dir(auth_infos):
|
|
"""
|
|
This should ensure that SSH connection works and that the directory where
|
|
we want to push data is either empty or contains at least a .htaccess and
|
|
index.htm file
|
|
"""
|
|
print "Checking connection: %s@%s:%s (port %d)" % (auth_infos['ds_website_username'], auth_infos['ds_website_server_fqdn'], auth_infos['ds_website_server_root'], auth_infos['ds_website_server_port'])
|
|
|
|
try:
|
|
with get_ssh(auth_infos) as sftp:
|
|
with sftp.cd(auth_infos['ds_website_server_root']):
|
|
files = sftp.listdir()
|
|
if len(files) == 0 or (('.htaccess' in files) and ('index.htm' in files)):
|
|
return True
|
|
else:
|
|
print "Invalid content for %s:" % (auth_infos['ds_website_server_root']), files
|
|
return False
|
|
except paramiko.ssh_exception.AuthenticationException as ex:
|
|
print "Authentication error, please verify credentials"
|
|
return False
|
|
except IOError as ex:
|
|
print "Unable to read a file (private key or invalid path on server ?)", ex
|
|
return False
|
|
|
|
# Should not be reached
|
|
return False
|
|
|
|
def push_files_sftp(files, auth_infos):
|
|
"""
|
|
This will push all of the ``files`` listed to the server root folder.
|
|
"""
|
|
|
|
# Directories that we might be required to create
|
|
dirs = filter(lambda x: len(x) > 0, list(set([ os.path.dirname(x) for x in files ])))
|
|
|
|
created = []
|
|
pushed = []
|
|
|
|
try:
|
|
with get_ssh(auth_infos) as sftp:
|
|
with sftp.cd(auth_infos['ds_website_server_root']):
|
|
# Create dirs if needed, they all should depend from the root
|
|
for dir in dirs:
|
|
if not sftp.isdir(dir):
|
|
print "Creating directory", dir
|
|
sftp.makedirs(dir)
|
|
created.append(dir)
|
|
|
|
# Push all files, chdir() for each
|
|
for fil in files:
|
|
with sftp.cd(os.path.dirname(fil)):
|
|
print "Pushing", fil
|
|
sftp.put(fil)
|
|
pushed.append(fil)
|
|
|
|
return pushed
|
|
except paramiko.ssh_exception.AuthenticationException as ex:
|
|
print "Authentication error, please verify credentials"
|
|
return False
|
|
except IOError as ex:
|
|
print "Unable to read a file (private key or invalid path on server ?)", ex
|
|
return False
|
|
|
|
# Should not be reached
|
|
return False
|
|
|
|
def maybe_publish(file='index.htm'):
|
|
"""
|
|
Publishing to the web requires the following env variables to be set
|
|
'ds_website_username: defines the SSH username to connect to
|
|
'ds_website_privkey: defines the SSH privkey filename to use for connection
|
|
'ds_website_server_fqdn: hostname of the SSH server
|
|
'ds_website_server_port: port of the SSH server, defaults to 22
|
|
'ds_website_server_root: directory on the server where to push data,
|
|
should be either empty, or containing at least
|
|
a .htaccess and index.htm file
|
|
"""
|
|
|
|
ssh_auth_infos = {
|
|
'ds_website_username': '',
|
|
'ds_website_privkey': '',
|
|
'ds_website_server_fqdn': '',
|
|
'ds_website_server_port': 22,
|
|
'ds_website_server_root': ''
|
|
}
|
|
|
|
for key in ssh_auth_infos.keys():
|
|
vartype = type(ssh_auth_infos[key])
|
|
value = os.environ.get(key)
|
|
if value is not None:
|
|
if vartype == str:
|
|
ssh_auth_infos[key] = str(os.environ.get(key))
|
|
elif vartype == int:
|
|
try:
|
|
ssh_auth_infos[key] = int(os.environ.get(key))
|
|
except TypeError as ex:
|
|
print "WARNING:", "Keeping default SSH port value because of:", ex
|
|
|
|
missing_env = filter(lambda x: len(str(ssh_auth_infos[x])) == 0, ssh_auth_infos.keys())
|
|
if len(missing_env) > 0:
|
|
print "Not publishing, missing some required environment variables:", missing_env
|
|
print "But maybe this is what you wanted, after all ..."
|
|
return False
|
|
|
|
merge_logs("logs")
|
|
|
|
all_deps = parse_for_deps(file)
|
|
|
|
if len(all_deps) == 0:
|
|
print "Problem during deps computation, aborting"
|
|
return False
|
|
|
|
if not verify_ssh_dir(ssh_auth_infos):
|
|
print "Problem during SSH directory verification, aborting"
|
|
return False
|
|
|
|
all_files = [ file ] + all_deps
|
|
uploaded = push_files_sftp(all_files, ssh_auth_infos)
|
|
if len(uploaded) == 0:
|
|
print "Unable to upload anything"
|
|
return False
|
|
elif len(uploaded) != len(all_files):
|
|
print "Partial upload has been completed:"
|
|
print "all_files=", all_files
|
|
print "uploaded=", uploaded
|
|
else:
|
|
print "Complete upload has been completed."
|
|
|
|
return True
|
|
|
|
# Support CLI invocation
|
|
if __name__ == "__main__":
|
|
if maybe_publish():
|
|
print "All good!"
|
|
sys.exit(0)
|
|
else:
|
|
print "Error happened ..."
|
|
sys.exit(1)
|