ci: publish DTF images and notify Portainer
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
name: Validate, publish and deploy
|
name: Build and deploy
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
@@ -8,102 +8,62 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
validate:
|
validate:
|
||||||
|
name: Validate source
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 15
|
timeout-minutes: 15
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
||||||
- name: Validate source and deployment definitions
|
- name: Run fast regression checks
|
||||||
run: |
|
run: |
|
||||||
python3 -m py_compile local/*.py deploy/*.py
|
python3 -m py_compile local/*.py deploy/*.py
|
||||||
python3 -m unittest local.test_dependency_lock local.test_staging_readiness deploy.test_production_preflight local.test_pricing -v
|
python3 -m unittest \
|
||||||
|
local.test_dependency_lock \
|
||||||
|
local.test_staging_readiness \
|
||||||
|
deploy.test_production_preflight \
|
||||||
|
local.test_pricing -v
|
||||||
sh -n local/lock_dependencies.sh
|
sh -n local/lock_dependencies.sh
|
||||||
docker compose config --quiet
|
|
||||||
docker compose -f compose.staging.yaml config --quiet
|
|
||||||
set -a
|
|
||||||
. deploy/portainer.env.example
|
|
||||||
set +a
|
|
||||||
docker stack config --compose-file deploy/stack.yaml >/dev/null
|
|
||||||
|
|
||||||
publish-and-deploy:
|
publish-and-deploy:
|
||||||
|
name: Publish images and notify Portainer
|
||||||
needs: validate
|
needs: validate
|
||||||
if: gitea.event_name == 'push' && gitea.ref == 'refs/heads/main'
|
if: gitea.event_name == 'push' && gitea.ref == 'refs/heads/main'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 75
|
timeout-minutes: 45
|
||||||
env:
|
|
||||||
COMPOSE_PROJECT_NAME: dtf-release-${{ gitea.run_number }}
|
|
||||||
REGISTRY_HOST: ${{ vars.REGISTRY_HOST }}
|
|
||||||
REGISTRY_OWNER: ${{ vars.REGISTRY_OWNER }}
|
|
||||||
PYTHON_BASE_IMAGE: ${{ vars.PYTHON_BASE_IMAGE }}
|
|
||||||
NGINX_BASE_IMAGE: ${{ vars.NGINX_BASE_IMAGE }}
|
|
||||||
TRIVY_IMAGE: ${{ vars.TRIVY_IMAGE }}
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
||||||
- name: Require production-capable application source
|
- name: Sign in to the Gitea Container Registry
|
||||||
run: python3 deploy/production_preflight.py --source-only
|
|
||||||
- name: Validate immutable build inputs
|
|
||||||
run: |
|
|
||||||
for value in "$PYTHON_BASE_IMAGE" "$NGINX_BASE_IMAGE" "$TRIVY_IMAGE"; do
|
|
||||||
echo "$value" | grep -Eq '^[a-z0-9][a-z0-9._:/-]*@sha256:[0-9a-f]{64}$'
|
|
||||||
echo "$value" | grep -Ev '@sha256:0{64}$' >/dev/null
|
|
||||||
done
|
|
||||||
case "$REGISTRY_HOST/$REGISTRY_OWNER" in *[A-Z]*|*' '*|'/'*) exit 2;; esac
|
|
||||||
- name: Run complete isolated regression suite
|
|
||||||
run: |
|
|
||||||
docker compose up --build -d --wait
|
|
||||||
python3 -m local.security_test
|
|
||||||
python3 -m local.scanning_test
|
|
||||||
python3 -m local.smoke_test
|
|
||||||
python3 -m local.workflow_test
|
|
||||||
docker compose exec -T api python -m local.runtime_security_test
|
|
||||||
docker compose exec -T api python -m local.retention_test
|
|
||||||
node local/browser_test.mjs
|
|
||||||
python3 -m local.backup create-and-verify
|
|
||||||
- name: Build production images
|
|
||||||
run: |
|
|
||||||
api_sha="$REGISTRY_HOST/$REGISTRY_OWNER/dtf-api:${{ gitea.sha }}"
|
|
||||||
api_latest="$REGISTRY_HOST/$REGISTRY_OWNER/dtf-api:latest"
|
|
||||||
web_sha="$REGISTRY_HOST/$REGISTRY_OWNER/dtf-web:${{ gitea.sha }}"
|
|
||||||
web_latest="$REGISTRY_HOST/$REGISTRY_OWNER/dtf-web:latest"
|
|
||||||
docker build --pull --file deploy/Dockerfile.api \
|
|
||||||
--build-arg PYTHON_BASE_IMAGE="$PYTHON_BASE_IMAGE" \
|
|
||||||
--build-arg VCS_REF="${{ gitea.sha }}" \
|
|
||||||
--tag "$api_sha" --tag "$api_latest" .
|
|
||||||
docker build --pull --file deploy/Dockerfile.web \
|
|
||||||
--build-arg PYTHON_BASE_IMAGE="$PYTHON_BASE_IMAGE" \
|
|
||||||
--build-arg NGINX_BASE_IMAGE="$NGINX_BASE_IMAGE" \
|
|
||||||
--build-arg VCS_REF="${{ gitea.sha }}" \
|
|
||||||
--tag "$web_sha" --tag "$web_latest" .
|
|
||||||
- name: Block secret, configuration and image findings
|
|
||||||
run: |
|
|
||||||
api="$REGISTRY_HOST/$REGISTRY_OWNER/dtf-api:${{ gitea.sha }}"
|
|
||||||
web="$REGISTRY_HOST/$REGISTRY_OWNER/dtf-web:${{ gitea.sha }}"
|
|
||||||
docker run --rm --volume "$PWD:/src:ro" --workdir /src "$TRIVY_IMAGE" \
|
|
||||||
fs --scanners secret --exit-code 1 --severity HIGH,CRITICAL .
|
|
||||||
docker run --rm --volume "$PWD:/src:ro" --workdir /src "$TRIVY_IMAGE" \
|
|
||||||
config --exit-code 1 --severity HIGH,CRITICAL deploy
|
|
||||||
docker run --rm --volume /var/run/docker.sock:/var/run/docker.sock:ro "$TRIVY_IMAGE" \
|
|
||||||
image --exit-code 1 --scanners vuln --severity HIGH,CRITICAL "$api"
|
|
||||||
docker run --rm --volume /var/run/docker.sock:/var/run/docker.sock:ro "$TRIVY_IMAGE" \
|
|
||||||
image --exit-code 1 --scanners vuln --severity HIGH,CRITICAL "$web"
|
|
||||||
- name: Publish latest and rollback tags
|
|
||||||
env:
|
env:
|
||||||
REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }}
|
REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }}
|
||||||
REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
|
REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
|
||||||
run: |
|
run: |
|
||||||
trap 'docker logout "$REGISTRY_HOST" >/dev/null 2>&1 || true' EXIT
|
test -n "$REGISTRY_USERNAME"
|
||||||
echo "$REGISTRY_TOKEN" | docker login "$REGISTRY_HOST" --username "$REGISTRY_USERNAME" --password-stdin
|
test -n "$REGISTRY_TOKEN"
|
||||||
docker push "$REGISTRY_HOST/$REGISTRY_OWNER/dtf-api:${{ gitea.sha }}"
|
echo "$REGISTRY_TOKEN" | docker login gitea.blyzer.com.br \
|
||||||
docker push "$REGISTRY_HOST/$REGISTRY_OWNER/dtf-api:latest"
|
--username "$REGISTRY_USERNAME" --password-stdin
|
||||||
docker push "$REGISTRY_HOST/$REGISTRY_OWNER/dtf-web:${{ gitea.sha }}"
|
- name: Build and publish API
|
||||||
docker push "$REGISTRY_HOST/$REGISTRY_OWNER/dtf-web:latest"
|
run: |
|
||||||
- name: Trigger the Portainer stack webhook
|
image="gitea.blyzer.com.br/blyzer/dtf-api"
|
||||||
|
docker build --pull --file deploy/Dockerfile.api \
|
||||||
|
--build-arg VCS_REF="${{ gitea.sha }}" \
|
||||||
|
--tag "$image:latest" --tag "$image:${{ gitea.sha }}" .
|
||||||
|
docker push "$image:latest"
|
||||||
|
docker push "$image:${{ gitea.sha }}"
|
||||||
|
- name: Build and publish web
|
||||||
|
run: |
|
||||||
|
image="gitea.blyzer.com.br/blyzer/dtf-web"
|
||||||
|
docker build --pull --file deploy/Dockerfile.web \
|
||||||
|
--build-arg VCS_REF="${{ gitea.sha }}" \
|
||||||
|
--tag "$image:latest" --tag "$image:${{ gitea.sha }}" .
|
||||||
|
docker push "$image:latest"
|
||||||
|
docker push "$image:${{ gitea.sha }}"
|
||||||
|
- name: Trigger Portainer redeployment
|
||||||
env:
|
env:
|
||||||
PORTAINER_WEBHOOK: ${{ secrets.PORTAINER_WEBHOOK }}
|
PORTAINER_WEBHOOK: ${{ secrets.PORTAINER_WEBHOOK }}
|
||||||
run: |
|
run: |
|
||||||
test -n "$PORTAINER_WEBHOOK"
|
if [ -z "$PORTAINER_WEBHOOK" ]; then
|
||||||
|
echo "PORTAINER_WEBHOOK is not configured; images were published but deployment was skipped."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
curl --fail --silent --show-error --max-time 30 --request POST "$PORTAINER_WEBHOOK"
|
curl --fail --silent --show-error --max-time 30 --request POST "$PORTAINER_WEBHOOK"
|
||||||
- name: Stop isolated test stack
|
|
||||||
if: always()
|
|
||||||
run: docker compose down --volumes --remove-orphans
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# syntax=docker/dockerfile:1
|
# syntax=docker/dockerfile:1
|
||||||
ARG PYTHON_BASE_IMAGE
|
ARG PYTHON_BASE_IMAGE=python:3.12-slim
|
||||||
FROM ${PYTHON_BASE_IMAGE}
|
FROM ${PYTHON_BASE_IMAGE}
|
||||||
|
|
||||||
ARG VCS_REF=unknown
|
ARG VCS_REF=unknown
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# syntax=docker/dockerfile:1
|
# syntax=docker/dockerfile:1
|
||||||
ARG PYTHON_BASE_IMAGE
|
ARG PYTHON_BASE_IMAGE=python:3.12-slim
|
||||||
ARG NGINX_BASE_IMAGE
|
ARG NGINX_BASE_IMAGE=nginx:1.28-alpine
|
||||||
FROM ${PYTHON_BASE_IMAGE} AS policy
|
FROM ${PYTHON_BASE_IMAGE} AS policy
|
||||||
WORKDIR /build
|
WORKDIR /build
|
||||||
COPY dtf-site.html /build/dtf-site.html
|
COPY dtf-site.html /build/dtf-site.html
|
||||||
|
|||||||
Reference in New Issue
Block a user