ci: use reusable golden images
Some checks failed
CI/CD Pipeline / Manual Action Help (push) Has been skipped
CI/CD Pipeline / Build Golden Images (push) Has been skipped
CI/CD Pipeline / Start Dev Containers in Dokploy (push) Has been skipped
CI/CD Pipeline / Drop and Recreate Dev Database (push) Has been skipped
CI/CD Pipeline / Quality Gate (push) Failing after 7s
CI/CD Pipeline / Build and Push Images (push) Has been skipped
CI/CD Pipeline / Deploy Dev in Dokploy (push) Has been skipped
CI/CD Pipeline / Internal Notify (push) Successful in 1s
Some checks failed
CI/CD Pipeline / Manual Action Help (push) Has been skipped
CI/CD Pipeline / Build Golden Images (push) Has been skipped
CI/CD Pipeline / Start Dev Containers in Dokploy (push) Has been skipped
CI/CD Pipeline / Drop and Recreate Dev Database (push) Has been skipped
CI/CD Pipeline / Quality Gate (push) Failing after 7s
CI/CD Pipeline / Build and Push Images (push) Has been skipped
CI/CD Pipeline / Deploy Dev in Dokploy (push) Has been skipped
CI/CD Pipeline / Internal Notify (push) Successful in 1s
This commit is contained in:
@@ -14,7 +14,7 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
manual_action:
|
manual_action:
|
||||||
description: "Manual action: noop, cleanup_dev_database, or dokploy_start"
|
description: "Manual action: noop, build_golden_images, cleanup_dev_database, or dokploy_start"
|
||||||
required: true
|
required: true
|
||||||
default: "noop"
|
default: "noop"
|
||||||
dokploy_target:
|
dokploy_target:
|
||||||
@@ -37,6 +37,10 @@ env:
|
|||||||
REGISTRY_NAMESPACE: "${{ github.repository_owner }}"
|
REGISTRY_NAMESPACE: "${{ github.repository_owner }}"
|
||||||
WEB_IMAGE: "mostovik-backend-web"
|
WEB_IMAGE: "mostovik-backend-web"
|
||||||
CELERY_IMAGE: "mostovik-backend-celery"
|
CELERY_IMAGE: "mostovik-backend-celery"
|
||||||
|
CI_GOLDEN_IMAGE: "mostovik-backend-ci-golden"
|
||||||
|
WEB_GOLDEN_IMAGE: "mostovik-backend-web-golden"
|
||||||
|
CELERY_GOLDEN_IMAGE: "mostovik-backend-celery-golden"
|
||||||
|
GOLDEN_TAG: "py311-uv0.7.2"
|
||||||
DOKPLOY_DEV_WEB_WEBHOOK_URL: "https://deploy.dev.nii-ecos.ru/api/deploy/_EjfuYBpzGJ18uPwBZ3iF"
|
DOKPLOY_DEV_WEB_WEBHOOK_URL: "https://deploy.dev.nii-ecos.ru/api/deploy/_EjfuYBpzGJ18uPwBZ3iF"
|
||||||
DOKPLOY_DEV_WORKER_WEBHOOK_URL: "https://deploy.dev.nii-ecos.ru/api/deploy/hltL7K2HmG1a8EIzr-mVA"
|
DOKPLOY_DEV_WORKER_WEBHOOK_URL: "https://deploy.dev.nii-ecos.ru/api/deploy/hltL7K2HmG1a8EIzr-mVA"
|
||||||
DOKPLOY_DEV_BEAT_WEBHOOK_URL: "https://deploy.dev.nii-ecos.ru/api/deploy/RkdykbqU6faErrZBAN9Rv"
|
DOKPLOY_DEV_BEAT_WEBHOOK_URL: "https://deploy.dev.nii-ecos.ru/api/deploy/RkdykbqU6faErrZBAN9Rv"
|
||||||
@@ -63,9 +67,95 @@ jobs:
|
|||||||
echo "- manual_action=cleanup_dev_database"
|
echo "- manual_action=cleanup_dev_database"
|
||||||
echo "- cleanup_confirm=CLEAN_DEV_DB"
|
echo "- cleanup_confirm=CLEAN_DEV_DB"
|
||||||
echo "This drops and recreates the dev database, then triggers Dokploy web/worker/beat."
|
echo "This drops and recreates the dev database, then triggers Dokploy web/worker/beat."
|
||||||
|
echo "For base image refresh run with manual_action=build_golden_images."
|
||||||
echo "For Dokploy start run with manual_action=dokploy_start and dokploy_target=all|web|worker|beat."
|
echo "For Dokploy start run with manual_action=dokploy_start and dokploy_target=all|web|worker|beat."
|
||||||
} >> "${GITHUB_STEP_SUMMARY:-/dev/stdout}"
|
} >> "${GITHUB_STEP_SUMMARY:-/dev/stdout}"
|
||||||
|
|
||||||
|
build_golden_images:
|
||||||
|
name: Build Golden Images
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 60
|
||||||
|
if: |
|
||||||
|
github.event_name == 'workflow_dispatch' &&
|
||||||
|
github.ref == 'refs/heads/dev' &&
|
||||||
|
github.event.inputs.manual_action == 'build_golden_images'
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
REPO_URL=$(echo "${GITHUB_SERVER_URL}" | sed "s|://|://oauth2:${{ gitea.token }}@|")
|
||||||
|
BRANCH="${GITHUB_HEAD_REF:-${GITHUB_REF_NAME}}"
|
||||||
|
git -c core.hooksPath=/dev/null clone --depth=1 --branch="${BRANCH}" "${REPO_URL}/${GITHUB_REPOSITORY}.git" .
|
||||||
|
git -c core.hooksPath=/dev/null checkout "${GITHUB_SHA}"
|
||||||
|
|
||||||
|
- name: Build and push golden images
|
||||||
|
env:
|
||||||
|
GITEA_TOKEN: ${{ gitea.token }}
|
||||||
|
REGISTRY_USER: ${{ secrets.REGISTRY_USER }}
|
||||||
|
REGISTRY_PASSWORD: ${{ secrets.REGISTRY_TOKEN }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
REGISTRY_PATH="${REGISTRY_HOST}/${REGISTRY_NAMESPACE}"
|
||||||
|
CI_GOLDEN_REF="${REGISTRY_PATH}/${CI_GOLDEN_IMAGE}"
|
||||||
|
WEB_GOLDEN_REF="${REGISTRY_PATH}/${WEB_GOLDEN_IMAGE}"
|
||||||
|
CELERY_GOLDEN_REF="${REGISTRY_PATH}/${CELERY_GOLDEN_IMAGE}"
|
||||||
|
REGISTRY_USER="${REGISTRY_USER:-${GITHUB_ACTOR}}"
|
||||||
|
REGISTRY_PASSWORD="${REGISTRY_PASSWORD:-${GITEA_TOKEN:-}}"
|
||||||
|
|
||||||
|
unset http_proxy https_proxy HTTP_PROXY HTTPS_PROXY all_proxy ALL_PROXY
|
||||||
|
export NO_PROXY="${NO_PROXY:-},${REGISTRY_HOST}"
|
||||||
|
export no_proxy="${no_proxy:-},${REGISTRY_HOST}"
|
||||||
|
|
||||||
|
if [ -z "${REGISTRY_PASSWORD}" ]; then
|
||||||
|
echo "REGISTRY_TOKEN secret is not set and gitea.token fallback is empty" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "${REGISTRY_PASSWORD}" \
|
||||||
|
| docker login "${REGISTRY_HOST}" \
|
||||||
|
-u "${REGISTRY_USER}" \
|
||||||
|
--password-stdin
|
||||||
|
|
||||||
|
if ! docker buildx inspect mostovik-builder >/dev/null 2>&1; then
|
||||||
|
docker buildx create --name mostovik-builder --use
|
||||||
|
else
|
||||||
|
docker buildx use mostovik-builder
|
||||||
|
fi
|
||||||
|
docker buildx inspect --bootstrap
|
||||||
|
|
||||||
|
docker buildx prune --all --force || true
|
||||||
|
docker builder prune --all --force || true
|
||||||
|
|
||||||
|
build_golden() {
|
||||||
|
local target="$1"
|
||||||
|
local ref="$2"
|
||||||
|
local build_args=()
|
||||||
|
if [ "${target}" = "celery-deps-base" ]; then
|
||||||
|
build_args+=(--build-arg "GOLDEN_WEB_IMAGE=${WEB_GOLDEN_REF}:${GOLDEN_TAG}")
|
||||||
|
fi
|
||||||
|
docker buildx build \
|
||||||
|
-f ./docker/Dockerfile \
|
||||||
|
--target "${target}" \
|
||||||
|
"${build_args[@]}" \
|
||||||
|
--push \
|
||||||
|
-t "${ref}:${GOLDEN_TAG}" \
|
||||||
|
-t "${ref}:latest" \
|
||||||
|
.
|
||||||
|
}
|
||||||
|
|
||||||
|
build_golden "ci-deps-base" "${CI_GOLDEN_REF}"
|
||||||
|
build_golden "web-deps-base" "${WEB_GOLDEN_REF}"
|
||||||
|
build_golden "celery-deps-base" "${CELERY_GOLDEN_REF}"
|
||||||
|
|
||||||
|
{
|
||||||
|
echo "Golden images pushed:"
|
||||||
|
echo "- ${CI_GOLDEN_REF}:${GOLDEN_TAG}"
|
||||||
|
echo "- ${WEB_GOLDEN_REF}:${GOLDEN_TAG}"
|
||||||
|
echo "- ${CELERY_GOLDEN_REF}:${GOLDEN_TAG}"
|
||||||
|
} >> "${GITHUB_STEP_SUMMARY:-/dev/stdout}"
|
||||||
|
|
||||||
quality:
|
quality:
|
||||||
name: Quality Gate
|
name: Quality Gate
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -81,94 +171,74 @@ jobs:
|
|||||||
git -c core.hooksPath=/dev/null clone --depth=1 --branch="${BRANCH}" "${REPO_URL}/${GITHUB_REPOSITORY}.git" .
|
git -c core.hooksPath=/dev/null clone --depth=1 --branch="${BRANCH}" "${REPO_URL}/${GITHUB_REPOSITORY}.git" .
|
||||||
git -c core.hooksPath=/dev/null checkout "${GITHUB_SHA}"
|
git -c core.hooksPath=/dev/null checkout "${GITHUB_SHA}"
|
||||||
|
|
||||||
- name: Install Python and uv
|
- name: Run quality in golden image
|
||||||
|
env:
|
||||||
|
GITEA_TOKEN: ${{ gitea.token }}
|
||||||
|
REGISTRY_USER: ${{ secrets.REGISTRY_USER }}
|
||||||
|
REGISTRY_PASSWORD: ${{ secrets.REGISTRY_TOKEN }}
|
||||||
|
SKIP_LINT: ${{ contains(github.event.head_commit.message, '#no_lint') }}
|
||||||
|
SKIP_TEST: ${{ contains(github.event.head_commit.message, '#no_test') }}
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
rm -rf .venv .ci-bin .ci-python-env
|
|
||||||
CLEAN_PATH=""
|
|
||||||
IFS=: read -r -a PATH_PARTS <<< "${PATH}"
|
|
||||||
for path_part in "${PATH_PARTS[@]}"; do
|
|
||||||
case "${path_part}" in
|
|
||||||
.venv/bin|*/.venv/bin)
|
|
||||||
continue
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
CLEAN_PATH="${CLEAN_PATH:+${CLEAN_PATH}:}${path_part}"
|
|
||||||
done
|
|
||||||
export PATH="${CLEAN_PATH}"
|
|
||||||
hash -r
|
|
||||||
PROJECT_PYTHON_VERSION="$(cat .python-version 2>/dev/null || printf '%s' "${PYTHON_VERSION}")"
|
|
||||||
PYTHON_BIN="$(./scripts/ensure-ci-python.sh "${PROJECT_PYTHON_VERSION}")"
|
|
||||||
case "${PYTHON_BIN}" in
|
|
||||||
.venv/*|*/.venv/*)
|
|
||||||
echo "Refusing to use project virtualenv as base Python: ${PYTHON_BIN}" >&2
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
"${PYTHON_BIN}" --version
|
|
||||||
|
|
||||||
printf 'PYTHON_BIN=%s\n' "${PYTHON_BIN}" > .ci-python-env
|
REGISTRY_PATH="${REGISTRY_HOST}/${REGISTRY_NAMESPACE}"
|
||||||
mkdir -p .ci-bin
|
CI_GOLDEN_REF="${REGISTRY_PATH}/${CI_GOLDEN_IMAGE}"
|
||||||
if command -v uv >/dev/null 2>&1; then
|
REGISTRY_USER="${REGISTRY_USER:-${GITHUB_ACTOR}}"
|
||||||
cp "$(command -v uv)" .ci-bin/uv
|
REGISTRY_PASSWORD="${REGISTRY_PASSWORD:-${GITEA_TOKEN:-}}"
|
||||||
elif ! curl -LsSf --connect-timeout 5 --max-time 60 --retry 1 "https://astral.sh/uv/${UV_VERSION}/install.sh" | env UV_INSTALL_DIR="${PWD}/.ci-bin" sh; then
|
|
||||||
curl -LsSf --connect-timeout 5 --max-time 60 --retry 1 "https://astral.sh/uv/install.sh" | env UV_INSTALL_DIR="${PWD}/.ci-bin" sh
|
unset http_proxy https_proxy HTTP_PROXY HTTPS_PROXY all_proxy ALL_PROXY
|
||||||
|
export NO_PROXY="${NO_PROXY:-},${REGISTRY_HOST}"
|
||||||
|
export no_proxy="${no_proxy:-},${REGISTRY_HOST}"
|
||||||
|
|
||||||
|
if [ -z "${REGISTRY_PASSWORD}" ]; then
|
||||||
|
echo "REGISTRY_TOKEN secret is not set and gitea.token fallback is empty" >&2
|
||||||
|
exit 1
|
||||||
fi
|
fi
|
||||||
.ci-bin/uv --version
|
|
||||||
|
|
||||||
- name: Create virtual environment and install dependencies
|
echo "${REGISTRY_PASSWORD}" \
|
||||||
run: |
|
| docker login "${REGISTRY_HOST}" \
|
||||||
set -euo pipefail
|
-u "${REGISTRY_USER}" \
|
||||||
. ./.ci-python-env
|
--password-stdin
|
||||||
case "${PYTHON_BIN}" in
|
|
||||||
.venv/*|*/.venv/*)
|
|
||||||
echo "Refusing to create venv from project virtualenv: ${PYTHON_BIN}" >&2
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
"${PYTHON_BIN}" -m venv --without-pip .venv
|
|
||||||
. .venv/bin/activate
|
|
||||||
.ci-bin/uv sync \
|
|
||||||
--dev \
|
|
||||||
--frozen \
|
|
||||||
--active \
|
|
||||||
--python "${PWD}/.venv/bin/python" \
|
|
||||||
--no-managed-python \
|
|
||||||
--no-python-downloads
|
|
||||||
|
|
||||||
- name: Run Ruff linting
|
if ! docker buildx inspect mostovik-builder >/dev/null 2>&1; then
|
||||||
if: ${{ !contains(github.event.head_commit.message, '#no_lint') }}
|
docker buildx create --name mostovik-builder --use
|
||||||
run: |
|
else
|
||||||
set -euo pipefail
|
docker buildx use mostovik-builder
|
||||||
. .venv/bin/activate
|
fi
|
||||||
ruff check src
|
docker buildx inspect --bootstrap
|
||||||
|
|
||||||
- name: Run Ruff formatting check
|
if ! docker buildx imagetools inspect "${CI_GOLDEN_REF}:${GOLDEN_TAG}" >/dev/null 2>&1; then
|
||||||
if: ${{ !contains(github.event.head_commit.message, '#no_lint') }}
|
docker buildx prune --all --force || true
|
||||||
run: |
|
docker builder prune --all --force || true
|
||||||
set -euo pipefail
|
docker buildx build \
|
||||||
. .venv/bin/activate
|
-f ./docker/Dockerfile \
|
||||||
ruff format src --check
|
--target ci-deps-base \
|
||||||
|
--push \
|
||||||
|
-t "${CI_GOLDEN_REF}:${GOLDEN_TAG}" \
|
||||||
|
-t "${CI_GOLDEN_REF}:latest" \
|
||||||
|
.
|
||||||
|
fi
|
||||||
|
|
||||||
- name: Run regular pytest suite
|
docker run --rm \
|
||||||
if: ${{ !contains(github.event.head_commit.message, '#no_test') }}
|
-v "${PWD}:/workspace" \
|
||||||
env:
|
-w /workspace \
|
||||||
DJANGO_SETTINGS_MODULE: settings.test
|
-e DJANGO_SETTINGS_MODULE=settings.test \
|
||||||
SECRET_KEY: test-secret-key-for-ci
|
-e SECRET_KEY=test-secret-key-for-ci \
|
||||||
run: |
|
-e SKIP_LINT="${SKIP_LINT}" \
|
||||||
set -euo pipefail
|
-e SKIP_TEST="${SKIP_TEST}" \
|
||||||
export PYTHONPATH="${PWD}/src:${PYTHONPATH:-}"
|
"${CI_GOLDEN_REF}:${GOLDEN_TAG}" \
|
||||||
.venv/bin/python -m pytest tests --ignore=tests/test_api_inventory_e2e.py -q
|
bash -lc '
|
||||||
|
set -euo pipefail
|
||||||
- name: Run API inventory pytest suite
|
export PYTHONPATH="/workspace/src:${PYTHONPATH:-}"
|
||||||
if: ${{ !contains(github.event.head_commit.message, '#no_test') }}
|
if [ "${SKIP_LINT}" != "true" ]; then
|
||||||
env:
|
ruff check src
|
||||||
DJANGO_SETTINGS_MODULE: settings.test
|
ruff format src --check
|
||||||
SECRET_KEY: test-secret-key-for-ci
|
fi
|
||||||
run: |
|
if [ "${SKIP_TEST}" != "true" ]; then
|
||||||
set -euo pipefail
|
python -m pytest tests --ignore=tests/test_api_inventory_e2e.py -q
|
||||||
export PYTHONPATH="${PWD}/src:${PYTHONPATH:-}"
|
python -m pytest tests/test_api_inventory_e2e.py -q
|
||||||
.venv/bin/python -m pytest tests/test_api_inventory_e2e.py -q
|
fi
|
||||||
|
'
|
||||||
|
|
||||||
build_push:
|
build_push:
|
||||||
name: Build and Push Images
|
name: Build and Push Images
|
||||||
@@ -218,6 +288,8 @@ jobs:
|
|||||||
REGISTRY_PATH="${REGISTRY_HOST}/${REGISTRY_NAMESPACE}"
|
REGISTRY_PATH="${REGISTRY_HOST}/${REGISTRY_NAMESPACE}"
|
||||||
WEB_REF="${REGISTRY_PATH}/${WEB_IMAGE}"
|
WEB_REF="${REGISTRY_PATH}/${WEB_IMAGE}"
|
||||||
CELERY_REF="${REGISTRY_PATH}/${CELERY_IMAGE}"
|
CELERY_REF="${REGISTRY_PATH}/${CELERY_IMAGE}"
|
||||||
|
WEB_GOLDEN_REF="${REGISTRY_PATH}/${WEB_GOLDEN_IMAGE}"
|
||||||
|
CELERY_GOLDEN_REF="${REGISTRY_PATH}/${CELERY_GOLDEN_IMAGE}"
|
||||||
REGISTRY_USER="${REGISTRY_USER:-${GITHUB_ACTOR}}"
|
REGISTRY_USER="${REGISTRY_USER:-${GITHUB_ACTOR}}"
|
||||||
REGISTRY_PASSWORD="${REGISTRY_PASSWORD:-${GITEA_TOKEN:-}}"
|
REGISTRY_PASSWORD="${REGISTRY_PASSWORD:-${GITEA_TOKEN:-}}"
|
||||||
|
|
||||||
@@ -255,15 +327,40 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
docker buildx inspect --bootstrap
|
docker buildx inspect --bootstrap
|
||||||
|
|
||||||
|
ensure_golden() {
|
||||||
|
local target="$1"
|
||||||
|
local ref="$2"
|
||||||
|
local build_args=()
|
||||||
|
if [ "${target}" = "celery-deps-base" ]; then
|
||||||
|
build_args+=(--build-arg "GOLDEN_WEB_IMAGE=${WEB_GOLDEN_REF}:${GOLDEN_TAG}")
|
||||||
|
fi
|
||||||
|
if docker buildx imagetools inspect "${ref}:${GOLDEN_TAG}" >/dev/null 2>&1; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
docker buildx prune --all --force || true
|
||||||
|
docker builder prune --all --force || true
|
||||||
|
docker buildx build \
|
||||||
|
-f ./docker/Dockerfile \
|
||||||
|
--target "${target}" \
|
||||||
|
"${build_args[@]}" \
|
||||||
|
--push \
|
||||||
|
-t "${ref}:${GOLDEN_TAG}" \
|
||||||
|
-t "${ref}:latest" \
|
||||||
|
.
|
||||||
|
}
|
||||||
|
|
||||||
|
ensure_golden "web-deps-base" "${WEB_GOLDEN_REF}"
|
||||||
|
ensure_golden "celery-deps-base" "${CELERY_GOLDEN_REF}"
|
||||||
|
|
||||||
docker buildx build \
|
docker buildx build \
|
||||||
-f ./docker/Dockerfile \
|
-f ./docker/Dockerfile \
|
||||||
--target runtime-web \
|
--target runtime-web \
|
||||||
--build-arg INSTALL_DEV=false \
|
--build-arg INSTALL_DEV=false \
|
||||||
|
--build-arg GOLDEN_WEB_IMAGE="${WEB_GOLDEN_REF}:${GOLDEN_TAG}" \
|
||||||
--label "org.opencontainers.image.revision=${GITHUB_SHA}" \
|
--label "org.opencontainers.image.revision=${GITHUB_SHA}" \
|
||||||
--label "org.opencontainers.image.source=${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}" \
|
--label "org.opencontainers.image.source=${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}" \
|
||||||
--output type=image,push=true,oci-mediatypes=false \
|
--push \
|
||||||
--provenance=false \
|
|
||||||
--sbom=false \
|
|
||||||
"${WEB_TAGS[@]}" \
|
"${WEB_TAGS[@]}" \
|
||||||
.
|
.
|
||||||
|
|
||||||
@@ -271,16 +368,18 @@ jobs:
|
|||||||
-f ./docker/Dockerfile \
|
-f ./docker/Dockerfile \
|
||||||
--target runtime-celery \
|
--target runtime-celery \
|
||||||
--build-arg INSTALL_DEV=false \
|
--build-arg INSTALL_DEV=false \
|
||||||
|
--build-arg GOLDEN_CELERY_IMAGE="${CELERY_GOLDEN_REF}:${GOLDEN_TAG}" \
|
||||||
--label "org.opencontainers.image.revision=${GITHUB_SHA}" \
|
--label "org.opencontainers.image.revision=${GITHUB_SHA}" \
|
||||||
--label "org.opencontainers.image.source=${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}" \
|
--label "org.opencontainers.image.source=${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}" \
|
||||||
--output type=image,push=true,oci-mediatypes=false \
|
--push \
|
||||||
--provenance=false \
|
|
||||||
--sbom=false \
|
|
||||||
"${CELERY_TAGS[@]}" \
|
"${CELERY_TAGS[@]}" \
|
||||||
.
|
.
|
||||||
|
|
||||||
{
|
{
|
||||||
echo "Registry API: ${REGISTRY_API_URL}"
|
echo "Registry API: ${REGISTRY_API_URL}"
|
||||||
|
echo "Golden images:"
|
||||||
|
echo "- ${WEB_GOLDEN_REF}:${GOLDEN_TAG}"
|
||||||
|
echo "- ${CELERY_GOLDEN_REF}:${GOLDEN_TAG}"
|
||||||
echo "Pushed images:"
|
echo "Pushed images:"
|
||||||
echo "- ${WEB_REF}:${BRANCH_TAG}"
|
echo "- ${WEB_REF}:${BRANCH_TAG}"
|
||||||
echo "- ${WEB_REF}:${BRANCH_TAG}-${SHA_SHORT}"
|
echo "- ${WEB_REF}:${BRANCH_TAG}-${SHA_SHORT}"
|
||||||
|
|||||||
@@ -46,13 +46,6 @@ repos:
|
|||||||
- id: check-merge-conflict
|
- id: check-merge-conflict
|
||||||
- id: detect-private-key
|
- id: detect-private-key
|
||||||
|
|
||||||
- repo: https://github.com/hadolint/hadolint
|
|
||||||
rev: v2.12.0
|
|
||||||
hooks:
|
|
||||||
- id: hadolint-docker
|
|
||||||
name: hadolint dockerfiles
|
|
||||||
files: ^docker/.*Dockerfile.*$
|
|
||||||
|
|
||||||
- repo: https://github.com/shellcheck-py/shellcheck-py
|
- repo: https://github.com/shellcheck-py/shellcheck-py
|
||||||
rev: v0.11.0.1
|
rev: v0.11.0.1
|
||||||
hooks:
|
hooks:
|
||||||
@@ -61,6 +54,11 @@ repos:
|
|||||||
|
|
||||||
- repo: local
|
- repo: local
|
||||||
hooks:
|
hooks:
|
||||||
|
- id: hadolint-docker
|
||||||
|
name: hadolint dockerfiles
|
||||||
|
entry: bash -c 'for file in "$@"; do docker run --rm -i ghcr.io/hadolint/hadolint:latest hadolint - < "$file"; done' --
|
||||||
|
language: system
|
||||||
|
files: ^docker/.*Dockerfile.*$
|
||||||
- id: django-check-migrations
|
- id: django-check-migrations
|
||||||
name: django check migrations
|
name: django check migrations
|
||||||
entry: ./scripts/check-migrations.sh
|
entry: ./scripts/check-migrations.sh
|
||||||
|
|||||||
@@ -1,4 +1,11 @@
|
|||||||
FROM python:3.11-slim-bookworm AS base
|
ARG PYTHON_IMAGE=python:3.11-slim-bookworm
|
||||||
|
ARG UV_IMAGE=ghcr.io/astral-sh/uv:0.7.2
|
||||||
|
ARG GOLDEN_WEB_IMAGE=web-deps-base
|
||||||
|
ARG GOLDEN_CELERY_IMAGE=celery-deps-base
|
||||||
|
|
||||||
|
FROM ${UV_IMAGE} AS uv-bin
|
||||||
|
|
||||||
|
FROM ${PYTHON_IMAGE} AS base
|
||||||
|
|
||||||
ENV PYTHONDONTWRITEBYTECODE=1 \
|
ENV PYTHONDONTWRITEBYTECODE=1 \
|
||||||
PYTHONUNBUFFERED=1 \
|
PYTHONUNBUFFERED=1 \
|
||||||
@@ -11,13 +18,10 @@ WORKDIR /app
|
|||||||
|
|
||||||
RUN groupadd -r appgroup && useradd -r -g appgroup -m appuser
|
RUN groupadd -r appgroup && useradd -r -g appgroup -m appuser
|
||||||
|
|
||||||
# Install uv binary.
|
COPY --from=uv-bin /uv /uvx /usr/local/bin/
|
||||||
COPY --from=ghcr.io/astral-sh/uv:0.7.2 /uv /uvx /usr/local/bin/
|
|
||||||
|
|
||||||
|
|
||||||
FROM base AS builder
|
FROM base AS builder-base
|
||||||
|
|
||||||
ARG INSTALL_DEV=false
|
|
||||||
|
|
||||||
# hadolint ignore=DL3008
|
# hadolint ignore=DL3008
|
||||||
RUN apt-get update \
|
RUN apt-get update \
|
||||||
@@ -34,14 +38,29 @@ RUN apt-get update \
|
|||||||
|
|
||||||
COPY pyproject.toml uv.lock ./
|
COPY pyproject.toml uv.lock ./
|
||||||
|
|
||||||
RUN if [ "${INSTALL_DEV}" = "true" ]; then \
|
|
||||||
uv sync --frozen --no-install-project --dev; \
|
FROM builder-base AS prod-deps-base
|
||||||
else \
|
|
||||||
uv sync --frozen --no-install-project; \
|
RUN uv sync --frozen --no-install-project
|
||||||
fi
|
|
||||||
|
|
||||||
|
|
||||||
FROM base AS runtime-base
|
FROM builder-base AS ci-deps-build
|
||||||
|
|
||||||
|
RUN uv sync --frozen --no-install-project --dev
|
||||||
|
|
||||||
|
|
||||||
|
FROM base AS ci-deps-base
|
||||||
|
|
||||||
|
COPY --from=ci-deps-build /app/.venv /app/.venv
|
||||||
|
|
||||||
|
ENV PATH="/app/.venv/bin:${PATH}" \
|
||||||
|
PYTHONPATH=/workspace/src \
|
||||||
|
DJANGO_SETTINGS_MODULE=settings.test
|
||||||
|
|
||||||
|
|
||||||
|
FROM ci-deps-base AS web-deps-base
|
||||||
|
|
||||||
|
USER root
|
||||||
|
|
||||||
# hadolint ignore=DL3008
|
# hadolint ignore=DL3008
|
||||||
RUN apt-get update \
|
RUN apt-get update \
|
||||||
@@ -54,13 +73,8 @@ RUN apt-get update \
|
|||||||
zlib1g \
|
zlib1g \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
COPY --from=builder /app/.venv /app/.venv
|
|
||||||
COPY src/ ./src/
|
|
||||||
COPY docker/scripts/ ./docker/scripts/
|
|
||||||
|
|
||||||
RUN mkdir -p logs media staticfiles input/fns input/fns/processed input/fns/failed src/static \
|
RUN mkdir -p logs media staticfiles input/fns input/fns/processed input/fns/failed src/static \
|
||||||
&& chmod +x /app/docker/scripts/*.sh \
|
&& chown -R appuser:appgroup logs media staticfiles input src/static
|
||||||
&& chown -R appuser:appgroup /app
|
|
||||||
|
|
||||||
ENV PATH="/app/.venv/bin:${PATH}" \
|
ENV PATH="/app/.venv/bin:${PATH}" \
|
||||||
PYTHONPATH=/app/src \
|
PYTHONPATH=/app/src \
|
||||||
@@ -94,13 +108,7 @@ ENV PATH="/app/.venv/bin:${PATH}" \
|
|||||||
USER appuser
|
USER appuser
|
||||||
|
|
||||||
|
|
||||||
FROM runtime-base AS runtime-web
|
FROM ${GOLDEN_WEB_IMAGE} AS celery-deps-base
|
||||||
|
|
||||||
EXPOSE 8000
|
|
||||||
CMD ["/app/docker/scripts/start-web.sh"]
|
|
||||||
|
|
||||||
|
|
||||||
FROM runtime-base AS runtime-celery
|
|
||||||
|
|
||||||
USER root
|
USER root
|
||||||
|
|
||||||
@@ -129,7 +137,36 @@ RUN apt-get update \
|
|||||||
|
|
||||||
ENV PLAYWRIGHT_BROWSERS_PATH=/app/.playwright
|
ENV PLAYWRIGHT_BROWSERS_PATH=/app/.playwright
|
||||||
RUN python -m playwright install chromium \
|
RUN python -m playwright install chromium \
|
||||||
&& chown -R appuser:appgroup /app
|
&& chown -R appuser:appgroup /app/.playwright
|
||||||
|
|
||||||
|
USER appuser
|
||||||
|
|
||||||
|
|
||||||
|
FROM ${GOLDEN_WEB_IMAGE} AS runtime-web
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
USER root
|
||||||
|
COPY src/ ./src/
|
||||||
|
COPY docker/scripts/ ./docker/scripts/
|
||||||
|
RUN mkdir -p logs media staticfiles input/fns input/fns/processed input/fns/failed src/static \
|
||||||
|
&& chmod +x /app/docker/scripts/*.sh \
|
||||||
|
&& chown -R appuser:appgroup logs media staticfiles input src/static docker/scripts
|
||||||
|
|
||||||
|
USER appuser
|
||||||
|
|
||||||
|
EXPOSE 8000
|
||||||
|
CMD ["/app/docker/scripts/start-web.sh"]
|
||||||
|
|
||||||
|
|
||||||
|
FROM ${GOLDEN_CELERY_IMAGE} AS runtime-celery
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
USER root
|
||||||
|
COPY src/ ./src/
|
||||||
|
COPY docker/scripts/ ./docker/scripts/
|
||||||
|
RUN mkdir -p logs media staticfiles input/fns input/fns/processed input/fns/failed src/static \
|
||||||
|
&& chmod +x /app/docker/scripts/*.sh \
|
||||||
|
&& chown -R appuser:appgroup logs media staticfiles input src/static docker/scripts
|
||||||
|
|
||||||
USER appuser
|
USER appuser
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user