Skip to content

Commit

Permalink
chore: updates
Browse files Browse the repository at this point in the history
  • Loading branch information
adamstankiewicz committed Jun 6, 2024
1 parent 0f7549c commit 3e2f22e
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 49 deletions.
15 changes: 11 additions & 4 deletions tubular/scripts/frontend_deploy.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@


@click.command("frontend_deploy")
@click.option(
'--common-config-file',
help='File from which common configuration variables are read.',
)
@click.option(
'--env-config-file',
help='File from which to read environment configuration variables.',
Expand All @@ -40,25 +44,28 @@
is_flag=True,
help='Boolean to decide if Cloudflare cache needs to be purged or not.',
)
def frontend_deploy(env_config_file, app_name, app_dist, purge_cache):
def frontend_deploy(common_config_file, env_config_file, app_name, app_dist, purge_cache):
"""
Copies a frontend application to an s3 bucket.
Args:
common_config_file (str): Path to a YAML file containing common configuration variables.
env_config_file (str): Path to a YAML file containing environment configuration variables.
app_name (str): Name of the frontend app.
app_dist (str): Path to frontend application dist directory.
purge_cache (bool): Should Cloudflare cache needs to be purged.
"""

if not env_config_file:
FAIL(1, 'Environment config file was not specified.')
if not app_name:
FAIL(1, 'Frontend application name was not specified.')
if not app_dist:
FAIL(1, 'Frontend application dist path was not specified.')
if not common_config_file:
FAIL(1, 'Common config file was not specified.')
if not env_config_file:
FAIL(1, 'Environment config file was not specified.')

deployer = FrontendDeployer(env_config_file, app_name)
deployer = FrontendDeployer(common_config_file, env_config_file, app_name)
bucket_name = deployer.env_cfg.get('S3_BUCKET_NAME')
if not bucket_name:
FAIL(1, 'No S3 bucket name configured for {}.'.format(app_name))
Expand Down
120 changes: 75 additions & 45 deletions tubular/scripts/frontend_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,26 @@
MAX_TRIES = 3


class FrontendBuilder:
""" Utility class for building frontends. """
SCRIPT_SHORTNAME = 'Build frontend'
LOG = partial(_log, SCRIPT_SHORTNAME)
FAIL = partial(_fail, SCRIPT_SHORTNAME)

def __init__(self, common_config_file, env_config_file, app_name, version_file):
class FrontendUtils:
"""
Base class for frontend utilities used by both buildig and deploying frontends.
"""

def __init__(self, common_config_file, env_config_file, app_name):
self.common_config_file = common_config_file
self.env_config_file = env_config_file
self.app_name = app_name
self.version_file = version_file
self.common_cfg, self.env_cfg = self._get_configs()

def FAIL(self):
""" Placeholder for failure method """
raise NotImplementedError

def LOG(self):
""" Placeholder for logging method """
raise NotImplementedError

def _get_configs(self):
""" Loads configs from their paths """
try:
Expand All @@ -55,6 +62,38 @@ def _get_configs(self):

return (common_vars, env_vars)

def get_app_config(self):
""" Combines the common and environment configs APP_CONFIG data """
app_config = self.common_cfg.get('APP_CONFIG', {})
app_config.update(self.env_cfg.get('APP_CONFIG', {}))
app_config['APP_VERSION'] = self.get_version_commit_sha()
if not app_config:
self.LOG('Config variables do not exist for app {}.'.format(self.app_name))
return app_config

def get_version_commit_sha(self):
""" Returns the commit SHA of the current HEAD """
return LocalGitAPI(Repo(self.app_name)).get_head_sha()


class FrontendBuilder(FrontendUtils):
""" Utility class for building frontends. """

SCRIPT_SHORTNAME = 'Build frontend'
LOG = partial(_log, SCRIPT_SHORTNAME)
FAIL = partial(_fail, SCRIPT_SHORTNAME)

def __init__(self, common_config_file, env_config_file, app_name, version_file):
"""
TODO
"""
super().__init__(
common_config_file=common_config_file,
env_config_file=env_config_file,
app_name=app_name,
)
self.version_file = version_file

def install_requirements(self):
""" Install requirements for app to build """
proc = subprocess.Popen(['npm install'], cwd=self.app_name, shell=True)
Expand Down Expand Up @@ -104,15 +143,6 @@ def install_requirements_npm_private(self):
install_list, self.app_name
))

