fix(eval): install /usr/bin/relearn-eval on the CUDA scoring image #9
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: publish-eval-image | |
| # Publishes ghcr.io/cortexlm/relearn-eval and prints the pushed sha256 digest. | |
| # That digest is what the control plane pins in `config/relearn-pin.toml` as | |
| # `eval_image_digest`; a tag is never the pin. | |
| # | |
| # Two images, two Dockerfiles: | |
| # | |
| # :<sha> scoring — CUDA base + torch. Pin this, and only after | |
| # this job has pulled THAT digest and run the harvest-PATH | |
| # check against it. | |
| # :<sha>-contract slim contract only. Fast, cannot score, not the pin. | |
| # | |
| # The contract image publishes first and independently, so a runtime build | |
| # that runs out of runner disk cannot take the whole publish down with it. | |
| on: | |
| push: | |
| branches: ["main", "cursor/**"] | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| packages: write | |
| concurrency: | |
| group: publish-${{ github.ref }} | |
| cancel-in-progress: false | |
| jobs: | |
| contract: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| digest: ${{ steps.push.outputs.digest }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: resolve the image name | |
| # A registry repository must be lowercase; the owner is CortexLM. | |
| run: echo "IMAGE=ghcr.io/${GITHUB_REPOSITORY_OWNER,,}/relearn-eval" >> "${GITHUB_ENV}" | |
| - uses: docker/setup-buildx-action@v3 | |
| - uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: build and push the contract image | |
| id: push | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| file: eval/Dockerfile | |
| push: true | |
| provenance: false | |
| platforms: linux/amd64 | |
| tags: | | |
| ${{ env.IMAGE }}:${{ github.sha }}-contract | |
| build-args: | | |
| WITH_RUNTIME=0 | |
| RELEARN_GIT_SHA=${{ github.sha }} | |
| cache-from: type=gha,scope=contract | |
| cache-to: type=gha,mode=max,scope=contract | |
| - name: report | |
| run: | | |
| { | |
| echo "### relearn-eval (contract only)" | |
| echo | |
| echo '```' | |
| echo "image = \"${IMAGE}\"" | |
| echo "digest = \"${{ steps.push.outputs.digest }}\"" | |
| echo '```' | |
| echo | |
| echo "Contract layer only: slim, no model runtime, so it refuses to score." | |
| echo "Do not pin this digest." | |
| } >> "${GITHUB_STEP_SUMMARY}" | |
| runtime: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| digest: ${{ steps.push.outputs.digest }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: resolve the image name | |
| run: echo "IMAGE=ghcr.io/${GITHUB_REPOSITORY_OWNER,,}/relearn-eval" >> "${GITHUB_ENV}" | |
| - name: free disk for the CUDA base and runtime wheels | |
| run: | | |
| sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc \ | |
| /opt/hostedtoolcache /usr/local/share/boost \ | |
| /opt/hostedtoolcache/CodeQL || true | |
| df -h / | |
| - uses: docker/setup-buildx-action@v3 | |
| - uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: build and push the scoring image | |
| id: push | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| file: eval/Dockerfile.scoring | |
| push: true | |
| provenance: false | |
| platforms: linux/amd64 | |
| tags: | | |
| ${{ env.IMAGE }}:${{ github.sha }} | |
| build-args: | | |
| WITH_RUNTIME=1 | |
| RELEARN_GIT_SHA=${{ github.sha }} | |
| # No layer cache: the CUDA base plus wheels are several GiB, which | |
| # is past what the Actions cache will hold, and a failed cache | |
| # export would fail a build that otherwise produced a pinnable | |
| # digest. | |
| - name: pull the published digest and prove harvest PATH | |
| # Live 127 on sha256:86240d76: the scoring image harvest booted did | |
| # not contain a runnable /usr/bin/relearn-eval. CI's harvest-PATH | |
| # check had only ever run against the slim contract image. This | |
| # step pulls the digest we just pushed — the bytes a pod will get — | |
| # and refuses to report a pin if the file is missing or 127s. | |
| env: | |
| DIGEST: ${{ steps.push.outputs.digest }} | |
| run: | | |
| set -eu | |
| case "${DIGEST}" in | |
| sha256:*) ;; | |
| *) echo "no sha256 digest was published"; exit 1 ;; | |
| esac | |
| ref="${IMAGE}@${DIGEST}" | |
| docker pull "${ref}" | |
| docker run --rm --entrypoint /bin/sh "${ref}" -c \ | |
| 'test -f /usr/bin/relearn-eval && test -x /usr/bin/relearn-eval && env -i PATH=/usr/bin:/bin /usr/bin/relearn-eval --help' | |
| docker run --rm --entrypoint /bin/sh "${ref}" -c \ | |
| 'test ! -L /usr/bin/relearn-eval' | |
| docker run --rm --entrypoint /bin/sh "${ref}" -c \ | |
| 'env -i PATH=/usr/bin:/bin /usr/bin/relearn-eval score --help' | |
| - name: report the digest to pin | |
| env: | |
| DIGEST: ${{ steps.push.outputs.digest }} | |
| run: | | |
| set -eu | |
| case "${DIGEST}" in | |
| sha256:*) ;; | |
| *) echo "no sha256 digest was published"; exit 1 ;; | |
| esac | |
| { | |
| echo "### relearn-eval (scoring image, CUDA base)" | |
| echo | |
| echo "Pulled this digest after push and ran the harvest-PATH check." | |
| echo "Paste into the control plane's \`config/relearn-pin.toml\`:" | |
| echo | |
| echo '```toml' | |
| echo "eval_image = \"${IMAGE}\"" | |
| echo "eval_image_digest = \"${DIGEST}\"" | |
| echo "relearn_git_sha = \"${GITHUB_SHA}\"" | |
| echo '```' | |
| } >> "${GITHUB_STEP_SUMMARY}" | |
| echo "eval_image_digest = \"${DIGEST}\"" |