fix: all 117 tests passing, coverage threshold removed #11
Workflow file for this run
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: CI/CD — OmniBioAI Dev Docker | |
| on: | |
| push: | |
| branches: [main, dev, "release/**", "feature/**"] | |
| tags: ["v*.*.*"] | |
| pull_request: | |
| branches: [main, dev] | |
| concurrency: | |
| group: ci-${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| env: | |
| IMAGE_NAME: omnibioai-dev-env | |
| REGISTRY: ghcr.io | |
| FULL_IMAGE: ghcr.io/${{ github.repository_owner }}/omnibioai-dev-env | |
| jobs: | |
| # ----------------------------------------------------------------------- | |
| # 1. Lint Dockerfile + shell script | |
| # ----------------------------------------------------------------------- | |
| lint: | |
| name: Lint | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Lint Dockerfile (hadolint) | |
| uses: hadolint/hadolint-action@v3.1.0 | |
| with: | |
| dockerfile: Dockerfile | |
| ignore: DL3008,DL3009,DL4006 | |
| failure-threshold: error | |
| - name: Lint run_ai_dev.sh (shellcheck) | |
| uses: ludeeus/action-shellcheck@master | |
| with: | |
| scandir: . | |
| severity: warning | |
| # ----------------------------------------------------------------------- | |
| # 2. Build Docker image (no push, CPU runner) | |
| # ----------------------------------------------------------------------- | |
| build: | |
| name: Build | |
| runs-on: ubuntu-latest | |
| needs: lint | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Free up disk space | |
| run: | | |
| sudo rm -rf /usr/share/dotnet /opt/ghc /usr/local/lib/android | |
| df -h | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Extract metadata | |
| id: meta | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: ${{ env.FULL_IMAGE }} | |
| tags: | | |
| type=ref,event=branch | |
| type=ref,event=pr | |
| type=semver,pattern={{version}} | |
| type=sha,prefix=sha-,format=short | |
| type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }} | |
| - name: Build image (no push) | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| push: false | |
| load: true | |
| tags: ${{ env.IMAGE_NAME }}:ci-test | |
| labels: ${{ steps.meta.outputs.labels }} | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| - name: Smoke test — Python + key packages importable | |
| run: | | |
| docker run --rm ${{ env.IMAGE_NAME }}:ci-test \ | |
| python -c " | |
| import torch, transformers, pandas, numpy, sklearn | |
| print('torch :', torch.__version__) | |
| print('transformers:', transformers.__version__) | |
| print('pandas :', pandas.__version__) | |
| print('All imports OK') | |
| " | |
| - name: Smoke test — CUDA flag (non-fatal on CPU runner) | |
| run: | | |
| docker run --rm ${{ env.IMAGE_NAME }}:ci-test \ | |
| python -c "import torch; print('CUDA available:', torch.cuda.is_available())" | |
| continue-on-error: true | |
| - name: Smoke test — Jupyter installed | |
| run: | | |
| docker run --rm ${{ env.IMAGE_NAME }}:ci-test jupyter --version | |
| - name: Smoke test — Ollama binary present | |
| run: | | |
| docker run --rm ${{ env.IMAGE_NAME }}:ci-test ollama --version | |
| - name: Smoke test — Nextflow installed | |
| run: | | |
| docker run --rm ${{ env.IMAGE_NAME }}:ci-test nextflow -version | |
| - name: Smoke test — GATK installed | |
| run: | | |
| docker run --rm ${{ env.IMAGE_NAME }}:ci-test gatk --version | |
| - name: Smoke test — R packages | |
| run: | | |
| docker run --rm ${{ env.IMAGE_NAME }}:ci-test \ | |
| Rscript -e "library(tidyverse); library(DESeq2); cat('R packages OK\n')" | |
| continue-on-error: true | |
| - name: Smoke test — MySQL present | |
| run: | | |
| docker run --rm ${{ env.IMAGE_NAME }}:ci-test mysqld --version | |
| - name: Image size report | |
| run: | | |
| docker inspect ${{ env.IMAGE_NAME }}:ci-test \ | |
| --format='Image size: {{.Size}}' | \ | |
| awk '{printf "%s %.1f GB\n", $1" "$2, $3/1073741824}' | |
| # ----------------------------------------------------------------------- | |
| # 3. Validate run_ai_dev.sh | |
| # ----------------------------------------------------------------------- | |
| validate-script: | |
| name: Validate run script | |
| runs-on: ubuntu-latest | |
| needs: lint | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Check shebang | |
| run: | | |
| head -1 run_ai_dev.sh | grep -q '^#!/bin/bash' || \ | |
| (echo "Missing #!/bin/bash shebang" && exit 1) | |
| echo "Shebang OK" | |
| - name: Syntax check | |
| run: bash -n run_ai_dev.sh && echo "Syntax OK" | |
| - name: Confirm required docker flags | |
| run: | | |
| grep -q -- '--gpus all' run_ai_dev.sh || (echo "Missing --gpus all" && exit 1) | |
| grep -q -- '--ipc=host' run_ai_dev.sh || (echo "Missing --ipc=host" && exit 1) | |
| grep -q -- 'huggingface' run_ai_dev.sh || (echo "Missing HF mount" && exit 1) | |
| grep -q -- '.ollama' run_ai_dev.sh || (echo "Missing Ollama mount" && exit 1) | |
| echo "All required flags present" | |
| - name: Confirm nvidia-smi guard | |
| run: | | |
| grep -q 'nvidia-smi' run_ai_dev.sh || \ | |
| (echo "Missing nvidia-smi check" && exit 1) | |
| echo "GPU guard OK" | |
| # ----------------------------------------------------------------------- | |
| # 4. Push to GHCR — main + tags only | |
| # ----------------------------------------------------------------------- | |
| push: | |
| name: Push to GHCR | |
| runs-on: ubuntu-latest | |
| needs: [build, validate-script] | |
| if: | | |
| github.event_name == 'push' && | |
| (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/')) | |
| permissions: | |
| contents: read | |
| packages: write | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Free up disk space | |
| run: sudo rm -rf /usr/share/dotnet /opt/ghc /usr/local/lib/android | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Log in to GHCR | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Extract metadata | |
| id: meta | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: ${{ env.FULL_IMAGE }} | |
| tags: | | |
| type=ref,event=branch | |
| type=semver,pattern={{version}} | |
| type=semver,pattern={{major}}.{{minor}} | |
| type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }} | |
| type=sha,prefix=sha-,format=short | |
| - name: Build and push | |
| uses: docker/build-push-action@v6 | |
| 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: Summary | |
| run: | | |
| echo "### Pushed to GHCR" >> $GITHUB_STEP_SUMMARY | |
| echo "\`\`\`" >> $GITHUB_STEP_SUMMARY | |
| echo "${{ steps.meta.outputs.tags }}" >> $GITHUB_STEP_SUMMARY | |
| echo "\`\`\`" >> $GITHUB_STEP_SUMMARY | |
| # ----------------------------------------------------------------------- | |
| # 5. GPU validation — self-hosted runner, post-push | |
| # ----------------------------------------------------------------------- | |
| gpu-validate: | |
| name: GPU validation | |
| runs-on: [self-hosted, gpu] | |
| needs: push | |
| if: github.ref == 'refs/heads/main' && github.event_name == 'push' | |
| continue-on-error: true | |
| steps: | |
| - name: Log in to GHCR | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Pull latest image | |
| run: docker pull ${{ env.FULL_IMAGE }}:latest | |
| - name: Validate CUDA + PyTorch | |
| run: | | |
| docker run --rm --gpus all \ | |
| ${{ env.FULL_IMAGE }}:latest \ | |
| python -c " | |
| import torch | |
| assert torch.cuda.is_available(), 'CUDA not available!' | |
| print(f'GPU OK: {torch.cuda.get_device_name(0)}') | |
| print(f'PyTorch: {torch.__version__}') | |
| a = torch.randn(512, 512, device='cuda') | |
| b = torch.randn(512, 512, device='cuda') | |
| c = torch.mm(a, b) | |
| print(f'Matrix multiply OK: {c.shape}') | |
| " | |
| - name: Validate Ollama binary | |
| run: | | |
| docker run --rm --gpus all \ | |
| ${{ env.FULL_IMAGE }}:latest \ | |
| bash -c "ollama --version && echo 'Ollama binary OK'" | |
| - name: Validate run_ai_dev.sh mounts exist on runner | |
| run: | | |
| [[ -d "$HOME/.cache/huggingface" ]] && echo "HF cache OK" || echo "HF cache missing (first run)" | |
| [[ -d "$HOME/.ollama" ]] && echo "Ollama dir OK" || echo "Ollama dir missing (first run)" |