Skip to content

Commit

Permalink
Merge pull request #23 from wagtail-examples/feature/manual-poetry-ve…
Browse files Browse the repository at this point in the history
…rsion

Quick fix for unexpected Dockerfile content
  • Loading branch information
nickmoreton authored Jul 14, 2024
2 parents bfc6c0f + 91c0ef1 commit d9d7cab
Show file tree
Hide file tree
Showing 6 changed files with 162 additions and 9 deletions.
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

0 comments on commit d9d7cab

Please sign in to comment.