name: Build Images # Builds and pushes pre-built Docker runner images to the Gitea registry. # These images eliminate the 40-120s setup tax on every CI job by baking # devx and all dependencies into the image. # # Triggers: # - After post-merge workflow completes successfully (workflow_run) # This ensures images are only rebuilt AFTER the release is published # to PyPI, so the image always has the latest released version. # - Manually via workflow_dispatch # # Consolidated into 2 jobs (from 3): # build-and-push (includes release-commit detection) ──→ cleanup # # The workflow builds 3 tier images in sequence: # ci-base → ci-quality → ci-full # Each tier builds FROM the previous one, so they must be built in order. # After pushing, a cleanup job removes old versions (keeps last 2 + latest). on: workflow_run: workflows: ["Post-merge"] types: [completed] branches: [master] workflow_dispatch: concurrency: group: build-images cancel-in-progress: false jobs: build-and-push: runs-on: docker container: image: git.oblachno.oblachno.fyi/oblachno-oss/runner-images/ci-full:latest credentials: username: ${{ vars.CI_GITEA_USERNAME }} password: ${{ secrets.CI_GITEA_API_TOKEN }} timeout-minutes: 30 outputs: is-release: ${{ steps.check.outputs.is-release }} steps: - uses: actions/checkout@v4 with: fetch-depth: 1 - name: Set up environment env: CI_GITEA_API_TOKEN: ${{ secrets.CI_GITEA_API_TOKEN }} run: make setup-release - name: Check if this is a release commit id: check env: PYTHONPATH: src run: | . .venv/bin/activate python3 -m devx.ci.detect_release_commit - name: Docker registry login if: >- github.event_name == 'workflow_dispatch' || (github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success' && steps.check.outputs.is-release == 'false') env: CI_GITEA_API_TOKEN: ${{ secrets.CI_GITEA_API_TOKEN }} CI_GITEA_USERNAME: ${{ vars.CI_GITEA_USERNAME }} run: | . .venv/bin/activate _TOKEN="$CI_GITEA_API_TOKEN" [ -z "$_TOKEN" ] && _TOKEN="$DEVELOPER_GITEA_API_TOKEN" [ -z "$_TOKEN" ] && _TOKEN="$CI_GITEA_TOKEN" if [ -z "$_TOKEN" ]; then echo "Gitea API token not set — skipping Docker login"; exit 1; fi echo "$_TOKEN" | docker login git.oblachno.oblachno.fyi -u "$CI_GITEA_USERNAME" --password-stdin - name: Build and push tier images if: >- github.event_name == 'workflow_dispatch' || (github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success' && steps.check.outputs.is-release == 'false') env: CI_GITEA_API_TOKEN: ${{ secrets.CI_GITEA_API_TOKEN }} CI_GITEA_USERNAME: ${{ vars.CI_GITEA_USERNAME }} PYTHONPATH: src # Serialize blob uploads to avoid Gitea registry race condition # (BlobUploader.Append offset mismatch — see DEVX-162). DOCKER_MAX_CONCURRENT_UPLOADS: "1" run: | . .venv/bin/activate export PATH="$HOME/.local/bin:$PATH" # Build ci-base first (it's the base for ci-quality and ci-full) python3 -m devx.tools.build_image \ --dockerfile docker/ci-base/Dockerfile \ --name oblachno-oss/runner-images/ci-base \ --tag latest \ --registry git.oblachno.oblachno.fyi \ --push --pull # Build ci-quality (FROM ci-base-latest) python3 -m devx.tools.build_image \ --dockerfile docker/ci-quality/Dockerfile \ --name oblachno-oss/runner-images/ci-quality \ --tag latest \ --registry git.oblachno.oblachno.fyi \ --push # Build ci-full (FROM ci-quality-latest) python3 -m devx.tools.build_image \ --dockerfile docker/ci-full/Dockerfile \ --name oblachno-oss/runner-images/ci-full \ --tag latest \ --registry git.oblachno.oblachno.fyi \ --push - name: Notify on failure if: failure() env: CI_GITEA_API_TOKEN: ${{ secrets.CI_GITEA_API_TOKEN }} PYTHONPATH: src run: | . .venv/bin/activate 2>/dev/null || true export PATH="$HOME/.local/bin:$PATH" python3 -m devx.ci.notify_failure \ --repo "${{ github.repository }}" \ --run-id "${{ github.run_id }}" \ --workflow "build-images/build-and-push" \ --commit "${{ github.sha }}" \ --auto-login cleanup: needs: [build-and-push] if: always() && needs.build-and-push.result == 'success' runs-on: docker container: image: git.oblachno.oblachno.fyi/oblachno-oss/runner-images/ci-base:latest credentials: username: ${{ vars.CI_GITEA_USERNAME }} password: ${{ secrets.CI_GITEA_API_TOKEN }} timeout-minutes: 10 steps: - uses: actions/checkout@v4 with: fetch-depth: 1 - name: Set up environment env: CI_GITEA_API_TOKEN: ${{ secrets.CI_GITEA_API_TOKEN }} run: make setup-ci - name: Clean up old image versions env: CI_GITEA_API_TOKEN: ${{ secrets.CI_GITEA_API_TOKEN }} PYTHONPATH: src run: | . .venv/bin/activate python3 -m devx.tools.clean_images \ --owner oblachno-oss \ --name oblachno-oss/runner-images/ci-base \ --name oblachno-oss/runner-images/ci-quality \ --name oblachno-oss/runner-images/ci-full \ --keep 2