Skip to content

fix(training): handle TrainingConfig passed directly from router #303

fix(training): handle TrainingConfig passed directly from router

fix(training): handle TrainingConfig passed directly from router #303

Workflow file for this run

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