Skip to content

Merge pull request #11 from nhnammldlnlpcvrs/feature #187

Merge pull request #11 from nhnammldlnlpcvrs/feature

Merge pull request #11 from nhnammldlnlpcvrs/feature #187

Workflow file for this run

name: CI Pipeline
on:
push:
branches:
- "feature*"
- main
pull_request:
branches:
- main
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository_owner }}/hallucination-backend
PYTHON_VERSION: "3.10"
jobs:
test:
name: Unit Test & Quality Gate
runs-on: self-hosted
outputs:
coverage: ${{ steps.coverage.outputs.percentage }}
steps:
- uses: actions/checkout@v4
- name: Build CI Docker image
run: |
docker build \
-f docker/Dockerfile.ci \
-t hallucination-ci:${{ github.sha }} .
- name: Run unit tests with coverage
run: |
docker run --rm \
-v ${{ github.workspace }}:/workspace \
hallucination-ci:${{ github.sha }} \
pytest tests/unit \
--cov=backend \
--cov-report=xml:/workspace/coverage.xml \
--cov-report=term-missing \
--cov-fail-under=80
- name: Extract coverage percentage
id: coverage
run: |
COVERAGE=$(python3 -c "
import xml.etree.ElementTree as ET
tree = ET.parse('coverage.xml')
root = tree.getroot()
print(round(float(root.attrib['line-rate']) * 100, 1))
")
echo "percentage=${COVERAGE}" >> $GITHUB_OUTPUT
echo "Coverage: ${COVERAGE}%"
- name: Upload coverage report
uses: actions/upload-artifact@v4
with:
name: coverage-report
path: coverage.xml
build:
name: Build & Push Image
runs-on: self-hosted
needs: test
if: github.event_name == 'push'
outputs:
image_tag: ${{ steps.meta.outputs.tags }}
image_digest: ${{ steps.build.outputs.digest }}
short_sha: ${{ steps.sha.outputs.short }}
steps:
- uses: actions/checkout@v4
- name: Get short SHA
id: sha
run: echo "short=${GITHUB_SHA::8}" >> $GITHUB_OUTPUT
- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GHCR_TOKEN }}
- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=sha,prefix=,suffix=,format=short
type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }}
type=ref,event=branch
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
driver-opts: image=moby/buildkit:latest
- name: Cache MLflow Model
id: model-cache
uses: actions/cache@v3
with:
path: backend/model_store/
key: mlflow-model-production-${{ hashFiles('scripts/pull_model_from_registry.py') }}
restore-keys: |
mlflow-model-production-
- name: Pull model from MLflow Registry
if: steps.model-cache.outputs.cache-hit != 'true'
env:
MLFLOW_TRACKING_URI: ${{ secrets.MLFLOW_TRACKING_URI }}
MLFLOW_MODEL_NAME: vihallu-detector
MLFLOW_MODEL_ALIAS: production
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
MLFLOW_S3_ENDPOINT_URL: ${{ secrets.MLFLOW_S3_ENDPOINT_URL }}
MLFLOW_S3_IGNORE_TLS: "true"
run: |
python3 -m venv .venv_mlflow
. .venv_mlflow/bin/activate
pip install --quiet --upgrade pip
pip install --quiet \
"setuptools>=70.0" \
"mlflow==2.19.0" \
"boto3==1.34.69" \
"protobuf>=3.20,<5"
python scripts/pull_model_from_registry.py || {
echo "[WARN] MLflow download failed, creating empty model_store"
mkdir -p backend/model_store
}
deactivate
rm -rf .venv_mlflow
- name: Verify model exists
run: |
if [ ! -d "backend/model_store" ] || [ -z "$(ls -A backend/model_store)" ]; then
echo "[ERROR] Model store is empty!"
exit 1
fi
ls -la backend/model_store/
- name: Build and push Docker image
id: build
uses: docker/build-push-action@v5
with:
context: .
file: docker/Dockerfile.backend
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=local,src=/tmp/docker-cache
cache-to: type=local,dest=/tmp/docker-cache,mode=max
build-args: |
GIT_SHA=${{ github.sha }}
BUILD_DATE=${{ github.event.head_commit.timestamp }}
- name: Output image info
run: |
echo "Image pushed:"
echo "Tags: ${{ steps.meta.outputs.tags }}"
echo "Digest: ${{ steps.build.outputs.digest }}"
echo "SHA: ${{ steps.sha.outputs.short }}"
trigger-deploy:
name: Trigger Jenkins CD
runs-on: self-hosted
needs: build
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
steps:
- name: Trigger Jenkins deployment
run: |
curl -f -X POST \
"${{ secrets.JENKINS_URL }}/job/hallucination-cd/buildWithParameters" \
--user "${{ secrets.JENKINS_USER }}:${{ secrets.JENKINS_TOKEN }}" \
--data-urlencode "IMAGE_TAG=${{ needs.build.outputs.short_sha }}" \
--data-urlencode "GIT_SHA=${{ github.sha }}" \
--data-urlencode "GIT_BRANCH=${{ github.ref_name }}" \
--data-urlencode "TRIGGERED_BY=github-actions"
echo "Jenkins CD triggered with IMAGE_TAG=${{ needs.build.outputs.short_sha }}"
- name: Comment on commit
uses: actions/github-script@v7
with:
script: |
github.rest.repos.createCommitComment({
owner: context.repo.owner,
repo: context.repo.repo,
commit_sha: context.sha,
body: `**CD Pipeline Triggered**\n- Image: \`ghcr.io/${{ env.IMAGE_NAME }}:${{ needs.build.outputs.short_sha }}\`\n- Jenkins: ${{ secrets.JENKINS_URL }}/job/hallucination-cd/`
})