diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 7d534ec..7135aa3 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -19,19 +19,27 @@ jobs: run: echo ${GITHUB_TOKEN} | docker login -u ${GITHUB_ACTOR} --password-stdin ghcr.io env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Pull image + - name: Pull images run: | - docker pull ${{ env.IMAGE }}:latest || true - - name: Build image + docker pull ${{ env.IMAGE }}-builder:latest || true + docker pull ${{ env.IMAGE }}-final:latest || true + - name: Build images run: | docker build \ - --cache-from ${{ env.IMAGE }}:latest \ - --tag ${{ env.IMAGE }}:latest \ + --target builder \ + --cache-from ${{ env.IMAGE }}-builder:latest \ + --tag ${{ env.IMAGE }}-builder:latest \ + --file ./project/Dockerfile.prod \ + "./project" + docker build \ + --cache-from ${{ env.IMAGE }}-final:latest \ + --tag ${{ env.IMAGE }}-final:latest \ --file ./project/Dockerfile.prod \ "./project" - - name: Push image + - name: Push images run: | - docker push ${{ env.IMAGE }}:latest + docker push ${{ env.IMAGE }}-builder:latest + docker push ${{ env.IMAGE }}-final:latest test: name: Test Docker Image @@ -46,14 +54,21 @@ jobs: run: echo ${GITHUB_TOKEN} | docker login -u ${GITHUB_ACTOR} --password-stdin ghcr.io env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Pull image + - name: Pull images run: | - docker pull ${{ env.IMAGE }}:latest || true - - name: Build image + docker pull ${{ env.IMAGE }}-builder:latest || true + docker pull ${{ env.IMAGE }}-final:latest || true + - name: Build images run: | docker build \ - --cache-from ${{ env.IMAGE }}:latest \ - --tag ${{ env.IMAGE }}:latest \ + --target builder \ + --cache-from ${{ env.IMAGE }}-builder:latest \ + --tag ${{ env.IMAGE }}-builder:latest \ + --file ./project/Dockerfile.prod \ + "./project" + docker build \ + --cache-from ${{ env.IMAGE }}-final:latest \ + --tag ${{ env.IMAGE }}-final:latest \ --file ./project/Dockerfile.prod \ "./project" - name: Run container @@ -66,7 +81,9 @@ jobs: -e DATABASE_URL=sqlite://sqlite.db \ -e DATABASE_TEST_URL=sqlite://sqlite.db \ -p 5003:8765 \ - ${{ env.IMAGE }}:latest + ${{ env.IMAGE }}-final:latest + - name: Install requirements + run: docker exec fastapi-tdd pip install black==23.12.1 flake8==7.0.0 isort==5.13.2 pytest==7.4.4 - name: Pytest run: docker exec fastapi-tdd python -m pytest . - name: Flake8 @@ -92,13 +109,21 @@ jobs: run: echo ${GITHUB_TOKEN} | docker login -u ${GITHUB_ACTOR} --password-stdin ghcr.io env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Pull image + - name: Pull images run: | - docker pull ${{ env.IMAGE }}:latest || true - - name: Build image + docker pull ${{ env.IMAGE }}-builder:latest || true + docker pull ${{ env.IMAGE }}-final:latest || true + - name: Build images run: | docker build \ - --cache-from ${{ env.IMAGE }}:latest \ + --target builder \ + --cache-from ${{ env.IMAGE }}-builder:latest \ + --tag ${{ env.IMAGE }}-builder:latest \ + --file ./project/Dockerfile.prod \ + "./project" + docker build \ + --cache-from ${{ env.IMAGE }}-final:latest \ + --tag ${{ env.IMAGE }}:latest \ --tag ${{ env.HEROKU_REGISTRY_IMAGE }}:latest \ --file ./project/Dockerfile.prod \ "./project" @@ -107,7 +132,7 @@ jobs: env: HEROKU_AUTH_TOKEN: ${{ secrets.HEROKU_AUTH_TOKEN }} - name: Push to the registry - run: docker push ${{ env.HEROKU_REGISTRY_IMAGE }} + run: docker push ${{ env.HEROKU_REGISTRY_IMAGE }}:latest - name: Set environment variables run: | echo "HEROKU_REGISTRY_IMAGE=${{ env.HEROKU_REGISTRY_IMAGE }}" >> $GITHUB_ENV diff --git a/project/Dockerfile b/project/Dockerfile index b752128..cb8c488 100644 --- a/project/Dockerfile +++ b/project/Dockerfile @@ -16,7 +16,8 @@ RUN apt-get update \ # install python dependencies RUN pip install --upgrade pip COPY ./requirements.txt . -RUN pip install -r requirements.txt +COPY ./requirements-dev.txt . +RUN pip install -r requirements-dev.txt # add app COPY . . diff --git a/project/Dockerfile.prod b/project/Dockerfile.prod index 4015b19..3832eff 100644 --- a/project/Dockerfile.prod +++ b/project/Dockerfile.prod @@ -1,3 +1,39 @@ +########### +# BUILDER # +########### + +# pull official base image +FROM python:3.12.1-slim-bookworm as builder + +# install system dependencies +RUN apt-get update \ + && apt-get -y install netcat-traditional gcc postgresql \ + && apt-get clean + +# set work directory +WORKDIR /usr/src/app + +# set environment variables +ENV PYTHONDONTWRITEBYTECODE 1 +ENV PYTHONUNBUFFERED 1 + +# install dependencies +RUN pip install --upgrade pip +COPY ./requirements.txt . +RUN pip wheel --no-cache-dir --no-deps --wheel-dir /usr/src/app/wheels -r requirements.txt + +# lint +COPY . /usr/src/app/ +RUN pip install black==23.12.1 flake8==7.0.0 isort==5.13.2 +RUN flake8 . +RUN black --exclude=migrations . --check +RUN isort . --check-only + + +######### +# FINAL # +######### + # pull official base image FROM python:3.12.1-slim-bookworm @@ -25,16 +61,17 @@ RUN apt-get update \ && apt-get clean # install python dependencies +COPY --from=builder /usr/src/app/wheels /wheels +COPY --from=builder /usr/src/app/requirements.txt . RUN pip install --upgrade pip -COPY ./requirements.txt . -RUN pip install -r requirements.txt +RUN pip install --no-cache /wheels/* RUN pip install "uvicorn[standard]==0.26.0" # add app COPY . . # chown all the files to the app user -RUN chown -R app:app $APP_HOME +RUN chown -R app:app $HOME # change to the app user USER app diff --git a/project/app/api/ping.py b/project/app/api/ping.py index 223fb95..034e33a 100644 --- a/project/app/api/ping.py +++ b/project/app/api/ping.py @@ -8,7 +8,7 @@ @router.get("/ping") async def pong(settings: Settings = Depends(get_settings)): return { - "ping": "pong!!", + "ping": "pong!", "environment": settings.environment, "testing": settings.testing, } diff --git a/project/requirements-dev.txt b/project/requirements-dev.txt new file mode 100644 index 0000000..475b437 --- /dev/null +++ b/project/requirements-dev.txt @@ -0,0 +1,8 @@ +black==23.12.1 +flake8==7.0.0 +isort==5.13.2 +pytest==7.4.4 +pytest-cov==4.1.0 +pytest-xdist==3.5.0 + +-r requirements.txt diff --git a/project/requirements.txt b/project/requirements.txt index 13223a3..9176370 100644 --- a/project/requirements.txt +++ b/project/requirements.txt @@ -1,15 +1,9 @@ aerich==0.7.2 asyncpg==0.29.0 -black==23.12.1 fastapi==0.109.0 -flake8==7.0.0 gunicorn==21.0.1 httpx==0.26.0 -isort==5.13.2 newspaper3k==0.2.8 pydantic-settings==2.1.0 -pytest==7.4.4 -pytest-cov==4.1.0 -pytest-xdist==3.5.0 tortoise-orm==0.20.0 uvicorn==0.26.0 diff --git a/project/tests/test_ping.py b/project/tests/test_ping.py index fd0250d..ef72e0d 100644 --- a/project/tests/test_ping.py +++ b/project/tests/test_ping.py @@ -1,4 +1,4 @@ def test_ping(test_app): response = test_app.get("/ping") assert response.status_code == 200 - assert response.json() == {"environment": "dev", "ping": "pong!!", "testing": True} + assert response.json() == {"environment": "dev", "ping": "pong!", "testing": True}