From 9bb1f9b78f7a799fbacc957b29d35c62767bb13e Mon Sep 17 00:00:00 2001 From: Ee Durbin Date: Tue, 24 Sep 2024 09:54:13 -0400 Subject: [PATCH 1/2] Support literal `=` in Procfile env settings Vendor https://github.com/smartmob-project/procfile for now, with https://github.com/smartmob-project/procfile/pull/9/files applied for now --- cabotage/celery/tasks/build.py | 4 +- cabotage/utils/procfile.py | 138 +++++++++++++++++++++++++++++++++ 2 files changed, 139 insertions(+), 3 deletions(-) create mode 100644 cabotage/utils/procfile.py diff --git a/cabotage/celery/tasks/build.py b/cabotage/celery/tasks/build.py index 2f7e25c..8f39113 100644 --- a/cabotage/celery/tasks/build.py +++ b/cabotage/celery/tasks/build.py @@ -17,8 +17,6 @@ TemporaryDirectory, ) -import procfile - from dockerfile_parse import DockerfileParser from flask import current_app from dxf import DXF @@ -50,6 +48,7 @@ from cabotage.utils.release_build_context import RELEASE_DOCKERFILE_TEMPLATE from cabotage.utils.github import post_deployment_status_update +from cabotage.utils import procfile Activity = activity_plugin.activity_cls @@ -691,7 +690,6 @@ def build_image_buildkit(image=None): "buildkitd.toml": buildkitd_toml, }, ) - print(buildctl_command, buildctl_args) job_object = kubernetes.client.V1Job( metadata=kubernetes.client.V1ObjectMeta( name=f"imagebuild-{image.build_job_id}", diff --git a/cabotage/utils/procfile.py b/cabotage/utils/procfile.py new file mode 100644 index 0000000..51b27d6 --- /dev/null +++ b/cabotage/utils/procfile.py @@ -0,0 +1,138 @@ +# -*- coding: utf-8 -*- + +"""Parser for Procfiles. + +Implements `Smartmob RFC 1 `_. + +Modified from https://github.com/smartmob-project/procfile/blob/338756d5b645f17aa2366c34afa3b7a58d880796/procfile/__init__.py + +Copyright (c) 2015 strawboss contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +* The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +""" + + +import re + + +_PROCFILE_LINE = re.compile( + "".join( + [ + r"^(?P.+?):\s*", + r"(?:env(?P(?:\s+.+?=.+?)+)\s+)?", + r"(?P.+)$", + ] + ) +) + + +def _find_duplicates(items): + seen = {} + duplicates = [] + for i, item in items: + if item in seen: + duplicates.append((i, item, seen[item])) + else: + seen[item] = i + return duplicates + + +def _group_lines(lines): + start, group = (0, []) + for i, line in enumerate(lines): + if line.rstrip().endswith("\\"): + group.append(line[:-1]) + else: + if group: + group.append(line.lstrip()) + else: + group.append(line) + yield start, "".join(group) + start, group = (i + 1, []) + if group: + yield start, "".join(group[:-1]) + group[-1].rstrip() + + +def _parse_procfile_line(line): + line = line.strip() + match = _PROCFILE_LINE.match(line) + if match is None: + raise ValueError('Invalid profile line "%s".' % line) + parts = match.groupdict() + environment = parts["environment"] + if environment: + environment = [ + tuple(variable.strip().split("=", 1)) + for variable in environment.strip().split(" ") + ] + else: + environment = [] + return ( + parts["process_type"], + parts["command"], + environment, + ) + + +def loads(content): + """Load a Procfile from a string.""" + lines = _group_lines(line for line in content.split("\n")) + lines = [(i, _parse_procfile_line(line)) for i, line in lines if line.strip()] + errors = [] + # Reject files with duplicate process types (no sane default). + duplicates = _find_duplicates(((i, line[0]) for i, line in lines)) + for i, process_type, j in duplicates: + errors.append( + "".join( + [ + 'Line %d: duplicate process type "%s": ', + "already appears on line %d.", + ] + ) + % (i + 1, process_type, j + 1) + ) + # Reject commands with duplicate variables (no sane default). + for i, line in lines: + process_type, env = line[0], line[2] + duplicates = _find_duplicates(((0, var[0]) for var in env)) + for _, variable, _ in duplicates: + errors.append( + "".join( + [ + 'Line %d: duplicate variable "%s" ', + 'for process type "%s".', + ] + ) + % (i + 1, variable, process_type) + ) + # Done! + if errors: + raise ValueError(errors) + return {k: {"cmd": cmd, "env": env} for _, (k, cmd, env) in lines} + + +def load(stream): + """Load a Procfile from a file-like object.""" + return loads(stream.read().decode("utf-8")) + + +def loadfile(path): + """Load a Procfile from a file.""" + with open(path, "rb") as stream: + return load(stream) From 756ebea9cd170f5559ac593fc48627d1bb24f85b Mon Sep 17 00:00:00 2001 From: Ee Durbin Date: Tue, 24 Sep 2024 09:55:50 -0400 Subject: [PATCH 2/2] remove dependency --- requirements/base.in | 1 - requirements/base.txt | 3 --- 2 files changed, 4 deletions(-) diff --git a/requirements/base.in b/requirements/base.in index b594548..f5a06ef 100644 --- a/requirements/base.in +++ b/requirements/base.in @@ -22,7 +22,6 @@ gunicorn hupper hvac kubernetes -procfile psycopg2 py-consul pygithub diff --git a/requirements/base.txt b/requirements/base.txt index c1a6cff..9b7e19d 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -561,9 +561,6 @@ passlib==1.7.4 \ --hash=sha256:aa6bca462b8d8bda89c70b382f0c298a20b5560af6cbfa2dce410c0a2fb669f1 \ --hash=sha256:defd50f72b65c5402ab2c573830a6978e5f202ad0d984793c8dde2c4152ebe04 # via flask-security-too -procfile==0.1.0 \ - --hash=sha256:1cdfff5702e455f72855c747ec8a7546a39f696e4334de0fd35e2a2072684919 - # via -r requirements/base.in prompt-toolkit==3.0.18 \ --hash=sha256:bf00f22079f5fadc949f42ae8ff7f05702826a97059ffcc6281036ad40ac6f04 \ --hash=sha256:e1b4f11b9336a28fa11810bc623c357420f69dfdb6d2dac41ca2c21a55c033bc