2020-06-30 16:38:06 +01:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
import shutil
|
|
|
|
import os
|
2020-07-17 16:49:35 +01:00
|
|
|
from pathlib import Path
|
2020-06-30 16:38:06 +01:00
|
|
|
import sys
|
2021-02-07 18:32:07 +00:00
|
|
|
import subprocess
|
2020-06-30 16:38:06 +01:00
|
|
|
from cmd import Cmd
|
|
|
|
|
2020-08-13 19:50:21 +01:00
|
|
|
stage_dir = '.music-tools'
|
2020-07-17 16:49:35 +01:00
|
|
|
scss_rel_path = Path('src', 'scss', 'style.scss')
|
|
|
|
css_rel_path = Path('build', 'style.css')
|
2020-06-30 16:38:06 +01:00
|
|
|
|
2020-07-01 11:30:48 +01:00
|
|
|
folders_to_ignore = ['venv', 'docs', '.git', '.idea', 'node_modules']
|
|
|
|
|
2020-06-30 16:38:06 +01:00
|
|
|
|
2021-02-16 22:51:13 +00:00
|
|
|
"""
|
|
|
|
COMPONENTS:
|
|
|
|
|
|
|
|
* App Engine
|
|
|
|
* Cloud Functions:
|
|
|
|
run_user_playlist
|
|
|
|
update_tag
|
|
|
|
|
|
|
|
run_all_playlists
|
|
|
|
run_all_playlist_stats
|
|
|
|
run_all_tags
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
2020-06-30 16:38:06 +01:00
|
|
|
class Admin(Cmd):
|
|
|
|
intro = 'Music Tools Admin... ? for help'
|
|
|
|
prompt = '> '
|
|
|
|
|
2021-01-27 01:45:47 +00:00
|
|
|
locals = ['spotframework', 'fmframework', 'spotfm']
|
|
|
|
|
2021-02-16 22:51:13 +00:00
|
|
|
def do_prepare_local_stage(self, args):
|
|
|
|
"""
|
|
|
|
Prepare local working directory for deployment using static sarsoolib injections
|
|
|
|
"""
|
2021-02-07 20:40:59 +00:00
|
|
|
# Now done via online repos, not static injection
|
2021-02-16 22:51:13 +00:00
|
|
|
print('>> backing up a directory')
|
|
|
|
os.chdir(Path(__file__).absolute().parent.parent)
|
|
|
|
|
|
|
|
print('>> deleting old deployment stage')
|
|
|
|
shutil.rmtree(stage_dir, ignore_errors=True)
|
|
|
|
|
|
|
|
print('>> copying main source')
|
|
|
|
shutil.copytree('playlist-manager' if Path('playlist-manager').exists() else 'Music-Tools',
|
|
|
|
stage_dir,
|
|
|
|
ignore=lambda path, contents:
|
|
|
|
contents if any(i in Path(path).parts for i in folders_to_ignore) else []
|
|
|
|
)
|
|
|
|
|
|
|
|
for dependency in Admin.locals:
|
|
|
|
print(f'>> injecting {dependency}')
|
|
|
|
shutil.copytree(
|
|
|
|
Path(dependency, dependency),
|
|
|
|
Path(stage_dir, dependency)
|
|
|
|
)
|
|
|
|
|
|
|
|
os.chdir(stage_dir)
|
|
|
|
|
|
|
|
# ADMIN
|
|
|
|
|
|
|
|
def do_set_project(self, args):
|
|
|
|
"""
|
|
|
|
Set project setting in gcloud console
|
|
|
|
"""
|
|
|
|
print('>> setting project')
|
2021-02-08 00:56:11 +00:00
|
|
|
subprocess.check_call('gcloud config set project sarsooxyz', shell=True)
|
2020-06-30 16:38:06 +01:00
|
|
|
|
2020-07-17 16:49:35 +01:00
|
|
|
def deploy_function(self, name, timeout: int = 60, region='europe-west2'):
|
2021-02-16 22:51:13 +00:00
|
|
|
"""
|
|
|
|
Deploy function with required environment variables
|
|
|
|
"""
|
2021-02-08 00:56:11 +00:00
|
|
|
subprocess.check_call(
|
|
|
|
f'gcloud functions deploy {name} '
|
|
|
|
f'--region {region} '
|
|
|
|
'--runtime=python38 '
|
|
|
|
f'--trigger-topic {name} '
|
|
|
|
'--set-env-vars DEPLOY_DESTINATION=PROD '
|
|
|
|
f'--timeout={timeout}s', shell=True
|
2021-02-07 23:32:46 +00:00
|
|
|
)
|
2020-06-30 16:38:06 +01:00
|
|
|
|
2021-02-16 22:51:13 +00:00
|
|
|
def do_rename(self, args):
|
|
|
|
"""
|
|
|
|
Rename playlist in firestore
|
|
|
|
"""
|
|
|
|
from music.model.user import User
|
|
|
|
from music.model.playlist import Playlist
|
2020-07-17 16:49:35 +01:00
|
|
|
|
2021-02-16 22:51:13 +00:00
|
|
|
username = input('enter username: ')
|
|
|
|
user = User.collection.filter('username', '==', username).get()
|
2020-07-17 16:49:35 +01:00
|
|
|
|
2021-02-16 22:51:13 +00:00
|
|
|
if user is None:
|
|
|
|
print('>> user not found')
|
2020-07-17 16:49:35 +01:00
|
|
|
|
2021-02-16 22:51:13 +00:00
|
|
|
name = input('enter playlist name: ')
|
|
|
|
playlist = Playlist.collection.parent(user.key).filter('name', '==', name).get()
|
|
|
|
|
|
|
|
if playlist is None:
|
|
|
|
print('>> playlist not found')
|
|
|
|
|
|
|
|
new_name = input('enter new name: ')
|
|
|
|
playlist.name = new_name
|
|
|
|
playlist.update()
|
|
|
|
|
|
|
|
# PYTHON
|
|
|
|
|
|
|
|
def copy_main_file(self, path):
|
|
|
|
"""
|
|
|
|
Copy main.{path}.py file corresponding to Python build stage
|
|
|
|
"""
|
|
|
|
print('>> preparing main.py')
|
|
|
|
shutil.copy(f'main.{path}.py', 'main.py')
|
2020-07-17 16:49:35 +01:00
|
|
|
|
2021-02-16 22:51:13 +00:00
|
|
|
def do_main_group(self, args):
|
|
|
|
"""
|
|
|
|
Compile front-end and deploy to App Engine. Deploy primary functions (run_user_playlist, update_tag)
|
|
|
|
"""
|
|
|
|
self.do_set_project(None)
|
|
|
|
self.export_filtered_dependencies()
|
|
|
|
|
|
|
|
self.do_app(args)
|
|
|
|
self.do_tag(None)
|
|
|
|
self.do_playlist(None)
|
2020-07-01 11:30:48 +01:00
|
|
|
|
2021-02-16 22:51:13 +00:00
|
|
|
def do_app(self, args):
|
|
|
|
"""
|
|
|
|
Compile front-end and deploy to App Engine
|
|
|
|
"""
|
|
|
|
if not '-nb' in args.strip().split(' '):
|
|
|
|
print(">> compiling frontend")
|
|
|
|
self.compile_frontend()
|
|
|
|
self.copy_main_file('api')
|
2020-06-30 16:38:06 +01:00
|
|
|
|
2021-02-16 22:51:13 +00:00
|
|
|
print('>> deploying app engine service')
|
2021-02-08 00:56:11 +00:00
|
|
|
subprocess.check_call('gcloud app deploy', shell=True)
|
2020-06-30 16:38:06 +01:00
|
|
|
|
2021-02-16 22:51:13 +00:00
|
|
|
def function_deploy(self, main, function_id):
|
|
|
|
self.copy_main_file(main)
|
2020-06-30 16:38:06 +01:00
|
|
|
|
2021-02-16 22:51:13 +00:00
|
|
|
print(f'>> deploying {function_id}')
|
|
|
|
self.deploy_function(function_id)
|
|
|
|
|
|
|
|
def do_tag(self, args):
|
|
|
|
"""
|
|
|
|
Deploy update_tag function
|
|
|
|
"""
|
|
|
|
self.function_deploy('update_tag', 'update_tag')
|
2020-06-30 16:38:06 +01:00
|
|
|
|
|
|
|
def do_playlist(self, args):
|
2021-02-16 22:51:13 +00:00
|
|
|
"""
|
|
|
|
Deploy run_user_playlist function
|
|
|
|
"""
|
|
|
|
self.function_deploy('run_playlist', 'run_user_playlist')
|
2020-06-30 16:38:06 +01:00
|
|
|
|
|
|
|
|
2020-08-13 19:50:21 +01:00
|
|
|
# all playlists cron job
|
|
|
|
def do_playlist_cron(self, args):
|
2021-02-16 22:51:13 +00:00
|
|
|
"""
|
|
|
|
Deploy run_all_playlists function
|
|
|
|
"""
|
|
|
|
self.function_deploy('cron', 'run_all_playlists')
|
2020-08-13 19:50:21 +01:00
|
|
|
|
|
|
|
# all stats refresh cron job
|
2021-02-16 22:51:13 +00:00
|
|
|
def do_playlist_stats_cron(self, args):
|
|
|
|
"""
|
|
|
|
Deploy run_all_playlist_stats function
|
|
|
|
"""
|
|
|
|
self.function_deploy('cron', 'run_all_playlist_stats')
|
2020-08-13 19:50:21 +01:00
|
|
|
|
|
|
|
# all tags cron job
|
|
|
|
def do_tags_cron(self, args):
|
2021-02-16 22:51:13 +00:00
|
|
|
"""
|
|
|
|
Deploy run_all_tags function
|
|
|
|
"""
|
|
|
|
self.function_deploy('cron', 'run_all_tags')
|
2020-08-13 19:50:21 +01:00
|
|
|
|
|
|
|
# redeploy all cron job functions
|
2021-02-16 22:51:13 +00:00
|
|
|
def do_cron_functions(self, args):
|
|
|
|
"""
|
|
|
|
Deploy background functions including cron job scheduling for update actions (run_all_playlists, run_all_playlist_stats, run_all_tags)
|
|
|
|
"""
|
|
|
|
self.do_set_project(None)
|
|
|
|
self.export_filtered_dependencies()
|
|
|
|
|
|
|
|
self.do_playlist_cron(None)
|
|
|
|
self.do_playlist_stats_cron(None)
|
|
|
|
self.do_tags_cron(None)
|
|
|
|
|
|
|
|
def do_pydepend(self, args):
|
|
|
|
"""
|
|
|
|
Generate and export requirements.txt from Poetry manifest
|
|
|
|
"""
|
2021-01-27 01:45:47 +00:00
|
|
|
self.export_filtered_dependencies()
|
|
|
|
|
|
|
|
def export_filtered_dependencies(self):
|
2021-02-08 00:56:11 +00:00
|
|
|
string = subprocess.check_output('poetry export -f requirements.txt --without-hashes', shell=True, text=True)
|
2021-01-27 01:45:47 +00:00
|
|
|
|
|
|
|
depend = string.split('\n')
|
|
|
|
|
2021-02-07 20:40:59 +00:00
|
|
|
# filtered = [i for i in depend if not any(i.startswith(local) for local in Admin.locals)]
|
|
|
|
# filtered = [i for i in filtered if '==' in i]
|
|
|
|
# filtered = [i[:-2] for i in filtered] # get rid of space and slash at end of line
|
|
|
|
filtered = [i.split(';')[0] for i in depend]
|
2021-01-27 01:45:47 +00:00
|
|
|
|
|
|
|
with open('requirements.txt', 'w') as f:
|
|
|
|
f.write("\n".join(filtered))
|
|
|
|
|
2021-02-16 22:51:13 +00:00
|
|
|
# FRONT-END
|
|
|
|
|
|
|
|
def compile_frontend(self):
|
|
|
|
"""
|
|
|
|
Compile sass to css and run npm build task
|
|
|
|
"""
|
|
|
|
print('>> building css')
|
|
|
|
subprocess.check_call(f'sass --style=compressed {str(scss_rel_path)} {str(css_rel_path)}', shell=True)
|
|
|
|
|
|
|
|
print('>> building javascript')
|
|
|
|
subprocess.check_call('npm run build', shell=True)
|
|
|
|
|
|
|
|
def do_sass(self, args):
|
|
|
|
"""
|
|
|
|
Compile sass to css
|
|
|
|
"""
|
|
|
|
subprocess.check_call(f'sass --style=compressed {str(scss_rel_path)} {str(css_rel_path)}', shell=True)
|
|
|
|
|
|
|
|
def do_watchsass(self, args):
|
|
|
|
"""
|
|
|
|
Run sass compiler with watch argument to begin watching source folder for changes
|
|
|
|
"""
|
|
|
|
subprocess.check_call(f'sass --style=compressed --watch {str(scss_rel_path)} {str(css_rel_path)}', shell=True)
|
|
|
|
|
|
|
|
def do_exit(self, args):
|
|
|
|
"""
|
|
|
|
Exit script
|
|
|
|
"""
|
|
|
|
exit(0)
|
|
|
|
|
|
|
|
|
|
|
|
|
2021-02-07 18:32:07 +00:00
|
|
|
def test():
|
|
|
|
os.environ['GOOGLE_APPLICATION_CREDENTIALS'] = 'service.json'
|
2021-02-08 00:56:11 +00:00
|
|
|
subprocess.check_call("python -u -m unittest discover -s tests", shell=True)
|
2020-06-30 16:38:06 +01:00
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
console = Admin()
|
|
|
|
if len(sys.argv) > 1:
|
|
|
|
console.onecmd(' '.join(sys.argv[1:]))
|
|
|
|
else:
|
|
|
|
console.cmdloop()
|