diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 2bdd0d8..f81c300 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -7,9 +7,6 @@ on: tags: - "v*.*.*" -permissions: - contents: write - jobs: push_to_pypi: runs-on: ubuntu-latest @@ -36,16 +33,27 @@ jobs: needs: push_to_pypi name: Push Docker image to Docker Hub runs-on: ubuntu-latest + permissions: + contents: write + packages: write + steps: - name: Check out the repo uses: actions/checkout@v4 - name: Log in to Docker Hub - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Extract metadata (tags, labels) for Docker id: meta uses: docker/metadata-action@v4 @@ -53,13 +61,16 @@ jobs: images: elementsinteractive/twyn - name: Build and push Docker image - uses: docker/build-push-action@v4 + uses: docker/build-push-action@v5 with: context: . file: ./Dockerfile push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} + cache-from: type=registry,ref=ghcr.io/elementsinteractive/twyn:buildcache + cache-to: type=registry,ref=ghcr.io/elementsinteractive/twyn:buildcache,mode=max + release_notes: runs-on: ubuntu-latest needs: push_to_docker_hub diff --git a/Dockerfile b/Dockerfile index 30c07fa..eb87a11 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,36 +1,49 @@ +ARG PYTHON_IMAGE=3.13-slim + # --------------- `base` stage --------------- -FROM python:3.13-slim AS base +FROM python:${PYTHON_IMAGE} AS base -# Define variables +# Define global values. Define them as ARG so they are not present in the final image, and so they can be modified ARG USER=twyn ARG GROUP=twyn ARG WORKDIR=/app ARG VENV_PATH=${WORKDIR}/.venv -# Set `WORKDIR` WORKDIR ${WORKDIR} # Create a non-root user and group RUN groupadd -g 1001 ${GROUP} && \ - useradd -m -u 1001 -g ${GROUP} -s /bin/bash ${USER} - -# Copy all the needed files, setting their user and group ownerships to the ones we just created -COPY --chown=${USER}:${GROUP} src src -COPY --chown=${USER}:${GROUP} pyproject.toml pyproject.toml -COPY --chown=${USER}:${GROUP} README.md README.md -COPY --chown=${USER}:${GROUP} poetry.lock poetry.lock + useradd -m -u 1001 -g ${GROUP} -s /bin/false ${USER} # --------------- `build` stage --------------- FROM base AS build +# Define stage variables +ARG POETRY_VERSION=2.1.2 +ARG POETRY_PLUGIN_EXPORT_VERSION=1.9.0 + +# These should never change, define as ENV +ENV POETRY_HOME="/opt/poetry" +ENV PATH="${POETRY_HOME}/bin:${PATH}" + +# Create venv and upgrade pip +RUN python -m venv ${VENV_PATH} && \ + ${VENV_PATH}/bin/pip install --no-cache-dir --upgrade pip + # Install poetry and set up the virtualenv configs -RUN pip install --upgrade pip && \ - pip install poetry && \ - poetry config virtualenvs.in-project true && \ - poetry config virtualenvs.path ${VENV_PATH} +RUN apt-get update && apt-get install -y curl \ + && curl -sSL https://install.python-poetry.org | POETRY_VERSION=$POETRY_VERSION python3 + +RUN poetry self add poetry-plugin-export==${POETRY_PLUGIN_EXPORT_VERSION} + +# Copy all the needed files, without write permissions +COPY poetry.lock pyproject.toml ./ -# Install `twyn` in the virtual environment -RUN poetry install --only main +# Export dependencies to requirements.txt (no dev deps) +RUN poetry export --without-hashes --only main -f requirements.txt > requirements.txt + +# Create and install dependencies in the virtual env +RUN ${VENV_PATH}/bin/pip install --no-cache-dir -r requirements.txt # --------------- `final` stage --------------- FROM base AS final @@ -39,8 +52,17 @@ FROM base AS final USER ${USER}:${GROUP} # Copy over the virtual environment with all its dependencies and the project installed -COPY --from=build --chown=${USER}:${GROUP} ${VENV_PATH} ${VENV_PATH} +COPY --from=build ${WORKDIR}/requirements.txt requirements.txt ENV PATH="${VENV_PATH}/bin:$PATH" -# Set `ENTRYPOINT` +# Copy venv with all its dependencies along with pyproject.toml +COPY --from=build --chown=${USER}:${GROUP} ${VENV_PATH} ${VENV_PATH} +COPY --from=build --chown=${USER}:${GROUP} ${WORKDIR}/pyproject.toml . + +# Copy source code +COPY src src + +# Install the CLI tool +RUN $VENV_PATH/bin/pip install --no-cache-dir . + ENTRYPOINT [ "twyn" ]