def get_app_config(self):
""" Combines the common and environment configs APP_CONFIG data """
app_config = self.common_cfg.get('APP_CONFIG', {})
app_config.update(self.env_cfg.get('APP_CONFIG', {}))
app_config['APP_VERSION'] = self.get_version_commit_sha()
if not app_config:
self.LOG('Config variables do not exist for app {}.'.format(self.app_name))
return app_config

def get_npm_aliases_config(self):
""" Combines the common and environment configs NPM_ALIASES data """
npm_aliases_config = self.common_cfg.get('NPM_ALIASES', {})
Expand Down Expand Up @@ -140,10 +170,6 @@ def build_app(self, env_vars, fail_msg):
if build_return_code != 0:
self.FAIL(1, fail_msg)

def get_version_commit_sha(self):
""" Returns the commit SHA of the current HEAD """
return LocalGitAPI(Repo(self.app_name)).get_head_sha()

def create_version_file(self):
""" Creates a version.json file to be deployed with frontend """
# Add version.json file to build.
Expand Down Expand Up @@ -176,27 +202,13 @@ def copy_js_config_file_to_app_root(self, app_config, app_name):
self.FAIL(1, f"Could not copy '{source}' to '{destination}', due to destination not writable.")


class FrontendDeployer:
class FrontendDeployer(FrontendUtils):
""" Utility class for deploying frontends. """

SCRIPT_SHORTNAME = 'Deploy frontend'
LOG = partial(_log, SCRIPT_SHORTNAME)
FAIL = partial(_fail, SCRIPT_SHORTNAME)

def __init__(self, env_config_file, app_name):
self.env_config_file = env_config_file
self.app_name = app_name
self.env_cfg = self._get_config()

def _get_config(self):
""" Loads config from its path """
try:
with io.open(self.env_config_file, 'r') as contents:
env_vars = yaml.safe_load(contents)
except IOError:
self.FAIL(1, 'Environment config file {} could not be opened.'.format(self.env_config_file))
return env_vars

def _deploy_to_s3(self, bucket_name, app_path):
""" Deploy files to S3 bucket. """
bucket_uri = 's3://{}'.format(bucket_name)
Expand All @@ -209,20 +221,38 @@ def _deploy_to_s3(self, bucket_name, app_path):
self.FAIL(1, 'Could not sync app {} with S3 bucket {}.'.format(self.app_name, bucket_uri))

def _upload_js_sourcemaps(self, app_path):
""" Upload sourcemaps to Datadog. """
head_commit_sha = LocalGitAPI(Repo(self.app_name)).get_head_sha()
# Prioritize app-specific version override, if any, before default commit SHA version
version = self.env_cfg.get('DATADOG_VERSION') or head_commit_sha
args = ' '.join([
f'--service="{self.env_cfg.get('DATADOG_SERVICE')}"',
""" Upload JavaScript sourcemaps to Datadog. """
app_config = self.get_app_config()
api_key = app_config.get('DATADOG_API_KEY')
if not api_key:
# Can't upload source maps without ``DATADOG_API_KEY``, which must be set as an environment variable
# before executing the Datadog CLI. The Datadog documentation suggests using a dedicated Datadog API key:
# https://docs.datadoghq.com/real_user_monitoring/guide/upload-javascript-source-maps/
self.LOG(1, 'Could not find Datadog API key while uploading source maps.')
return

# Prioritize app-specific version override, if any, before default APP_VERSION commit SHA version
version = app_config.get('DATADOG_VERSION') or app_config.get('APP_VERSION')
if not version:
self.LOG(1, 'Could not find version for app {} while uploading source maps.'.format(self.app_name))
return

env_vars = ' '.join([
f'DATADOG_API_KEY={api_key}',
])
command_args = ' '.join([
f'--service="{app_config.get('DATADOG_SERVICE')}"',
f'--release-version="{version}"'
'--minified-path-prefix="/"', # Sourcemaps are relative to the root when deployed
])
# TODO: ``DATADOG_API_KEY`` must be set as an environment variable before executing the following
# command. The Datadog documentation suggests using a dedicated Datadog API key:
# https://docs.datadoghq.com/real_user_monitoring/guide/upload-javascript-source-maps/
self.LOG('Uploading source maps to Datadog for app {}.'.format(self.app_name))
proc = subprocess.Popen(
' '.join(['datadog-ci sourcemaps upload', app_path, args]),
' '.join([
env_vars,
'./node_modules/.bin/datadog-ci sourcemaps upload',
app_path,
command_args,
]),
cwd=self.app_name,
shell=True,
)
Expand Down

0 comments on commit 3e2f22e

Please sign in to comment.