- Fix ODOO_IMAGE from 17.0 to 19.0 to match deployment - Build image on 19.0 branch (not just main) - Add runboat_tag stage: tags image with branch slug and triggers runboat build via API so PRs get live test instances on runboat.itsulu.com Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
262 lines
8 KiB
YAML
262 lines
8 KiB
YAML
stages:
|
|
- lint
|
|
- test
|
|
- build
|
|
- e2e
|
|
- runboat
|
|
- notify
|
|
|
|
variables:
|
|
ODOO_IMAGE: odoo:19.0
|
|
POSTGRES_HOST: postgres
|
|
POSTGRES_PORT: 5432
|
|
POSTGRES_DB: odoo_test
|
|
POSTGRES_USER: odoo
|
|
POSTGRES_PASSWORD: odoo
|
|
TEMPLATE_DATABASE: odoo_primed
|
|
|
|
# ================================================================
|
|
# LINT & CODE QUALITY
|
|
# ================================================================
|
|
|
|
pylint-odoo:
|
|
stage: lint
|
|
image: $ODOO_IMAGE
|
|
script:
|
|
- pip install --no-cache-dir pylint-odoo
|
|
- pylint-odoo --rcfile=.pylintrc-odoo addons/itsulu_blog_publisher
|
|
allow_failure: true
|
|
|
|
black:
|
|
stage: lint
|
|
image: $ODOO_IMAGE
|
|
script:
|
|
- pip install --no-cache-dir black
|
|
- black --check addons/itsulu_blog_publisher
|
|
allow_failure: true
|
|
|
|
# ================================================================
|
|
# UNIT TESTS (TDD) & BDD
|
|
# ================================================================
|
|
|
|
unit_tests:
|
|
stage: test
|
|
image: $ODOO_IMAGE
|
|
services:
|
|
- name: postgres:15
|
|
alias: postgres
|
|
command:
|
|
- postgres
|
|
- -c
|
|
- fsync=off
|
|
- -c
|
|
- shared_buffers=512MB
|
|
before_script:
|
|
- export PGPASSWORD=$POSTGRES_PASSWORD
|
|
# Create primed template database
|
|
- createdb -h $POSTGRES_HOST -U $POSTGRES_USER $TEMPLATE_DATABASE
|
|
# Install Odoo + modules into template DB
|
|
- |
|
|
odoo -d $TEMPLATE_DATABASE \
|
|
-i base,website,website_blog,mail,itsulu_blog_publisher \
|
|
--addons-path=/usr/lib/python3/dist-packages/odoo/addons,$CI_PROJECT_DIR/addons \
|
|
--without-demo=all --stop-after-init --db_host=$POSTGRES_HOST --db_user=$POSTGRES_USER \
|
|
--db_password=$POSTGRES_PASSWORD
|
|
# Install test dependencies
|
|
- pip install --no-cache-dir pytest pytest-odoo pytest-bdd pytest-cov pytest-html
|
|
script:
|
|
- export PGPASSWORD=$POSTGRES_PASSWORD
|
|
# Clone template DB for test isolation
|
|
- createdb -h $POSTGRES_HOST -U $POSTGRES_USER -T $TEMPLATE_DATABASE $POSTGRES_DB
|
|
# Run TDD, BDD, and performance tests
|
|
- |
|
|
python3 -m pytest \
|
|
addons/itsulu_blog_publisher/tests \
|
|
-v \
|
|
--odoo-database=$POSTGRES_DB \
|
|
--cov=addons/itsulu_blog_publisher \
|
|
--cov-report=term-missing \
|
|
--cov-report=html \
|
|
--cov-report=xml \
|
|
--junitxml=report.xml \
|
|
--html=report-unit.html \
|
|
--self-contained-html
|
|
artifacts:
|
|
when: always
|
|
reports:
|
|
junit: report.xml
|
|
coverage_report:
|
|
coverage_format: cobertura
|
|
path: coverage.xml
|
|
paths:
|
|
- htmlcov/
|
|
- report-unit.html
|
|
expire_in: 30 days
|
|
coverage: '/^TOTAL.*\s+(\d+%)$/'
|
|
|
|
# ================================================================
|
|
# E2E TESTS (ITSulu K8s Cluster)
|
|
# ================================================================
|
|
|
|
e2e_tests:
|
|
stage: e2e
|
|
image: bitnami/kubectl:latest
|
|
needs: [build_image]
|
|
before_script:
|
|
- |
|
|
mkdir -p ~/.kube
|
|
echo "$KUBE_CONFIG" | base64 -d > ~/.kube/config
|
|
script:
|
|
# Create ephemeral E2E test job on K8s cluster
|
|
- |
|
|
TIMESTAMP=$(date +%s)
|
|
JOB_NAME="blog-publisher-e2e-test-${TIMESTAMP}"
|
|
|
|
kubectl apply -f - <<EOF
|
|
apiVersion: batch/v1
|
|
kind: Job
|
|
metadata:
|
|
name: $JOB_NAME
|
|
namespace: itsulu-testing
|
|
spec:
|
|
backoffLimit: 1
|
|
ttlSecondsAfterFinished: 3600
|
|
template:
|
|
spec:
|
|
serviceAccountName: test-runner
|
|
restartPolicy: Never
|
|
imagePullSecrets:
|
|
- name: gitlab-docker-creds
|
|
initContainers:
|
|
- name: setup-test-db
|
|
image: postgres:15-alpine
|
|
env:
|
|
- name: PGPASSWORD
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: test-db-info
|
|
key: password
|
|
- name: DB_USER
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: test-db-info
|
|
key: username
|
|
command: [sh, -c]
|
|
args:
|
|
- |
|
|
until pg_isready -h test-db-svc -U $$DB_USER; do sleep 2; done
|
|
PGPASSWORD=$$PGPASSWORD dropdb -h test-db-svc -U $$DB_USER odoo_e2e --if-exists
|
|
PGPASSWORD=$$PGPASSWORD createdb -h test-db-svc -U $$DB_USER -T odoo_template odoo_e2e
|
|
containers:
|
|
- name: test-runner
|
|
image: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
|
|
imagePullPolicy: IfNotPresent
|
|
env:
|
|
- name: PGPASSWORD
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: test-db-info
|
|
key: password
|
|
- name: DB_USER
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: test-db-info
|
|
key: username
|
|
- name: POSTGRES_HOST
|
|
value: test-db-svc
|
|
command: [sh, -c]
|
|
args:
|
|
- |
|
|
python3 -m pytest /mnt/extra-addons/itsulu_blog_publisher/tests/ \
|
|
--odoo-database=odoo_e2e \
|
|
-v --tb=short \
|
|
--html=/tmp/report-e2e.html --self-contained-html \
|
|
--junitxml=/tmp/report-e2e.xml
|
|
volumeMounts:
|
|
- name: test-results
|
|
mountPath: /tmp
|
|
volumes:
|
|
- name: test-results
|
|
emptyDir:
|
|
sizeLimit: 1Gi
|
|
EOF
|
|
|
|
# Wait for job completion
|
|
kubectl wait --for=condition=complete job/$JOB_NAME -n itsulu-testing --timeout=3600s || true
|
|
|
|
# Get pod name and download report
|
|
POD_NAME=$(kubectl get pods -n itsulu-testing -l batch.kubernetes.io/job-name=$JOB_NAME -o jsonpath='{.items[0].metadata.name}')
|
|
kubectl cp itsulu-testing/$POD_NAME:/tmp/report-e2e.html ./report-e2e.html || true
|
|
artifacts:
|
|
when: always
|
|
paths:
|
|
- report-e2e.html
|
|
expire_in: 1 week
|
|
only:
|
|
- merge_requests
|
|
allow_failure: true # E2E failures don't block merge (informational)
|
|
|
|
|
|
# ================================================================
|
|
# BUILD DOCKER IMAGE
|
|
# ================================================================
|
|
|
|
build_image:
|
|
stage: build
|
|
image: docker:latest
|
|
services:
|
|
- docker:dind
|
|
variables:
|
|
IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
|
|
script:
|
|
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
|
|
- docker build -t $IMAGE_TAG -t $CI_REGISTRY_IMAGE:latest .
|
|
- docker push $IMAGE_TAG
|
|
- docker push $CI_REGISTRY_IMAGE:latest
|
|
only:
|
|
- main
|
|
- /^19\.0$/
|
|
- merge_requests
|
|
|
|
# ================================================================
|
|
# RUNBOAT: TAG IMAGE WITH BRANCH/PR VERSION FOR RUNBOAT PICKUP
|
|
# ================================================================
|
|
|
|
runboat_tag:
|
|
stage: runboat
|
|
image: docker:latest
|
|
services:
|
|
- docker:dind
|
|
needs: [build_image]
|
|
variables:
|
|
BRANCH_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG
|
|
script:
|
|
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
|
|
# Tag image with branch slug so runboat can find the right image per branch
|
|
- docker pull $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
|
|
- docker tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA $BRANCH_TAG
|
|
- docker push $BRANCH_TAG
|
|
# Notify Forgejo commit status via runboat API
|
|
- |
|
|
curl -sf -X POST "$RUNBOAT_BASE_URL/api/v1/builds" \
|
|
-u "$RUNBOAT_API_ADMIN_USER:$RUNBOAT_API_ADMIN_PASSWD" \
|
|
-H "Content-Type: application/json" \
|
|
-d "{\"repo\":\"itsulu-odoo/$CI_PROJECT_NAME\",\"target_branch\":\"$CI_COMMIT_REF_NAME\",\"pr\":null,\"git_commit\":\"$CI_COMMIT_SHA\"}" \
|
|
|| echo "Runboat trigger skipped (not a watched branch)"
|
|
only:
|
|
- /^19\.0$/
|
|
- main
|
|
allow_failure: true
|
|
|
|
# ================================================================
|
|
# NOTIFY ON FAILURE
|
|
# ================================================================
|
|
|
|
notify_failure:
|
|
stage: notify
|
|
script:
|
|
- echo "Pipeline failed for $CI_PROJECT_NAME on $CI_COMMIT_BRANCH"
|
|
when: on_failure
|
|
only:
|
|
- main
|
|
- /^19\.0$/
|