Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Quick fix for unexpected Dockerfile content #23

Merged
merged 2 commits into from
Jul 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 17 additions & 6 deletions src/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,16 +80,27 @@ def start(repo_url, report):
repository_manager.dockerfile_path = str(pathlib.Path(docker_files[0]).absolute())

repository_manager.parse_docker_image()
docker_image = repository_manager.docker_image

if not docker_image:
console.print("Unable to determine the base image from the Dockerfile.", style="red1")
docker_image = click.prompt("Enter a valid Docker image to use e.g. python:3.11", type=str)

repository_manager.parse_poetry_version()
poetry_version = repository_manager.poetry_version

if not poetry_version:
console.print("Unable to determine the poetry version from the Dockerfile.", style="red1")
poetry_version = click.prompt("Enter a valid Poetry version to use e.g. 1.8.2", type=str)

table = Table(title="Repository Information", box=box.MARKDOWN, show_lines=True)
table.add_column("", style="bright_white")
table.add_column("Details", style="bright_white")
table.add_row("Repository URL", repository_manager.repo_url)
table.add_row("Branch Name", repository_manager.get_branch())
table.add_row("Dockerfile Path", repository_manager.dockerfile_path)
table.add_row("Poetry Version", repository_manager.poetry_version)
table.add_row("Docker Image", repository_manager.docker_image)
table.add_row("Poetry Version", poetry_version)
table.add_row("Docker Image", docker_image)
console.print(table)

process = click.confirm("Do you want to continue with the above details?", default=True)
Expand All @@ -102,8 +113,8 @@ def start(repo_url, report):

# run the docker image
docker = DockerManager(
repository_manager.docker_image,
repository_manager.poetry_version,
docker_image,
poetry_version,
repository_manager.get_repo_dir,
)

Expand Down Expand Up @@ -134,8 +145,8 @@ def start(repo_url, report):
table.add_row("Repository URL", repository_manager.repo_url)
table.add_row("Branch Name", repository_manager.get_branch())
table.add_row("Dockerfile Path", repository_manager.dockerfile_path)
table.add_row("Poetry Version", repository_manager.poetry_version)
table.add_row("Docker Image", repository_manager.docker_image)
table.add_row("Poetry Version", poetry_version)
table.add_row("Docker Image", docker_image)
console.print(table)

if report:
Expand Down
12 changes: 10 additions & 2 deletions src/managers/repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ def parse_docker_image(self):
pattern = r"^FROM.*?(python:?\d*\.*\d*\.*?).*"
# result would be python:3.9

python_image = None

for line in content:

if line.startswith("#"):
Expand All @@ -104,9 +106,11 @@ def parse_docker_image(self):

if match:
image = match.group(1)
self.docker_image = image
python_image = image
break

self.docker_image = python_image

def parse_poetry_version(self):
with open(self.dockerfile_path) as f:
content = f.read().split("\n")
Expand All @@ -115,6 +119,8 @@ def parse_poetry_version(self):
pattern = r"^ARG.*?POETRY_VERSION=(.*)"
# result would be 1.4.2

poetry_version = None

for line in content:

if line.startswith("#"):
Expand All @@ -124,4 +130,6 @@ def parse_poetry_version(self):

if match:
version = match.group(1)
self.poetry_version = version
poetry_version = version

self.poetry_version = poetry_version
5 changes: 4 additions & 1 deletion src/parsers/docker.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,13 @@ def get_poetry_version(self):
pattern = r"^ARG POETRY_VERSION=(.*?)"
# result would be ARG POETRY_VERSION=1.4.2

poetry_version = None

for line in content:
if line.startswith("#"):
continue
match = re.search(pattern, line)
if match:
poetry_version = line.split("=")[1].strip()
return poetry_version

return poetry_version
19 changes: 19 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@ def typical_dockerfile_content():
return f.read()


@pytest.fixture
def unexpected_dockerfile_content():
"""Return the content of a typical Dockerfile for testing"""

with open(base_dir / "tests/test_files/DockerfileNoPoetry", "r") as f:
return f.read()


@pytest.fixture
def dockerfile_fixture(typical_dockerfile_content, tmp_path):
"""Create a dockerfile with content for testing"""
Expand All @@ -26,6 +34,17 @@ def dockerfile_fixture(typical_dockerfile_content, tmp_path):
return dockerfile


@pytest.fixture
def dockerfile_fixture_no_poetry(unexpected_dockerfile_content, tmp_path):
"""Create a dockerfile with content for testing"""

dockerfile = tmp_path / "Dockerfile"
lines = unexpected_dockerfile_content.split("\n")
dockerfile.write_text("\n".join(lines))

