fix(training): handle TrainingConfig passed directly from router #303
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Lint, Test, Build, and Publish Docker Images | |
| on: | |
| push: | |
| branches: [main, dev] | |
| pull_request: | |
| branches: [main] | |
| permissions: | |
| contents: write | |
| packages: write | |
| jobs: | |
| # ────────────────────────────────────────────────────────────────────────── | |
| # 🧹 Lint | |
| # ────────────────────────────────────────────────────────────────────────── | |
| lint: | |
| name: "🧹 Lint Code & Dockerfiles" | |
| runs-on: ubuntu-latest | |
| if: | | |
| !contains(github.event.head_commit.message, '[skip ci]') && | |
| !contains(github.event.head_commit.message, 'chore(release)') | |
| steps: | |
| - name: "🕺 Checkout repository" | |
| uses: actions/checkout@v4 | |
| with: | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: "🐍 Set up Python" | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.11" | |
| - name: "📦 Cache pip dependencies" | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/.cache/pip | |
| key: ${{ runner.os }}-pip-lint-${{ hashFiles('**/*_reqs_*.txt', '**/pyproject.toml') }} | |
| restore-keys: | | |
| ${{ runner.os }}-pip-lint- | |
| - name: "⚙️ Install Linting Tools" | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install "ruff==0.4.0" "black==24.1.1" "isort==5.13.2" bandit mypy | |
| sudo wget -qO /usr/local/bin/hadolint \ | |
| https://github.com/hadolint/hadolint/releases/latest/download/hadolint-Linux-x86_64 | |
| sudo chmod +x /usr/local/bin/hadolint | |
| - name: "✨ Run Ruff Linter" | |
| run: | | |
| if [ "${{ github.event_name }}" = "pull_request" ]; then | |
| ruff check . --output-format=github | |
| else | |
| ruff check . --fix --output-format=github || true | |
| fi | |
| - name: "⚫️ Run Black Formatter" | |
| run: | | |
| if [ "${{ github.event_name }}" = "pull_request" ]; then | |
| black --check . | |
| else | |
| black . | |
| git diff --exit-code || true | |
| fi | |
| - name: "🔀 isort (Import Sort Check)" | |
| run: isort --check . --skip-glob "tests/integration/*" --skip "tests/conftest.py" --skip "src/api/training/worker.py" | |
| - name: "🛡️ Bandit (Security Analysis)" | |
| run: bandit -r src/ -x tests/ -s B101 -c .bandit.yml | |
| - name: "🛡️ Mypy (Static Type Analysis)" | |
| run: mypy src --ignore-missing-imports || true | |
| - name: "🐳 Lint Dockerfiles" | |
| run: | | |
| hadolint docker/api/Dockerfile || true | |
| hadolint docker/sandbox/Dockerfile || true | |
| hadolint docker/training/Dockerfile || true | |
| hadolint docker/inference/Dockerfile || true | |
| hadolint docker/router/Dockerfile || true | |
| # ────────────────────────────────────────────────────────────────────────── | |
| # ✅ Tests | |
| # ────────────────────────────────────────────────────────────────────────── | |
| test: | |
| name: "✅ Run Unit Tests" | |
| runs-on: ubuntu-latest | |
| needs: lint | |
| if: | | |
| !contains(github.event.head_commit.message, '[skip ci]') && | |
| !contains(github.event.head_commit.message, 'chore(release)') | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| python-version: ["3.11", "3.12"] | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| - name: "📦 Cache pip dependencies" | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/.cache/pip | |
| key: ${{ runner.os }}-pip-test-${{ matrix.python-version }}-${{ hashFiles('**/*_reqs_*.txt', '**/pyproject.toml') }} | |
| restore-keys: | | |
| ${{ runner.os }}-pip-test-${{ matrix.python-version }}- | |
| - name: "⚙️ Install Project & Test Deps" | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install -r api_unhashed_reqs.txt | |
| pip install --require-hashes -r api_reqs_hashed.txt | |
| pip install -r sandbox_reqs_unhashed.txt | |
| pip install --require-hashes -r sandbox_reqs_hashed.txt | |
| pip install pytest pytest-cov | |
| - name: "🦀 Install Rust toolchain" | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: "📦 Cache Rust build artifacts" | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.cargo/registry | |
| ~/.cargo/git | |
| rust/fc_parser/target | |
| key: ${{ runner.os }}-rust-fc-parser-${{ matrix.python-version }}-${{ hashFiles('rust/fc_parser/Cargo.toml', 'rust/fc_parser/src/**') }} | |
| restore-keys: | | |
| ${{ runner.os }}-rust-fc-parser-${{ matrix.python-version }}- | |
| - name: "🦀 Build fc_parser Rust extension" | |
| run: | | |
| pip install maturin | |
| cd rust/fc_parser && maturin build --release | |
| pip install target/wheels/*.whl | |
| - name: "✅ Run Pytest with Coverage" | |
| run: pytest tests/ --cov=src --cov-report=term-missing | |
| # ────────────────────────────────────────────────────────────────────────── | |
| # 🏗️ Verify Docker Builds (DRY RUN FOR PRs) | |
| # ────────────────────────────────────────────────────────────────────────── | |
| verify_docker_builds: | |
| name: "🏗️ Verify Docker Builds (Dry Run)" | |
| runs-on: ubuntu-latest | |
| needs: test | |
| if: github.event_name == 'pull_request' | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: "⚙️ Setup Docker Buildx" | |
| uses: docker/setup-buildx-action@v3 | |
| - name: "🏗️ Build API Image (No Push)" | |
| uses: docker/build-push-action@v5 | |
| with: | |
| context: . | |
| file: ./docker/api/Dockerfile | |
| push: false | |
| platforms: linux/amd64 | |
| cache-from: type=gha,scope=api | |
| cache-to: type=gha,mode=max,scope=api | |
| - name: "🏗️ Build Sandbox Image (No Push)" | |
| uses: docker/build-push-action@v5 | |
| with: | |
| context: . | |
| file: ./docker/sandbox/Dockerfile | |
| push: false | |
| platforms: linux/amd64 | |
| cache-from: type=gha,scope=sandbox | |
| cache-to: type=gha,mode=max,scope=sandbox | |
| - name: "🏗️ Build Training Image (No Push)" | |
| uses: docker/build-push-action@v5 | |
| with: | |
| context: . | |
| file: ./docker/training/Dockerfile | |
| push: false | |
| platforms: linux/amd64 | |
| cache-from: type=gha,scope=training | |
| cache-to: type=gha,mode=max,scope=training | |
| - name: "🏗️ Build Inference Worker Image (No Push)" | |
| uses: docker/build-push-action@v5 | |
| with: | |
| context: . | |
| file: ./docker/inference/Dockerfile | |
| push: false | |
| platforms: linux/amd64 | |
| cache-from: type=gha,scope=inference | |
| cache-to: type=gha,mode=max,scope=inference | |
| - name: "🏗️ Build Router Image (No Push)" | |
| uses: docker/build-push-action@v5 | |
| with: | |
| context: . | |
| file: ./docker/router/Dockerfile | |
| push: false | |
| platforms: linux/amd64 | |
| cache-from: type=gha,scope=router | |
| cache-to: type=gha,mode=max,scope=router | |
| # ────────────────────────────────────────────────────────────────────────── | |
| # 🧪 Build & Publish Staging Images (DEV BRANCH ONLY) | |
| # ────────────────────────────────────────────────────────────────────────── | |
| build_staging: | |
| name: "🧪 Build & Publish Staging Images" | |
| runs-on: ubuntu-latest | |
| needs: test | |
| if: | | |
| github.event_name == 'push' && | |
| github.ref == 'refs/heads/dev' && | |
| !contains(github.event.head_commit.message, '[skip ci]') && | |
| !contains(github.event.head_commit.message, 'chore(release)') | |
| steps: | |
| - name: "🕺 Checkout" | |
| uses: actions/checkout@v4 | |
| - name: "⚙️ Setup QEMU" | |
| uses: docker/setup-qemu-action@v3 | |
| - name: "⚙️ Setup Docker Buildx" | |
| uses: docker/setup-buildx-action@v3 | |
| - name: "🔑 Login to Docker Hub" | |
| uses: docker/login-action@v3 | |
| with: | |
| username: thanosprime | |
| password: ${{ secrets.DOCKERHUB_THANOSPRIME }} | |
| - name: "🏷️ Resolve short SHA" | |
| id: sha | |
| run: echo "SHORT_SHA=${GITHUB_SHA::8}" >> $GITHUB_OUTPUT | |
| - name: "🏗️ Build & Push Staging API Image" | |
| uses: docker/build-push-action@v5 | |
| with: | |
| context: . | |
| file: ./docker/api/Dockerfile | |
| push: true | |
| tags: thanosprime/projectdavid-core-api:dev-${{ steps.sha.outputs.SHORT_SHA }} | |
| platforms: linux/amd64 | |
| cache-from: type=gha,scope=api | |
| cache-to: type=gha,mode=max,scope=api | |
| - name: "🏗️ Build & Push Staging Sandbox Image" | |
| uses: docker/build-push-action@v5 | |
| with: | |
| context: . | |
| file: ./docker/sandbox/Dockerfile | |
| push: true | |
| tags: thanosprime/projectdavid-core-sandbox:dev-${{ steps.sha.outputs.SHORT_SHA }} | |
| platforms: linux/amd64 | |
| cache-from: type=gha,scope=sandbox | |
| cache-to: type=gha,mode=max,scope=sandbox | |
| - name: "🏗️ Build & Push Staging Training Images" | |
| uses: docker/build-push-action@v5 | |
| with: | |
| context: . | |
| file: ./docker/training/Dockerfile | |
| push: true | |
| tags: | | |
| thanosprime/projectdavid-core-training-api:dev-${{ steps.sha.outputs.SHORT_SHA }} | |
| thanosprime/projectdavid-core-training-worker:dev-${{ steps.sha.outputs.SHORT_SHA }} | |
| platforms: linux/amd64 | |
| cache-from: type=gha,scope=training | |
| cache-to: type=gha,mode=max,scope=training | |
| - name: "🏗️ Build & Push Staging Inference Worker Image" | |
| uses: docker/build-push-action@v5 | |
| with: | |
| context: . | |
| file: ./docker/inference/Dockerfile | |
| push: true | |
| tags: thanosprime/projectdavid-core-inference-worker:dev-${{ steps.sha.outputs.SHORT_SHA }} | |
| platforms: linux/amd64 | |
| cache-from: type=gha,scope=inference | |
| cache-to: type=gha,mode=max,scope=inference | |
| - name: "🏗️ Build & Push Staging Router Image" | |
| uses: docker/build-push-action@v5 | |
| with: | |
| context: . | |
| file: ./docker/router/Dockerfile | |
| push: true | |
| tags: thanosprime/projectdavid-core-router:dev-${{ steps.sha.outputs.SHORT_SHA }} | |
| platforms: linux/amd64 | |
| cache-from: type=gha,scope=router | |
| cache-to: type=gha,mode=max,scope=router | |
| - name: "📋 Output staging image tags" | |
| run: | | |
| echo "## Staging images built from dev @ ${{ steps.sha.outputs.SHORT_SHA }}" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "| Service | Tag |" >> $GITHUB_STEP_SUMMARY | |
| echo "|---|---|" >> $GITHUB_STEP_SUMMARY | |
| echo "| api | \`dev-${{ steps.sha.outputs.SHORT_SHA }}\` |" >> $GITHUB_STEP_SUMMARY | |
| echo "| sandbox | \`dev-${{ steps.sha.outputs.SHORT_SHA }}\` |" >> $GITHUB_STEP_SUMMARY | |
| echo "| training-api | \`dev-${{ steps.sha.outputs.SHORT_SHA }}\` |" >> $GITHUB_STEP_SUMMARY | |
| echo "| training-worker | \`dev-${{ steps.sha.outputs.SHORT_SHA }}\` |" >> $GITHUB_STEP_SUMMARY | |
| echo "| inference-worker | \`dev-${{ steps.sha.outputs.SHORT_SHA }}\` |" >> $GITHUB_STEP_SUMMARY | |
| echo "| router | \`dev-${{ steps.sha.outputs.SHORT_SHA }}\` |" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "Pin these in platform \`docker-compose.yml\` for pre-release testing." >> $GITHUB_STEP_SUMMARY | |
| - name: "📦 Update staging repo docker-compose.yml" | |
| env: | |
| STAGING_REPO_PAT: ${{ secrets.STAGING_REPO_PAT }} | |
| SHORT_SHA: ${{ steps.sha.outputs.SHORT_SHA }} | |
| run: | | |
| git clone https://x-access-token:${STAGING_REPO_PAT}@github.com/project-david-ai/projectdavid-platform-staging.git /tmp/staging | |
| cd /tmp/staging | |
| git config user.email "ci@projectdavid.ai" | |
| git config user.name "Project David CI" | |
| sed -i "s/dev-[a-f0-9]\{8\}/dev-${SHORT_SHA}/g" docker-compose.yml | |
| sed -i "s/Pinned to staging build: dev-[a-f0-9]\{8\} ([0-9-]*)/Pinned to staging build: dev-${SHORT_SHA} ($(date +%Y-%m-%d))/g" docker-compose.yml | |
| git add docker-compose.yml | |
| git diff --cached --quiet && echo "No changes to commit" && exit 0 | |
| git commit -m "chore(deps): pin staging images to dev-${SHORT_SHA} [skip ci]" | |
| git push origin main | |
| # ────────────────────────────────────────────────────────────────────────── | |
| # 🚀 Build & Publish Docker images (MAIN BRANCH ONLY) | |
| # ────────────────────────────────────────────────────────────────────────── | |
| build_and_publish: | |
| name: "🚀 Build, Tag, and Publish Images to Docker Hub" | |
| runs-on: ubuntu-latest | |
| needs: test | |
| if: | | |
| github.event_name == 'push' && | |
| github.ref == 'refs/heads/main' && | |
| !contains(github.event.head_commit.message, '[skip ci]') && | |
| !contains(github.event.head_commit.message, 'chore(release)') | |
| steps: | |
| - name: "🕺 Checkout" | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| persist-credentials: true | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: "⚙️ Setup QEMU" | |
| uses: docker/setup-qemu-action@v3 | |
| - name: "⚙️ Setup Docker Buildx" | |
| uses: docker/setup-buildx-action@v3 | |
| - name: "🔑 Login to Docker Hub" | |
| uses: docker/login-action@v3 | |
| with: | |
| username: thanosprime | |
| password: ${{ secrets.DOCKERHUB_THANOSPRIME }} | |
| - name: "🚀 Setup Node.js" | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: "20" | |
| - name: "⚙️ Install semantic-release" | |
| run: | | |
| npm install -g semantic-release \ | |
| @semantic-release/commit-analyzer \ | |
| @semantic-release/release-notes-generator \ | |
| @semantic-release/changelog \ | |
| @semantic-release/exec \ | |
| @semantic-release/git \ | |
| @semantic-release/github | |
| - name: "🚀 Run semantic-release" | |
| id: semantic | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: npx semantic-release | |
| - name: "🏷️ Extract Git Tag Version" | |
| id: get_version | |
| run: | | |
| git fetch --tags origin | |
| VERSION=$(git describe --tags --exact-match HEAD 2>/dev/null || git describe --tags --abbrev=0 HEAD 2>/dev/null) | |
| if [ -z "$VERSION" ]; then | |
| echo "::error::Could not determine version tag after semantic-release." | |
| exit 1 | |
| fi | |
| VERSION=${VERSION#v} | |
| echo "Detected version: $VERSION" | |
| echo "VERSION=$VERSION" >> $GITHUB_OUTPUT | |
| - name: "🔧 Define API Image Metadata" | |
| id: meta_api | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: thanosprime/projectdavid-core-api | |
| tags: | | |
| type=semver,pattern={{version}},value=${{ steps.get_version.outputs.VERSION }} | |
| type=semver,pattern={{major}}.{{minor}},value=${{ steps.get_version.outputs.VERSION }} | |
| type=semver,pattern={{major}},value=${{ steps.get_version.outputs.VERSION }} | |
| type=raw,value=latest,enable={{is_default_branch}} | |
| type=sha,prefix=sha- | |
| - name: "🔧 Define Sandbox Image Metadata" | |
| id: meta_sandbox | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: thanosprime/projectdavid-core-sandbox | |
| tags: | | |
| type=semver,pattern={{version}},value=${{ steps.get_version.outputs.VERSION }} | |
| type=semver,pattern={{major}}.{{minor}},value=${{ steps.get_version.outputs.VERSION }} | |
| type=semver,pattern={{major}},value=${{ steps.get_version.outputs.VERSION }} | |
| type=raw,value=latest,enable={{is_default_branch}} | |
| type=sha,prefix=sha- | |
| - name: "🔧 Define Training API Image Metadata" | |
| id: meta_training_api | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: thanosprime/projectdavid-core-training-api | |
| tags: | | |
| type=semver,pattern={{version}},value=${{ steps.get_version.outputs.VERSION }} | |
| type=semver,pattern={{major}}.{{minor}},value=${{ steps.get_version.outputs.VERSION }} | |
| type=semver,pattern={{major}},value=${{ steps.get_version.outputs.VERSION }} | |
| type=raw,value=latest,enable={{is_default_branch}} | |
| type=sha,prefix=sha- | |
| - name: "🔧 Define Training Worker Image Metadata" | |
| id: meta_training_worker | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: thanosprime/projectdavid-core-training-worker | |
| tags: | | |
| type=semver,pattern={{version}},value=${{ steps.get_version.outputs.VERSION }} | |
| type=semver,pattern={{major}}.{{minor}},value=${{ steps.get_version.outputs.VERSION }} | |
| type=semver,pattern={{major}},value=${{ steps.get_version.outputs.VERSION }} | |
| type=raw,value=latest,enable={{is_default_branch}} | |
| type=sha,prefix=sha- | |
| - name: "🔧 Define Inference Worker Image Metadata" | |
| id: meta_inference_worker | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: thanosprime/projectdavid-core-inference-worker | |
| tags: | | |
| type=semver,pattern={{version}},value=${{ steps.get_version.outputs.VERSION }} | |
| type=semver,pattern={{major}}.{{minor}},value=${{ steps.get_version.outputs.VERSION }} | |
| type=semver,pattern={{major}},value=${{ steps.get_version.outputs.VERSION }} | |
| type=raw,value=latest,enable={{is_default_branch}} | |
| type=sha,prefix=sha- | |
| - name: "🔧 Define Router Image Metadata" | |
| id: meta_router | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: thanosprime/projectdavid-core-router | |
| tags: | | |
| type=semver,pattern={{version}},value=${{ steps.get_version.outputs.VERSION }} | |
| type=semver,pattern={{major}}.{{minor}},value=${{ steps.get_version.outputs.VERSION }} | |
| type=semver,pattern={{major}},value=${{ steps.get_version.outputs.VERSION }} | |
| type=raw,value=latest,enable={{is_default_branch}} | |
| type=sha,prefix=sha- | |
| - name: "🏗️ Build & Push API Image" | |
| uses: docker/build-push-action@v5 | |
| with: | |
| context: . | |
| file: ./docker/api/Dockerfile | |
| push: true | |
| tags: ${{ steps.meta_api.outputs.tags }} | |
| labels: ${{ steps.meta_api.outputs.labels }} | |
| platforms: linux/amd64 | |
| cache-from: type=gha,scope=api | |
| cache-to: type=gha,mode=max,scope=api | |
| - name: "🏗️ Build & Push Sandbox Image" | |
| uses: docker/build-push-action@v5 | |
| with: | |
| context: . | |
| file: ./docker/sandbox/Dockerfile | |
| push: true | |
| tags: ${{ steps.meta_sandbox.outputs.tags }} | |
| labels: ${{ steps.meta_sandbox.outputs.labels }} | |
| platforms: linux/amd64 | |
| cache-from: type=gha,scope=sandbox | |
| cache-to: type=gha,mode=max,scope=sandbox | |
| - name: "🏗️ Build & Push Training Images (training-api + training-worker)" | |
| uses: docker/build-push-action@v5 | |
| with: | |
| context: . | |
| file: ./docker/training/Dockerfile | |
| push: true | |
| tags: | | |
| ${{ steps.meta_training_api.outputs.tags }} | |
| ${{ steps.meta_training_worker.outputs.tags }} | |
| labels: ${{ steps.meta_training_api.outputs.labels }} | |
| platforms: linux/amd64 | |
| cache-from: type=gha,scope=training | |
| cache-to: type=gha,mode=max,scope=training | |
| - name: "🏗️ Build & Push Inference Worker Image" | |
| uses: docker/build-push-action@v5 | |
| with: | |
| context: . | |
| file: ./docker/inference/Dockerfile | |
| push: true | |
| tags: ${{ steps.meta_inference_worker.outputs.tags }} | |
| labels: ${{ steps.meta_inference_worker.outputs.labels }} | |
| platforms: linux/amd64 | |
| cache-from: type=gha,scope=inference | |
| cache-to: type=gha,mode=max,scope=inference | |
| - name: "🏗️ Build & Push Router Image" | |
| uses: docker/build-push-action@v5 | |
| with: | |
| context: . | |
| file: ./docker/router/Dockerfile | |
| push: true | |
| tags: ${{ steps.meta_router.outputs.tags }} | |
| labels: ${{ steps.meta_router.outputs.labels }} | |
| platforms: linux/amd64 | |
| cache-from: type=gha,scope=router | |
| cache-to: type=gha,mode=max,scope=router | |
| - name: "📋 Output production image tags" | |
| run: | | |
| VERSION="${{ steps.get_version.outputs.VERSION }}" | |
| MINOR=$(echo "${VERSION}" | cut -d. -f1-2) | |
| MAJOR=$(echo "${VERSION}" | cut -d. -f1) | |
| echo "## Production images released @ v${VERSION}" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "Each image is pushed with the following rolling tags:" >> $GITHUB_STEP_SUMMARY | |
| echo "- \`${VERSION}\` — exact version, immutable" >> $GITHUB_STEP_SUMMARY | |
| echo "- \`${MINOR}\` — rolling minor (auto-updates on patch releases)" >> $GITHUB_STEP_SUMMARY | |
| echo "- \`${MAJOR}\` — rolling major (auto-updates on minor + patch releases)" >> $GITHUB_STEP_SUMMARY | |
| echo "- \`latest\` — tip of main" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "| Service | Exact | Minor | Major |" >> $GITHUB_STEP_SUMMARY | |
| echo "|---|---|---|---|" >> $GITHUB_STEP_SUMMARY | |
| echo "| api | \`${VERSION}\` | \`${MINOR}\` | \`${MAJOR}\` |" >> $GITHUB_STEP_SUMMARY | |
| echo "| sandbox | \`${VERSION}\` | \`${MINOR}\` | \`${MAJOR}\` |" >> $GITHUB_STEP_SUMMARY | |
| echo "| training-api | \`${VERSION}\` | \`${MINOR}\` | \`${MAJOR}\` |" >> $GITHUB_STEP_SUMMARY | |
| echo "| training-worker | \`${VERSION}\` | \`${MINOR}\` | \`${MAJOR}\` |" >> $GITHUB_STEP_SUMMARY | |
| echo "| inference-worker | \`${VERSION}\` | \`${MINOR}\` | \`${MAJOR}\` |" >> $GITHUB_STEP_SUMMARY | |
| echo "| router | \`${VERSION}\` | \`${MINOR}\` | \`${MAJOR}\` |" >> $GITHUB_STEP_SUMMARY | |
| - name: "📦 Update projectdavid-platform with production image tags" | |
| env: | |
| PLATFORM_REPO_PAT: ${{ secrets.PLATFORM_REPO_PAT }} | |
| VERSION: ${{ steps.get_version.outputs.VERSION }} | |
| run: | | |
| git clone https://x-access-token:${PLATFORM_REPO_PAT}@github.com/project-david-ai/projectdavid-platform.git /tmp/platform | |
| cd /tmp/platform | |
| git config user.email "ci@projectdavid.ai" | |
| git config user.name "Project David CI" | |
| # ────────────────────────────────────────────────────────────── | |
| # Rolling-tag guard: | |
| # Platform YMLs pin core images to the MINOR tag (e.g. :1.39). | |
| # Docker Hub re-tags :1.39 on every patch release, so patch-level | |
| # bumps (1.39.0 → 1.39.1) propagate to operators automatically on | |
| # their next `docker compose pull` / stack restart — NO platform | |
| # release needed. | |
| # | |
| # This guard detects patch-only releases and exits early, leaving | |
| # the platform repo untouched. Only minor/major bumps trigger a | |
| # platform update (which cuts a new platform release downstream). | |
| # ────────────────────────────────────────────────────────────── | |
| CURR_MINOR=$(echo "${VERSION}" | cut -d. -f1-2) | |
| PREV_MINOR=$(grep -oP 'projectdavid-core-api:\K[0-9]+\.[0-9]+' docker-compose.yml 2>/dev/null | head -1 || echo "") | |
| echo "Previous platform pinned minor: ${PREV_MINOR:-<none>}" | |
| echo "Current core release minor: ${CURR_MINOR}" | |
| if [ -n "${PREV_MINOR}" ] && [ "${PREV_MINOR}" = "${CURR_MINOR}" ]; then | |
| echo "✅ Patch release within same minor series (${CURR_MINOR}.x)." | |
| echo " Operators on :${CURR_MINOR} will pull new patch on next restart." | |
| echo " No platform update required. Exiting cleanly." | |
| exit 0 | |
| fi | |
| echo "🚀 Minor/major bump detected (${PREV_MINOR:-<none>} → ${CURR_MINOR})." | |
| echo " Updating platform YMLs to pin :${CURR_MINOR}" | |
| YML_FILES=( | |
| "docker-compose.yml" | |
| "docker-compose.gpu.yml" | |
| "docker-compose.ollama.yml" | |
| "docker-compose.training.yml" | |
| "docker-compose.vllm.yml" | |
| "projectdavid_platform/docker-compose.yml" | |
| "projectdavid_platform/docker-compose.gpu.yml" | |
| "projectdavid_platform/docker-compose.ollama.yml" | |
| "projectdavid_platform/docker-compose.training.yml" | |
| "projectdavid_platform/docker-compose.vllm.yml" | |
| ) | |
| for FILE in "${YML_FILES[@]}"; do | |
| if [ ! -f "$FILE" ]; then | |
| echo "Skipping missing file: $FILE" | |
| continue | |
| fi | |
| # Pin to MINOR tag (e.g. :1.39) so patches auto-propagate. | |
| sed -i "s|thanosprime/projectdavid-core-\([^:]*\):[0-9]*\.[0-9]*\.[0-9]*|thanosprime/projectdavid-core-\1:${CURR_MINOR}|g" "$FILE" | |
| sed -i "s|thanosprime/projectdavid-core-\([^:]*\):[0-9]*\.[0-9]*\b|thanosprime/projectdavid-core-\1:${CURR_MINOR}|g" "$FILE" | |
| sed -i "s|thanosprime/projectdavid-core-\([^:]*\):latest|thanosprime/projectdavid-core-\1:${CURR_MINOR}|g" "$FILE" | |
| echo "Updated: $FILE" | |
| done | |
| if [ -f "PINNED_IMAGES.md" ]; then | |
| sed -i "s|:[0-9]*\.[0-9]*\.[0-9]*|:${CURR_MINOR}|g" PINNED_IMAGES.md | |
| sed -i "s|:latest|:${CURR_MINOR}|g" PINNED_IMAGES.md | |
| echo "Updated: PINNED_IMAGES.md" | |
| fi | |
| git add -A | |
| git diff --cached --quiet && echo "No changes to commit" && exit 0 | |
| git commit -m "chore(deps): pin production images to :${CURR_MINOR} (core v${VERSION}) [skip ci] | |
| Released from projectdavid-core @ $(date +%Y-%m-%d) | |
| Platform YMLs now pin to rolling minor tag :${CURR_MINOR}. | |
| Patch releases within ${CURR_MINOR}.x will propagate automatically | |
| on operator restart — no new platform release required. | |
| All docker-compose variants updated (root + projectdavid_platform/)." | |
| git push origin main |