name: Docker Image on: workflow_call: inputs: is_test: type: boolean required: true description: Test Mode registry: type: string required: true description: Registry install_source: type: string required: true description: Source to install from (e.g., PyPI Package or Git source) permissions: contents: read packages: write attestations: write id-token: write actions: read security-events: write jobs: check_dockerfile: name: Check Dockerfile runs-on: ubuntu-latest if: ${{ inputs.is_test }} environment: name: code_quality steps: - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Check Dockerfile uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0 id: dockerfile_lint with: call: check - name: Job Summary uses: jazanne/job-summary-action@690eb386a0b86fe4da7c6f0e543e61330ff09f06 # v1.0.0 if: success() || failure() with: summary: | ## Dockerfile Check - **Status**: ${{ steps.dockerfile_lint.outcome == 'success' && ':white_check_mark:' || ':x:' }} build_image: name: Build and push Docker image to ${{ inputs.registry }} needs: check_dockerfile if: ${{ always() && !cancelled() && !failure() }} runs-on: ubuntu-latest outputs: image_tag: ${{ steps.tag.outputs.TAG }} environment: name: docker_image url: ${{inputs.registry}}/${{github.repository}} steps: - name: Free Disk Space uses: endersonmenezes/free-disk-space@e6ed9b02e683a3b55ed0252f1ee469ce3b39a885 # v3.1.0 with: remove_android: true remove_dotnet: true remove_haskell: true remove_tool_cache: true remove_swap: true remove_packages_one_command: true rm_cmd: rmz remove_folders: >- /usr/share /usr/local/lib /usr/local/share /usr/local - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Get Python version from pyproject.toml id: get_python_version uses: mikefarah/yq@065b200af9851db0d5132f50bc10b1406ea5c0a8 # v4.50.1 with: cmd: yq -roy '.project.requires-python' pyproject.toml - name: Log in to ${{ inputs.registry }} Registry uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0 with: registry: ${{ inputs.registry }} username: mh0386 password: ${{ inputs.registry == 'ghcr.io' && secrets.GH_TOKEN || inputs.registry == 'docker.io' && secrets.TOKEN_KEY_DOCKER }} - name: Set up QEMU uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0 - name: Set up Docker Buildx uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0 - name: Docker meta id: meta uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0 with: images: ${{ inputs.registry }}/${{ github.repository }} tags: | type=raw,value=latest,enable=${{ github.event_name == 'push' && contains(github.ref, 'refs/tags/') }} type=ref,event=pr,prefix={{sha}}-pr- type=ref,event=tag type=ref,event=branch - name: Build and Push to ${{ inputs.registry }} uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0 id: push with: push: true sbom: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} annotations: ${{ steps.meta.outputs.annotations }} build-args: | INSTALL_SOURCE=${{ inputs.install_source }} PYTHON_VERSION=${{ steps.get_python_version.outputs.result }} env: DOCKER_CONTENT_TRUST: '1' - name: Generate artifact attestation uses: actions/attest-build-provenance@96278af6caaf10aea03fd8d33a09a777ca52d62f # v3.2.0 if: ${{ inputs.is_test == false }} with: subject-name: ${{ inputs.registry == 'docker.io' && 'index.docker.io' || inputs.registry }}/${{github.repository}} subject-digest: ${{ steps.push.outputs.digest }} push-to-registry: true - name: Update Docker Hub Description if: ${{ inputs.registry == 'docker.io' }} uses: peter-evans/dockerhub-description@1b9a80c056b620d92cedb9d9b5a223409c68ddfa # v5.0.0 with: username: mh0386 password: ${{ secrets.TOKEN_KEY_DOCKER }} repository: ${{ github.repository }} short-description: ${{ github.event.repository.description }} enable-url-completion: true - name: Export tag for Testing and Scanning id: tag run: echo "TAG=$(echo "${{ steps.meta.outputs.tags }}" | tail -n 1)" >> $GITHUB_OUTPUT docker_scout: name: Docker Scout CVEs needs: build_image runs-on: ubuntu-latest environment: name: container_health steps: - name: Docker Scout uses: docker/scout-action@f8c776824083494ab0d56b8105ba2ca85c86e4de # v1.18.2 with: command: cves dockerhub-user: mh0386 dockerhub-password: ${{ secrets.TOKEN_KEY_DOCKER }} image: ${{ needs.build_image.outputs.image_tag }} github-token: ${{ secrets.GH_TOKEN }} trufflehog: name: TruffleHog runs-on: ubuntu-latest needs: build_image environment: name: container_health steps: - name: Install trufflehog uses: jaxxstorm/action-install-gh-release@6096f2a2bbfee498ced520b6922ac2c06e990ed2 # v2.1.0 with: repo: trufflesecurity/trufflehog cache: enable - name: Docker Secret Scanning run: >- trufflehog docker --image ${{needs.build_image.outputs.image_tag}} --fail --github-actions --results=verified --log-level=4 --no-update syft: name: Syft runs-on: ubuntu-latest needs: build_image environment: name: container_health permissions: contents: write steps: - name: SBOM Generation uses: anchore/sbom-action@deef08a0db64bfad603422135db61477b16cef56 # v0.22.1 with: image: ${{ needs.build_image.outputs.image_tag }} dependency-snapshot: true output-file: ${{ github.event.repository.name }}-sbom.json format: syft-json grype: name: Grype needs: build_image runs-on: ubuntu-latest environment: name: container_health steps: - name: Scan image uses: anchore/scan-action@8d2fce09422cd6037e577f4130e9b925e9a37175 # v7.3.1 id: scan with: image: ${{ needs.build_image.outputs.image_tag }} cache-db: true - name: upload Anchore scan SARIF report if: success() || failure() uses: github/codeql-action/upload-sarif@b20883b0cd1f46c72ae0ba6d1090936928f9fa30 # v4.32.0 with: sarif_file: ${{ steps.scan.outputs.sarif }} trivy: name: Trivy needs: build_image runs-on: ubuntu-latest environment: name: container_health steps: - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Run Trivy vulnerability scanner uses: aquasecurity/trivy-action@b6643a29fecd7f34b3597bc6acb0a98b03d33ff8 # 0.33.1 with: image-ref: ${{ needs.build_image.outputs.image_tag }} trivy-config: .github/lint/.trivy.yaml format: sarif output: trivy-results-image.sarif exit-code: '1' scanners: vuln,secret,misconfig,license - name: Upload Trivy scan results to GitHub Security tab if: success() || failure() uses: github/codeql-action/upload-sarif@b20883b0cd1f46c72ae0ba6d1090936928f9fa30 # v4.32.0 with: sarif_file: trivy-results-image.sarif dockle: name: Dockle needs: build_image runs-on: ubuntu-latest environment: name: container_health steps: - name: Lint the Container Image uses: goodwithtech/dockle-action@e30e6af832aad6ea7dca2a248d31a85eab6dbd68 # v0.4.15 with: image: ${{ needs.build_image.outputs.image_tag }} format: sarif output: dockle.sarif accept-file: settings.py accept-key: --chmod,--chown,GRADIO_SERVER_PORT,GRADIO_SERVER_NAME,FASTEMBED_CACHE_PATH,PATH ignore: CIS-DI-0006 - name: upload Dockle scan SARIF report if: success() || failure() uses: github/codeql-action/upload-sarif@b20883b0cd1f46c72ae0ba6d1090936928f9fa30 # v4.32.0 with: sarif_file: dockle.sarif api_test: name: API Test needs: build_image runs-on: ubuntu-latest if: ${{ inputs.is_test }} environment: name: container_health services: vector_database: image: qdrant/qdrant:latest@sha256:0425e3e03e7fd9b3dc95c4214546afe19de2eb2e28ca621441a56663ac6e1f46 ports: - 6333:6333 app: image: ${{ needs.build_image.outputs.image_tag }} ports: - 7860:7860 env: MODEL__URL: https://api.groq.com/openai/v1 MODEL__API_KEY: ${{ secrets.GROQ_API_KEY }} MODEL__NAME: llama3-70b-8192 VECTOR_DATABASE__URL: http://vector_database:6333 VECTOR_DATABASE__NAME: test options: >- --health-cmd "curl -o /dev/null -f -s -w 'Status: %{http_code}, Time: %{time_total}s' http://localhost:7860/" --health-interval 10s --health-timeout 10s --health-start-period 20s --health-retries 15 steps: - name: Echo URL run: echo "${{ github.event.repository.name }} available on localhost:${{ job.services.app.ports['7860'] }}" - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install uv uses: astral-sh/setup-uv@803947b9bd8e9f986429fa0c5a41c367cd732b41 # v7.2.1 with: enable-cache: true activate-environment: true - name: Install deps run: uv sync --only-dev - name: Test uses: pavelzw/pytest-action@510c5e90c360a185039bea56ce8b3e7e51a16507 # v2.2.0 with: click-to-expand: false custom-arguments: tests/test_app.py::test_app -p no:warnings env: MERGIFY_TOKEN: ${{ secrets.MERGIFY_TOKEN }} clean: name: Cleaning GHCR needs: - api_test - docker_scout - dockle - trivy - grype - syft - trufflehog if: ${{ inputs.registry == 'ghcr.io' && ( success() || failure() ) && !contains(github.event.head_commit.message, '[skip ghcr clean]') }} runs-on: ubuntu-latest environment: name: docker_image steps: - name: GHCR Cleaning uses: snok/container-retention-policy@3b0972b2276b171b212f8c4efbca59ebba26eceb # v3.0.1 with: account: ${{ github.repository_owner }} token: ${{ secrets.GH_TOKEN }} image-names: ${{ github.event.repository.name }} image-tags: '!latest !*.*.*' cut-off: 1s