return dockerfile


@pytest.fixture
def poetry_lock_content():
"""Return the content of a typical poetry.lock for testing"""
Expand Down
103 changes: 103 additions & 0 deletions tests/test_files/DockerfileNoPoetry
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
#
# A typical Dockerfile for a Django app using Poetry for dependency management
# This image is never run it only a template for testing purposes
# The only parts needed from this are the python image and poetry version
#

FROM node:18-slim as frontend

ENV NODE_OPTIONS=--openssl-legacy-provider

ARG CI=true

COPY package.json package-lock.json tsconfig.json webpack.config.js tailwind.config.js ./
RUN npm ci --no-optional --no-audit --progress=false


COPY ./appname/ ./appname/
RUN npm run build:prod


# This is the production image. It is optimized for runtime performance and
FROM python:3.11-slim-bullseye as production

ARG POETRY_INSTALL_ARGS="--no-dev"

ENV VIRTUAL_ENV=/venv

RUN useradd newuser --create-home && mkdir /app $VIRTUAL_ENV && chown -R newuser /app $VIRTUAL_ENV

WORKDIR /app

# ENV PATH=$VIRTUAL_ENV/bin:$PATH \
# POETRY_INSTALL_ARGS=${POETRY_INSTALL_ARGS} \
# PYTHONUNBUFFERED=1 \
# DJANGO_SETTINGS_MODULE=appname.settings.production \
# PORT=8000 \
# WEB_CONCURRENCY=3 \
# GUNICORN_CMD_ARGS="-c gunicorn-conf.py --max-requests 1200 --max-requests-jitter 50 --access-logfile - --timeout 25"

# ARG BUILD_ENV
# ENV BUILD_ENV=${BUILD_ENV}

EXPOSE 8000

# RUN apt-get update --yes --quiet && apt-get install --yes --quiet --no-install-recommends \
# build-essential \
# libpq-dev \
# curl \
# git \
# && apt-get autoremove && rm -rf /var/lib/apt/lists/*


# RUN apt-get update --yes --quiet && apt-get install -y binutils libproj-dev gdal-bin

# RUN pip install --no-cache poetry==${POETRY_VERSION}

USER newuser

# Install your app's Python requirements.
# RUN python -m venv $VIRTUAL_ENV
# COPY --chown=sueryder pyproject.toml poetry.lock ./
# RUN pip install --no-cache --upgrade pip && poetry install ${POETRY_INSTALL_ARGS} --no-root --extras gunicorn && rm -rf $HOME/.cache

# COPY --chown=sueryder --from=frontend ./sueryder/static_compiled ./sueryder/static_compiled

# Copy application code.
COPY --chown=newuser . .

# Run poetry install again to install our project (so the the sueryder package is always importable)
# RUN poetry install ${POETRY_INSTALL_ARGS}

# Collect static. This command will move static files from application
# directories and "static_compiled" folder to the main static directory that
# will be served by the WSGI server.
# RUN SECRET_KEY=none python manage.py collectstatic --noinput --clear

# Load shortcuts
# COPY ./docker/bashrc.sh /home/sueryder/.bashrc

# Run the WSGI server. It reads GUNICORN_CMD_ARGS, PORT and WEB_CONCURRENCY
# environment variable hence we don't specify a lot options below.
CMD gunicorn sueryder.wsgi:application



FROM production as dev

USER root

RUN apt-get update --yes --quiet && apt-get install -y postgresql-client

USER newuser

ENV NODE_OPTIONS=--openssl-legacy-provider

ARG NVM_VERSION=0.39.5
COPY --chown=sueryder .nvmrc ./
RUN curl https://raw.githubusercontent.com/nvm-sh/nvm/v${NVM_VERSION}/install.sh | bash \
&& bash --login -c "nvm install --no-progress && nvm alias default $(nvm run --silent --version)"

COPY --chown=newuser --from=frontend ./node_modules ./node_modules

CMD tail -f /dev/null
9 changes: 9 additions & 0 deletions tests/test_parsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,15 @@ def test_match_docker_poetry_version(dockerfile_fixture):
assert poetry_version == "1.3.2"


def test_match_docker_poetry_not_found(dockerfile_fixture_no_poetry):
"""Test that the parser returns None if poetry is not found"""

parser = DockerFileParser(dockerfile_fixture_no_poetry)
poetry_version = parser.get_poetry_version()

assert poetry_version is None


def test_docker_parser_file_error():
"""Test that the parser can handle a file not found error"""

Expand Down
Loading