name: CI on: push: branches: [main, develop, v2] pull_request: branches: [main, develop] jobs: lint: runs-on: ubuntu-latest env: APT_CACHER_NG: http://192.168.88.14:3142 steps: - uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v4 with: python-version: "3.12" # - name: Get pip cache dir # id: pip-cache # run: | # echo "path=$(pip cache dir)" >> $GITEA_OUTPUT # echo "Pip cache dir: $(pip cache dir)" # - name: Cache pip dependencies # uses: actions/cache@v4 # with: # path: ${{ steps.pip-cache.outputs.path }} # key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt', 'requirements-test.txt', 'pyproject.toml') }} # restore-keys: | # ${{ runner.os }}-pip- - name: Configure apt proxy run: | if [ -n "${APT_CACHER_NG}" ]; then echo "Acquire::http::Proxy \"${APT_CACHER_NG}\";" | tee /etc/apt/apt.conf.d/01apt-cacher-ng fi - name: Install system packages run: | apt-get update apt-get install -y build-essential libpq-dev - name: Install dependencies run: | python -m pip install --upgrade pip pip install -r requirements.txt pip install -r requirements-test.txt - name: Run Ruff run: ruff check . - name: Run Black run: black --check . - name: Run Bandit run: bandit -c pyproject.toml -r tests test: runs-on: ubuntu-latest needs: lint env: APT_CACHER_NG: http://192.168.88.14:3142 DB_DRIVER: postgresql+psycopg2 DB_HOST: 192.168.88.35 DB_NAME: calminer_test DB_USER: calminer DB_PASSWORD: calminer_password services: postgres: image: postgres:17 env: POSTGRES_USER: ${{ env.DB_USER }} POSTGRES_PASSWORD: ${{ env.DB_PASSWORD }} POSTGRES_DB: ${{ env.DB_NAME }} options: >- --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 steps: - uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v4 with: python-version: "3.12" # - name: Get pip cache dir # id: pip-cache # run: | # echo "path=$(pip cache dir)" >> $GITEA_OUTPUT # echo "Pip cache dir: $(pip cache dir)" # - name: Cache pip dependencies # uses: actions/cache@v4 # with: # path: ${{ steps.pip-cache.outputs.path }} # key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt', 'requirements-test.txt', 'pyproject.toml') }} # restore-keys: | # ${{ runner.os }}-pip- - name: Configure apt proxy run: | if [ -n "${APT_CACHER_NG}" ]; then echo "Acquire::http::Proxy \"${APT_CACHER_NG}\";" | tee /etc/apt/apt.conf.d/01apt-cacher-ng fi - name: Install system packages run: | apt-get update apt-get install -y build-essential libpq-dev - name: Install dependencies run: | python -m pip install --upgrade pip pip install -r requirements.txt pip install -r requirements-test.txt - name: Install Playwright system dependencies run: playwright install-deps - name: Install Playwright browsers run: playwright install - name: Run tests env: DATABASE_DRIVER: ${{ env.DB_DRIVER }} DATABASE_HOST: postgres DATABASE_PORT: 5432 DATABASE_USER: ${{ env.DB_USER }} DATABASE_PASSWORD: ${{ env.DB_PASSWORD }} DATABASE_NAME: ${{ env.DB_NAME }} run: | pytest --cov=. --cov-report=term-missing --cov-report=xml --cov-fail-under=80 --junitxml=pytest-report.xml - name: Upload test artifacts if: always() uses: actions/upload-artifact@v4 with: name: test-artifacts path: | coverage.xml pytest-report.xml build: runs-on: ubuntu-latest needs: - lint - test env: DEFAULT_BRANCH: main REGISTRY_URL: ${{ secrets.REGISTRY_URL }} REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }} REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }} REGISTRY_CONTAINER_NAME: calminer steps: - name: Checkout uses: actions/checkout@v4 - name: Collect workflow metadata id: meta shell: bash run: | ref_name="${GITHUB_REF_NAME:-${GITHUB_REF##*/}}" event_name="${GITHUB_EVENT_NAME:-}" sha="${GITHUB_SHA:-}" if [ "$ref_name" = "${DEFAULT_BRANCH:-main}" ]; then echo "on_default=true" >> "$GITHUB_OUTPUT" else echo "on_default=false" >> "$GITHUB_OUTPUT" fi echo "ref_name=$ref_name" >> "$GITHUB_OUTPUT" echo "event_name=$event_name" >> "$GITHUB_OUTPUT" echo "sha=$sha" >> "$GITHUB_OUTPUT" - name: Set up QEMU and Buildx uses: docker/setup-buildx-action@v3 - name: Log in to gitea registry if: ${{ steps.meta.outputs.on_default == 'true' }} uses: docker/login-action@v3 continue-on-error: true with: registry: ${{ env.REGISTRY_URL }} username: ${{ env.REGISTRY_USERNAME }} password: ${{ env.REGISTRY_PASSWORD }} - name: Build image id: build-image env: REGISTRY_URL: ${{ env.REGISTRY_URL }} REGISTRY_CONTAINER_NAME: ${{ env.REGISTRY_CONTAINER_NAME }} SHA_TAG: ${{ steps.meta.outputs.sha }} PUSH_IMAGE: ${{ steps.meta.outputs.on_default == 'true' && steps.meta.outputs.event_name != 'pull_request' && env.REGISTRY_URL != '' && env.REGISTRY_USERNAME != '' && env.REGISTRY_PASSWORD != '' }} run: | set -eo pipefail LOG_FILE=build.log if [ "${PUSH_IMAGE}" = "true" ]; then docker buildx build \ --push \ --tag "${REGISTRY_URL}/allucanget/${REGISTRY_CONTAINER_NAME}:latest" \ --tag "${REGISTRY_URL}/allucanget/${REGISTRY_CONTAINER_NAME}:${SHA_TAG}" \ --file Dockerfile \ . 2>&1 | tee "${LOG_FILE}" else docker buildx build \ --load \ --tag "${REGISTRY_CONTAINER_NAME}:ci" \ --file Dockerfile \ . 2>&1 | tee "${LOG_FILE}" fi - name: Upload docker build logs if: failure() uses: actions/upload-artifact@v4 with: name: docker-build-logs path: build.log deploy: runs-on: ubuntu-latest needs: build if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request' env: REGISTRY_URL: ${{ secrets.REGISTRY_URL }} REGISTRY_CONTAINER_NAME: calminer KUBE_CONFIG: ${{ secrets.KUBE_CONFIG }} STAGING_KUBE_CONFIG: ${{ secrets.STAGING_KUBE_CONFIG }} PROD_KUBE_CONFIG: ${{ secrets.PROD_KUBE_CONFIG }} steps: - name: Checkout uses: actions/checkout@v4 - name: Set up kubectl for staging if: github.ref == 'refs/heads/main' && contains(github.event.head_commit.message, '[deploy staging]') uses: azure/k8s-set-context@v3 with: method: kubeconfig kubeconfig: ${{ env.STAGING_KUBE_CONFIG }} - name: Set up kubectl for production if: github.ref == 'refs/heads/main' && contains(github.event.head_commit.message, '[deploy production]') uses: azure/k8s-set-context@v3 with: method: kubeconfig kubeconfig: ${{ env.PROD_KUBE_CONFIG }} - name: Deploy to staging if: github.ref == 'refs/heads/main' && contains(github.event.head_commit.message, '[deploy staging]') run: | # Update image in deployment kubectl set image deployment/calminer-app calminer=${REGISTRY_URL}/allucanget/${REGISTRY_CONTAINER_NAME}:latest # Apply any config changes kubectl apply -f k8s/configmap.yaml kubectl apply -f k8s/secret.yaml # Wait for rollout kubectl rollout status deployment/calminer-app - name: Collect staging deployment logs if: github.ref == 'refs/heads/main' && contains(github.event.head_commit.message, '[deploy staging]') run: | mkdir -p logs/deployment/staging kubectl get pods -o wide > logs/deployment/staging/pods.txt kubectl get deployment calminer-app -o yaml > logs/deployment/staging/deployment.yaml kubectl logs deployment/calminer-app --all-containers=true --tail=500 > logs/deployment/staging/calminer-app.log - name: Deploy to production if: github.ref == 'refs/heads/main' && contains(github.event.head_commit.message, '[deploy production]') run: | # Update image in deployment kubectl set image deployment/calminer-app calminer=${REGISTRY_URL}/allucanget/${REGISTRY_CONTAINER_NAME}:latest # Apply any config changes kubectl apply -f k8s/configmap.yaml kubectl apply -f k8s/secret.yaml # Wait for rollout kubectl rollout status deployment/calminer-app - name: Collect production deployment logs if: github.ref == 'refs/heads/main' && contains(github.event.head_commit.message, '[deploy production]') run: | mkdir -p logs/deployment/production kubectl get pods -o wide > logs/deployment/production/pods.txt kubectl get deployment calminer-app -o yaml > logs/deployment/production/deployment.yaml kubectl logs deployment/calminer-app --all-containers=true --tail=500 > logs/deployment/production/calminer-app.log - name: Upload deployment logs if: always() uses: actions/upload-artifact@v4 with: name: deployment-logs path: logs/deployment if-no-files-found: ignore