name: CI/CD Pipeline on: push: branches: [ main, develop, dev ] pull_request: branches: [ main, develop, dev ] env: PYTHON_VERSION: "3.11" jobs: lint: name: Code Quality Checks runs-on: ubuntu-latest steps: - name: Checkout code run: | REPO_URL=$(echo ${GITHUB_SERVER_URL} | sed "s|://|://oauth2:${{ gitea.token }}@|") git clone --depth=1 --branch=${GITHUB_REF_NAME} ${REPO_URL}/${GITHUB_REPOSITORY}.git . git checkout ${GITHUB_SHA} - name: Install Python and uv run: | apt-get update && apt-get install -y software-properties-common add-apt-repository -y ppa:deadsnakes/ppa apt-get update && apt-get install -y python3.11 python3.11-venv curl -LsSf https://astral.sh/uv/install.sh | sh export PATH="$HOME/.local/bin:$PATH" - name: Create virtual environment and install dependencies run: | export PATH="$HOME/.local/bin:$PATH" uv venv --python python3.11 source .venv/bin/activate uv sync --dev - name: Run Ruff linting run: | export PATH="$HOME/.local/bin:$PATH" source .venv/bin/activate ruff check src/ - name: Run Ruff formatting check run: | export PATH="$HOME/.local/bin:$PATH" source .venv/bin/activate ruff format src/ --check test: name: Run Tests runs-on: ubuntu-latest steps: - name: Checkout code run: | REPO_URL=$(echo ${GITHUB_SERVER_URL} | sed "s|://|://oauth2:${{ gitea.token }}@|") git clone --depth=1 --branch=${GITHUB_REF_NAME} ${REPO_URL}/${GITHUB_REPOSITORY}.git . git checkout ${GITHUB_SHA} - name: Install Python and uv run: | apt-get update && apt-get install -y software-properties-common add-apt-repository -y ppa:deadsnakes/ppa apt-get update && apt-get install -y python3.11 python3.11-venv curl -LsSf https://astral.sh/uv/install.sh | sh export PATH="$HOME/.local/bin:$PATH" - name: Create virtual environment and install dependencies run: | export PATH="$HOME/.local/bin:$PATH" uv venv --python python3.11 source .venv/bin/activate uv sync --dev - name: Run Django tests run: | export PATH="$HOME/.local/bin:$PATH" source .venv/bin/activate export PYTHONPATH="${PWD}/src:${PYTHONPATH}" python src/manage.py test tests --verbosity=2 env: DJANGO_SETTINGS_MODULE: config.settings.test SECRET_KEY: test-secret-key-for-ci build: name: Build Docker Images runs-on: ubuntu-latest needs: [lint, test] steps: - name: Checkout code run: | REPO_URL=$(echo ${GITHUB_SERVER_URL} | sed "s|://|://oauth2:${{ gitea.token }}@|") git clone --depth=1 --branch=${GITHUB_REF_NAME} ${REPO_URL}/${GITHUB_REPOSITORY}.git . git checkout ${GITHUB_SHA} - name: Build web image run: | BRANCH_TAG=$(echo ${GITHUB_REF_NAME} | sed 's/\//-/g') SHA_SHORT=$(echo ${GITHUB_SHA} | cut -c1-7) docker build -f ./docker/Dockerfile.web -t state_corp-web:${BRANCH_TAG} -t state_corp-web:${BRANCH_TAG}-${SHA_SHORT} . - name: Build celery image run: | BRANCH_TAG=$(echo ${GITHUB_REF_NAME} | sed 's/\//-/g') SHA_SHORT=$(echo ${GITHUB_SHA} | cut -c1-7) docker build -f ./docker/Dockerfile.celery -t state_corp-celery:${BRANCH_TAG} -t state_corp-celery:${BRANCH_TAG}-${SHA_SHORT} . push: name: Push to Gitea Registry runs-on: ubuntu-latest needs: [build] if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/dev' steps: - name: Checkout code run: | REPO_URL=$(echo ${GITHUB_SERVER_URL} | sed "s|://|://oauth2:${{ gitea.token }}@|") git clone --depth=1 --branch=${GITHUB_REF_NAME} ${REPO_URL}/${GITHUB_REPOSITORY}.git . git checkout ${GITHUB_SHA} - name: Build and push images run: | # Install crane for pushing to Gitea container registry curl -sL https://github.com/google/go-containerregistry/releases/download/v0.19.0/go-containerregistry_Linux_x86_64.tar.gz | tar xz crane chmod +x crane BRANCH_TAG=$(echo ${GITHUB_REF_NAME} | sed 's/\//-/g') SHA_SHORT=$(echo ${GITHUB_SHA} | cut -c1-7) REGISTRY_HOST="10.10.0.10:3000" REGISTRY="${REGISTRY_HOST}/${{ github.repository_owner }}" # Debug echo "Registry: ${REGISTRY_HOST}" echo "Actor: ${GITHUB_ACTOR}" # Login to Gitea container registry (HTTP, requires --insecure) echo "${REGISTRY_PASSWORD}" | ./crane auth login --insecure ${REGISTRY_HOST} -u "${REGISTRY_USER}" --password-stdin # Build and push web image docker build -f ./docker/Dockerfile.web -t state_corp-web:local . docker save state_corp-web:local -o /tmp/web.tar ./crane push --insecure /tmp/web.tar ${REGISTRY}/state_corp-web:${BRANCH_TAG} ./crane push --insecure /tmp/web.tar ${REGISTRY}/state_corp-web:${BRANCH_TAG}-${SHA_SHORT} if [ "${GITHUB_REF_NAME}" = "main" ]; then ./crane push --insecure /tmp/web.tar ${REGISTRY}/state_corp-web:latest fi # Build and push celery image docker build -f ./docker/Dockerfile.celery -t state_corp-celery:local . docker save state_corp-celery:local -o /tmp/celery.tar ./crane push --insecure /tmp/celery.tar ${REGISTRY}/state_corp-celery:${BRANCH_TAG} ./crane push --insecure /tmp/celery.tar ${REGISTRY}/state_corp-celery:${BRANCH_TAG}-${SHA_SHORT} if [ "${GITHUB_REF_NAME}" = "main" ]; then ./crane push --insecure /tmp/celery.tar ${REGISTRY}/state_corp-celery:latest fi env: REGISTRY_USER: ${{ secrets.REGISTRY_USER }} REGISTRY_PASSWORD: ${{ secrets.REGISTRY_TOKEN }} - name: Image summary run: | echo "Images pushed to 10.10.0.10:3000/${{ github.repository_owner }}/" deploy: name: Deploy to Server runs-on: ubuntu-latest needs: [push] if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/dev' steps: - name: Checkout code run: | REPO_URL=$(echo ${GITHUB_SERVER_URL} | sed "s|://|://oauth2:${{ gitea.token }}@|") git clone --depth=1 --branch=${GITHUB_REF_NAME} ${REPO_URL}/${GITHUB_REPOSITORY}.git . git checkout ${GITHUB_SHA} - name: Deploy via SSH run: | BRANCH_TAG=$(echo ${GITHUB_REF_NAME} | sed 's/\//-/g') # Setup SSH (decode base64 key) mkdir -p ~/.ssh echo "${DEPLOY_SSH_KEY}" | base64 -d > ~/.ssh/deploy_key chmod 600 ~/.ssh/deploy_key ssh-keyscan -H ${DEPLOY_HOST} >> ~/.ssh/known_hosts 2>/dev/null # Copy docker-compose.prod.yml to server scp -i ~/.ssh/deploy_key -o StrictHostKeyChecking=no docker-compose.prod.yml ${DEPLOY_USER}@${DEPLOY_HOST}:/opt/state-corp-backend/ # Deploy commands ssh -i ~/.ssh/deploy_key -o StrictHostKeyChecking=no ${DEPLOY_USER}@${DEPLOY_HOST} " cd /opt/state-corp-backend # Login to registry (HTTP on internal IP) echo '${REGISTRY_PASSWORD}' | docker login --username '${REGISTRY_USER}' --password-stdin 10.10.0.10:3000 # Pull new images export IMAGE_TAG=${BRANCH_TAG} docker compose -f docker-compose.prod.yml pull web celery_worker celery_beat # Stop and remove all project containers docker compose -f docker-compose.prod.yml down --remove-orphans || true docker rm -f state_corp_db state_corp_redis state_corp_web state_corp_celery_worker state_corp_celery_beat 2>/dev/null || true # Start services docker compose -f docker-compose.prod.yml up -d # Cleanup old images docker image prune -f " echo "Deployed ${BRANCH_TAG} to ${DEPLOY_HOST}" env: DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }} DEPLOY_USER: ${{ secrets.DEPLOY_USER }} DEPLOY_SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }} REGISTRY_USER: ${{ secrets.REGISTRY_USER }} REGISTRY_PASSWORD: ${{ secrets.REGISTRY_TOKEN }}