name: Daily Update Pipeline on: schedule: # Runs every day at 3:00 AM UTC - cron: '0 3 * * *' workflow_dispatch: # Allows manual triggering inputs: skip_image_check: description: 'Skip image check and force build' required: false default: 'false' type: choice options: - 'true' - 'false' jobs: get-mlflow-full-base-image-digest: if: github.event.inputs.skip_image_check != 'true' runs-on: ubuntu-latest outputs: base_image_digest: ${{ steps.mlflow-full.outputs.base_image_digest }} success: ${{ steps.mlflow-full.outputs.success }} steps: - name: Get MLflow-full base image digest id: mlflow-full env: CI_RUNNER_TOKEN: ${{ secrets.CI_RUNNER_TOKEN }} run: | # Configure Docker for insecure registry mkdir -p ~/.docker echo '{ "auths": {}, "insecure-registries": ["10.0.0.2:3000"] }' > ~/.docker/config.json # Login to internal registry echo "${{ secrets.CI_RUNNER_TOKEN }}" | docker login 10.0.0.2:3000 --username "${{ gitea.actor }}" --password-stdin # Pull and inspect the image if docker pull 10.0.0.2:3000/brian/mlflow-full:latest 2>/dev/null; then BASE_IMAGE_DIGEST=$(docker inspect 10.0.0.2:3000/brian/mlflow-full:latest | jq -r '.[0].Config.Labels["org.opencontainers.image.base.digest"] // empty') if [ -n "$BASE_IMAGE_DIGEST" ] && [ "$BASE_IMAGE_DIGEST" != "null" ]; then echo "base_image_digest=$BASE_IMAGE_DIGEST" >> $GITHUB_OUTPUT echo "success=true" >> $GITHUB_OUTPUT echo "Base image digest from MLflow-full image: $BASE_IMAGE_DIGEST" else echo "success=false" >> $GITHUB_OUTPUT echo "No base image digest label found" fi else echo "success=false" >> $GITHUB_OUTPUT echo "Failed to pull MLflow-full image - this might be the first run" fi get-mlflow-digest: runs-on: ubuntu-latest outputs: digest: ${{ steps.mlflow-base.outputs.digest }} success: ${{ steps.mlflow-base.outputs.success }} steps: - name: Get MLflow base image digest id: mlflow-base run: | MANIFEST=$(docker manifest inspect ghcr.io/mlflow/mlflow:latest 2>/dev/null || echo "{}") if [ "$MANIFEST" != "{}" ]; then DIGEST=$(echo "$MANIFEST" | jq -r '.manifests[]? | select(.platform.architecture == "amd64" and .platform.os == "linux") | .digest // empty') echo "digest=$DIGEST" >> $GITHUB_OUTPUT echo "success=true" >> $GITHUB_OUTPUT echo "Digest found for MLflow base image: $DIGEST" else echo "success=false" >> $GITHUB_OUTPUT echo "MLflow base image not found" fi decide-to-build: if: always() # Run regardless of dependency status needs: - get-mlflow-full-base-image-digest - get-mlflow-digest runs-on: ubuntu-latest outputs: should_build: ${{ steps.decision.outputs.should_build }} steps: - name: Build decision id: decision run: | if [ "${{ github.event.inputs.skip_image_check }}" == "true" ]; then echo "Image check skipped, forcing build" echo "should_build=true" >> $GITHUB_OUTPUT elif [ "${{ steps.mlflow-base.outputs.success }}" == "true" ] && \ [ "${{ steps.mlflow-full.outputs.success }}" == "true" ]; then echo "✅ Both MLflow base and MLflow-full images found" if [ "${{ steps.mlflow-base.outputs.digest }}" != "${{ steps.mlflow-full.outputs.base_image_digest }}" ]; then echo "Base image digest has changed" echo "should_build=true" >> $GITHUB_OUTPUT else echo "Base image digest is unchanged" echo "should_build=false" >> $GITHUB_OUTPUT fi elif [ "${{ steps.mlflow-base.outputs.success }}" != "true" ]; then echo "❌ MLflow base image not found" echo "should_build=false" >> $GITHUB_OUTPUT else echo "❌ MLflow-full image not found" echo "should_build=false" >> $GITHUB_OUTPUT fi build-and-publish: if: needs.decide-to-build.outputs.should_build == 'true' needs: - get-mlflow-digest - decide-to-build runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v4 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 with: config-inline: | [registry."10.0.0.2:3000"] http = true insecure = true - name: Log in to Gitea Container Registry run: | # Configure Docker to allow insecure registry for this session mkdir -p ~/.docker echo '{ "auths": {}, "insecure-registries": ["10.0.0.2:3000"] }' > ~/.docker/config.json # Login using direct docker command echo "${{ secrets.CI_RUNNER_TOKEN }}" | docker login 10.0.0.2:3000 --username "${{ gitea.actor }}" --password-stdin - name: Extract metadata id: meta uses: docker/metadata-action@v5 with: images: 10.0.0.2:3000/${{ gitea.repository_owner }}/${{ gitea.event.repository.name }} tags: | type=schedule,pattern={{date 'YYYYMMDD'}} type=raw,value=latest labels: | org.opencontainers.image.title=mlflow-full org.opencontainers.image.description=MLflow with PostgreSQL support org.opencontainers.image.authors=Brian Jensen org.opencontainers.image.vendor=Brian Jensen org.opencontainers.image.licenses=Apache-2.0 org.opencontainers.image.base.name=ghcr.io/mlflow/mlflow:latest org.opencontainers.image.base.digest=${{ needs.get-mlflow-digest.outputs.digest }} maintainer=Brian Jensen - name: Build and push Docker image id: build-publish uses: docker/build-push-action@v5 with: context: . push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} cache-from: type=gha cache-to: type=gha,mode=max - name: Update deployment info run: | echo "Docker image built and pushed successfully at $(date)" echo "Image tags:" echo "${{ steps.meta.outputs.tags }}" echo "Image labels:" echo "${{ steps.meta.outputs.labels }}"