Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 82 additions & 0 deletions .github/workflows/publish-image.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Manually-triggered build+push of the endpoints client image to GHCR.
#
# Run it from the Actions tab ("Run workflow"): pick the branch/tag in the UI (that
# selection is github.sha), or type an explicit SHA/branch/tag in the `ref` input.
# The image is pushed to ghcr.io/mlcommons/endpoints tagged with the short commit
# SHA and the ref name, using the automatic GITHUB_TOKEN (no stored secret needed).
name: Publish client image

on:
workflow_dispatch:
inputs:
ref:
description: "Git ref to build (branch/tag/SHA). Blank = the ref selected above."
required: false
type: string
platforms:
description: "Target platform(s)"
required: true
default: linux/amd64
type: choice
options:
- linux/amd64
- linux/amd64,linux/arm64
provision_dsr1:
description: "Bake in the DeepSeek-R1 accuracy evaluator (heavier; needs build-time network)"
required: true
default: "1"
type: choice
options:
- "1"
- "0"
no_cache:
description: "Build with --no-cache (clean/reproducible, slower)"
required: true
default: "true"
type: choice
options:
- "true"
- "false"

# GITHUB_TOKEN needs packages:write to push to the org's GHCR package.
permissions:
contents: read
packages: write

jobs:
publish:
runs-on: ubuntu-latest
steps:
- name: Checkout selected ref
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
# Check out the ref *name* (not github.sha) so a local branch head exists
# and the script can resolve/tag it; blank input falls back to the
# dispatched branch/tag. Full history for `git rev-parse --short`.
ref: ${{ inputs.ref || github.ref_name }}

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Codex] medium (correctness): This checks out the dispatch ref directly, replacing the tree before the run step. Building a ref that predates this workflow means ./scripts/push_docker_image.sh won't exist and the run fails — even though the UI advertises building arbitrary refs. It also lets the target ref supply the script that runs after GHCR login.

Fix: keep the checkout on the workflow's own (trusted) ref, and pass the target via ENDPOINTS_REF so the script switches refs only for the Docker build. (Ensure the target ref is fetched — the script's origin/<ref> fallback needs the object present.)

fetch-depth: 0

# Registers QEMU binfmt handlers so the docker-container builder can build
# the non-native arch when platforms includes linux/arm64.
- name: Set up QEMU
uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4.2.0

- name: Set up Docker Buildx

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Claude] low (design): This provisions a buildx builder that the script never uses — push_docker_image.sh always creates and selects its own endpoints-buildx (docker-container) builder (lines 173/191). buildx is preinstalled on ubuntu-latest, so this step is effectively idle. Either drop it, or have the script honor a caller-supplied builder. (The Set up QEMU step above is needed for multi-arch — keep it.)

uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0

# Logs in as the user who triggered the run; the password is this run's
# automatic GITHUB_TOKEN (scoped by the permissions block above).
- name: Log in to GitHub Container Registry
uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4.4.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Build and push
env:
ENDPOINTS_REF: ${{ inputs.ref || github.ref_name }}
PLATFORM: ${{ inputs.platforms }}
PROVISION_DSR1: ${{ inputs.provision_dsr1 }}
NO_CACHE: ${{ inputs.no_cache == 'true' && '1' || '0' }}
run: ./scripts/push_docker_image.sh
238 changes: 238 additions & 0 deletions scripts/push_docker_image.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
#!/usr/bin/env bash
# push_docker_image.sh — build and push the endpoints client image to a registry.
#
# Builds scripts/Dockerfile.dev at a given git ref and pushes it to GitHub
# Container Registry (default: ghcr.io/mlcommons/endpoints), tagged with both the
# short commit SHA and the ref name so consumers can pin either.
#
# Prerequisites:
# - docker with buildx (the script provisions a docker-container builder; the
# default 'docker' driver can neither --push nor build multi-platform).
# - Authenticated to the target registry. Either run `docker login ghcr.io`
# yourself, or export GHCR_USER + GHCR_TOKEN (a GitHub PAT / GITHUB_TOKEN with
# write:packages) and this script logs in for you.
#
# Usage:
# ./scripts/push_docker_image.sh # build+push current HEAD (linux/amd64)
# ENDPOINTS_REF=v1.2.0 ./scripts/push_docker_image.sh # build+push a specific tag/branch/sha
# ./scripts/push_docker_image.sh --ref main # same, via flag
# ./scripts/push_docker_image.sh --multi-arch # linux/amd64,linux/arm64 manifest list
# ./scripts/push_docker_image.sh --platform linux/arm64 # single non-native arch
# ./scripts/push_docker_image.sh --cache # allow layer cache (default: --no-cache)
# ./scripts/push_docker_image.sh --multi-arch --no-push # dry build (validate both arches, publish nothing)
# ./scripts/push_docker_image.sh --allow-dirty # build HEAD despite uncommitted tracked changes
#
# Multi-arch note: --multi-arch (or --platform with a comma) pushes a manifest
# list usable on BOTH x86 and arm hosts; Docker pulls the matching arch. The
# non-native leg needs QEMU registered on the host once:
# docker run --privileged --rm tonistiigi/binfmt --install all
# Dockerfile.dev's DSR1 provisioning runs under emulation and is MUCH slower.
#
# Environment variables (flags take precedence):
# IMAGE full image ref w/o tag (default: ghcr.io/mlcommons/endpoints)
# ENDPOINTS_REF git ref to build (default: current HEAD; checked out if it differs)
# PLATFORM target platform(s) (default: linux/amd64)
# DOCKERFILE Dockerfile path (default: scripts/Dockerfile.dev)
# NO_CACHE 1=--no-cache, 0=cache (default: 1)
# ALLOW_DIRTY 1=build HEAD with uncommitted tracked changes (default: 0)
# PROVISION_DSR1 passthrough build-arg (default: Dockerfile default = 1)
# IMAGE_DESCRIPTION GHCR package-page description (default: "endpoints client image (ref <ref>, commit <sha>)")
# GHCR_USER/GHCR_TOKEN optional registry login (fallback: GITHUB_ACTOR/GITHUB_TOKEN)
set -euo pipefail

REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
BUILDX_BUILDER="endpoints-buildx"

usage() {
sed -n '2,/^set -euo/p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//; /^set -euo/d'
}

IMAGE="${IMAGE:-ghcr.io/mlcommons/endpoints}"
ENDPOINTS_REF="${ENDPOINTS_REF:-}"
PLATFORM="${PLATFORM:-linux/amd64}"
DOCKERFILE="${DOCKERFILE:-scripts/Dockerfile.dev}"
NO_CACHE="${NO_CACHE:-1}"
ALLOW_DIRTY="${ALLOW_DIRTY:-0}"
PUSH=1

while [[ $# -gt 0 ]]; do
case "$1" in
--ref)
[[ -n "${2:-}" && "$2" != --* ]] || { echo "error: --ref requires a value" >&2; exit 1; }
ENDPOINTS_REF="$2"; shift ;;
--ref=*) ENDPOINTS_REF="${1#*=}" ;;
--image)
[[ -n "${2:-}" && "$2" != --* ]] || { echo "error: --image requires a value" >&2; exit 1; }
IMAGE="$2"; shift ;;
--image=*) IMAGE="${1#*=}" ;;
--platform)
[[ -n "${2:-}" && "$2" != --* ]] || { echo "error: --platform requires a value, e.g. linux/arm64" >&2; exit 1; }
PLATFORM="$2"; shift ;;
--platform=*) PLATFORM="${1#*=}" ;;
--multi-arch) PLATFORM="linux/amd64,linux/arm64" ;;
--no-cache) NO_CACHE=1 ;;
--cache) NO_CACHE=0 ;;
--no-push) PUSH=0 ;;
--allow-dirty) ALLOW_DIRTY=1 ;;
--dockerfile)
[[ -n "${2:-}" && "$2" != --* ]] || { echo "error: --dockerfile requires a value" >&2; exit 1; }
DOCKERFILE="$2"; shift ;;
--dockerfile=*) DOCKERFILE="${1#*=}" ;;
-h | --help) usage; exit 0 ;;
*) echo "error: unknown argument '$1'" >&2; usage; exit 1 ;;
esac
shift
done

cd "$REPO_ROOT"

if ! docker buildx version >/dev/null 2>&1; then
echo "error: 'docker buildx' is required but not available." >&2
exit 1
fi

# ---------------------------------------------------------------------------
# Optional registry login. Skipped silently if no token is provided (assumes the
# caller already ran `docker login`). Registry host is the first path segment of
# IMAGE (e.g. ghcr.io from ghcr.io/mlcommons/endpoints).
# ---------------------------------------------------------------------------
REGISTRY_HOST="${IMAGE%%/*}"
LOGIN_USER="${GHCR_USER:-${GITHUB_ACTOR:-}}"
LOGIN_TOKEN="${GHCR_TOKEN:-${GITHUB_TOKEN:-}}"
if [[ "$PUSH" == "1" && -n "$LOGIN_TOKEN" ]]; then
echo ">> Logging in to ${REGISTRY_HOST} as ${LOGIN_USER:-<token>}"
echo "$LOGIN_TOKEN" | docker login "$REGISTRY_HOST" -u "${LOGIN_USER:-x-access-token}" --password-stdin

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using echo to pipe sensitive tokens or arbitrary strings can lead to unexpected behavior if the token starts with a hyphen (e.g., -n or -e), as echo may interpret it as an option. It is safer and more robust to use printf '%s\n' instead.

Suggested change
echo "$LOGIN_TOKEN" | docker login "$REGISTRY_HOST" -u "${LOGIN_USER:-x-access-token}" --password-stdin
printf '%s\n' "$LOGIN_TOKEN" | docker login "$REGISTRY_HOST" -u "${LOGIN_USER:-x-access-token}" --password-stdin

fi

# ---------------------------------------------------------------------------
# Resolve the ref to build. If ENDPOINTS_REF is set and points at a different
# commit than the current HEAD, check it out (requiring a clean tree) and restore
# the original ref on exit so the working copy is left as we found it.
# ---------------------------------------------------------------------------
ORIGINAL_REF="$(git rev-parse --abbrev-ref HEAD 2>/dev/null || true)"
[[ "$ORIGINAL_REF" == "HEAD" ]] && ORIGINAL_REF="$(git rev-parse HEAD)"

restore_ref() {
if [[ -n "${DID_CHECKOUT:-}" && -n "$ORIGINAL_REF" ]]; then
echo ">> Restoring original ref ${ORIGINAL_REF}"
git checkout --quiet "$ORIGINAL_REF"
fi
}
trap restore_ref EXIT

# Uncommitted changes to TRACKED files (staged or unstaged) make the working tree
# differ from HEAD. Untracked files are ignored here: they are not part of any commit
# and .dockerignore governs the build context.
tree_has_tracked_changes() { ! git diff --quiet HEAD 2>/dev/null; }

if [[ -n "$ENDPOINTS_REF" ]]; then
# Fall back to the remote-tracking ref: after actions/checkout lands a detached
# SHA, a bare branch name like 'main' exists only as origin/main, so the plain
# resolve would fail and abort the CI default-dispatch path.
target_sha="$(git rev-parse --verify "${ENDPOINTS_REF}^{commit}" 2>/dev/null \
|| git rev-parse --verify "origin/${ENDPOINTS_REF}^{commit}" 2>/dev/null || true)"
[[ -n "$target_sha" ]] || { echo "error: '$ENDPOINTS_REF' is not a valid git ref." >&2; exit 1; }
if [[ "$target_sha" != "$(git rev-parse HEAD)" ]]; then
if tree_has_tracked_changes; then
echo "error: working tree has uncommitted changes to tracked files; commit/stash before building a different ref ($ENDPOINTS_REF)." >&2
exit 1
fi
echo ">> Checking out ${ENDPOINTS_REF}"
git checkout --quiet "$ENDPOINTS_REF"
DID_CHECKOUT=1
fi
else
# Tag the built image with the current branch/ref name for readability.
ENDPOINTS_REF="$ORIGINAL_REF"
fi

SHORT_SHA="$(git rev-parse --short HEAD)"
# Docker tags allow only [A-Za-z0-9_.-], must not start with '.' or '-', and cap at
# 128 chars. Map every other char (e.g. '/', '+', '~', '#') to '-' so a legitimate
# git ref name can't yield an invalid tag that fails `-t` only after the whole build.
REF_TAG="$(printf '%s' "$ENDPOINTS_REF" | tr -c 'A-Za-z0-9_.-' '-')"
[[ "$REF_TAG" == [A-Za-z0-9_]* ]] || REF_TAG="_${REF_TAG}"
REF_TAG="${REF_TAG:0:128}"

# Building the working tree as-is (no distinct ref checked out): don't publish a
# :$SHORT_SHA tag whose '.' build context doesn't match commit $SHORT_SHA.
if [[ -z "${DID_CHECKOUT:-}" && "$ALLOW_DIRTY" != "1" ]] && tree_has_tracked_changes; then

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Codex + Claude] medium (build reproducibility + context): The dirty guard here uses git diff --quiet HEAD, which ignores untracked files. But scripts/Dockerfile.dev:49 does COPY src/ ./src/, so an untracked file under src/ gets baked into the image — a local --push can publish :${SHORT_SHA} whose contents don't match commit ${SHORT_SHA}.

Separately, the comment at line 124 claims ".dockerignore governs the build context", but no .dockerignore exists in the repo, so the whole working tree is uploaded as build context (docker buildx build … ., line 197), pulling in large untracked dirs (cached_datasets/, gtc_results/, dsr1_v61_seed_logs_*/) on every local run.

Fix: add a .dockerignore (exclude artifacts + anything not COPYed); for the SHA-tag correctness, also include untracked files under COPYed paths in the cleanliness check, or build from git archive.

dirty_msg="working tree has uncommitted changes to tracked files; :${SHORT_SHA} would not match commit ${SHORT_SHA}"
if [[ "$PUSH" == "1" ]]; then
echo "error: ${dirty_msg}." >&2
echo " Commit/stash the changes, or pass --allow-dirty to publish anyway." >&2
exit 1
fi
echo ">> warning: ${dirty_msg} (dry run; nothing pushed)." >&2
fi

# ---------------------------------------------------------------------------
# Ensure a docker-container builder (supports --push and multi-platform).
# ---------------------------------------------------------------------------
if ! docker buildx inspect "$BUILDX_BUILDER" >/dev/null 2>&1; then
echo ">> Creating buildx builder '${BUILDX_BUILDER}' (docker-container driver)"
docker buildx create --name "$BUILDX_BUILDER" --driver docker-container >/dev/null
fi

if [[ "$PLATFORM" == *,* ]]; then
echo ">> Multi-arch build for ${PLATFORM} — requires QEMU for non-native archs"
echo " (register once with: docker run --privileged --rm tonistiigi/binfmt --install all)"
fi

BUILD_ARGS=()
[[ "$NO_CACHE" == "1" ]] && BUILD_ARGS+=(--no-cache)
[[ -n "${PROVISION_DSR1:-}" ]] && BUILD_ARGS+=(--build-arg "PROVISION_DSR1=${PROVISION_DSR1}")

# Don't attach SLSA provenance attestations. buildx adds them by default on --push,
# which surfaces a spurious `unknown/unknown` entry on the GHCR package page and
# wraps even a single-arch build in an image index. Opting out yields a clean,
# single-manifest artifact; the source/revision/version annotations below preserve
# the "which commit built this" traceability the attestation would have carried.
BUILD_ARGS+=(--provenance=false)

# With --no-push we build to cache and discard (validation only). A multi-platform
# result cannot be --load-ed into the local store, so no output flag is added.
[[ "$PUSH" == "1" ]] && BUILD_ARGS+=(--push)

# OCI annotations for the GHCR package page (description + repo/commit provenance).
# --annotation requires the build to actually produce the component named by the
# level prefix, so gate on --push and pick the level by artifact shape: a multi-arch
# build pushes an image index (GHCR reads the description from the index); a
# single-arch build (provenance off, above) pushes a lone manifest, for which
# `index:` errors with "index annotations not supported for single platform export".
if [[ "$PUSH" == "1" ]]; then
if [[ "$PLATFORM" == *,* ]]; then
ANNOTATION_LEVEL="index"
else
ANNOTATION_LEVEL="manifest"
fi
IMAGE_DESCRIPTION="${IMAGE_DESCRIPTION:-endpoints client image (ref ${ENDPOINTS_REF}, commit ${SHORT_SHA})}"
BUILD_ARGS+=(
--annotation "${ANNOTATION_LEVEL}:org.opencontainers.image.source=https://github.com/mlcommons/endpoints"
--annotation "${ANNOTATION_LEVEL}:org.opencontainers.image.revision=${SHORT_SHA}"
--annotation "${ANNOTATION_LEVEL}:org.opencontainers.image.version=${ENDPOINTS_REF}"
--annotation "${ANNOTATION_LEVEL}:org.opencontainers.image.description=${IMAGE_DESCRIPTION}"
)
fi

BUILD_VERB=$([[ "$PUSH" == "1" ]] && echo "and pushing" || echo "(dry run, --no-push)")
echo ">> Building ${IMAGE}:${SHORT_SHA} (ref: ${ENDPOINTS_REF}) for ${PLATFORM} ${BUILD_VERB}"
docker buildx build \
--builder "$BUILDX_BUILDER" \
--platform "$PLATFORM" \
-f "$DOCKERFILE" \
-t "${IMAGE}:${SHORT_SHA}" \
-t "${IMAGE}:${REF_TAG}" \
${BUILD_ARGS[@]+"${BUILD_ARGS[@]}"} \

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The array expansion ${BUILD_ARGS[@]+"${BUILD_ARGS[@]}"} is missing outer double quotes. Without outer double quotes, any array elements containing spaces or glob characters will undergo word splitting and pathname expansion when expanded. To safely preserve spaces within array elements while maintaining compatibility with set -u in older Bash versions, wrap the entire expansion in double quotes.

Suggested change
${BUILD_ARGS[@]+"${BUILD_ARGS[@]}"} \
"${BUILD_ARGS[@]+"${BUILD_ARGS[@]}"}" \

.

echo
if [[ "$PUSH" == "1" ]]; then
echo "Done. Pushed:"
echo " ${IMAGE}:${SHORT_SHA}"
echo " ${IMAGE}:${REF_TAG}"
echo "Pull with:"
echo " docker pull ${IMAGE}:${SHORT_SHA}"
else
echo "Done. Dry build succeeded for ${PLATFORM} (nothing pushed)."
fi
35 changes: 21 additions & 14 deletions src/inference_endpoint/evaluation/livecodebench/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,14 +182,14 @@ consumer can **pull and run it with no `HF_TOKEN` and no rebuild**.
Two scripts in this directory wrap the docker build/tag/push/pull steps. Both resolve the image reference from environment variables
(shared via `_image_env.sh`):

| Variable | Required | Default | Meaning |
| -------------------- | -------- | -------------------- | --------------------------------------------------------------------------- |
| `LCB_IMAGE_REGISTRY` | yes | — | registry + namespace, e.g. `myregistry.com/team` |
| `LCB_IMAGE_NAME` | no | `lcb-service` | image repo name |
| `LCB_IMAGE_TAG` | no | `release_v6` | tag; defaults to the baked-in dataset version so the tag is self-describing |
| `LCB_LOCAL_TAG` | no | `lcb-service:latest` | local tag the run command / scorer expect |
| Variable | Required | Default | Meaning |
| -------------------- | -------------------- | -------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| `LCB_IMAGE_REGISTRY` | yes | — | registry + namespace, e.g. `myregistry.com/team` |
| `LCB_IMAGE_NAME` | no | `lcb-service` | image repo name |
| `LCB_IMAGE_TAG` | push: no · pull: yes | endpoints short SHA | image tag; `push_image.sh` defaults it to the endpoints commit SHA (one immutable tag per build). Pull must name the build. |
| `LCB_LOCAL_TAG` | no | `lcb-service:latest` | local tag the run command / scorer expect |

The resolved remote reference is `${LCB_IMAGE_REGISTRY}/${LCB_IMAGE_NAME}:${LCB_IMAGE_TAG}`.
The resolved remote reference is `${LCB_IMAGE_REGISTRY}/${LCB_IMAGE_NAME}:${LCB_IMAGE_TAG}`. `push_image.sh` sets `LCB_IMAGE_TAG` to the endpoints commit short SHA by default, so each build publishes an immutable `…/lcb-service:<sha>` — there is no moving `latest`/`release_v6` tag. The same SHA is also baked into the image as the `org.opencontainers.image.revision` label.

#### Push (maintainer)

Expand All @@ -200,8 +200,14 @@ Requires `docker login dhi.io` (base images) and `docker login` to your target r
LCB_IMAGE_REGISTRY=myregistry.com/team HF_TOKEN=<your HuggingFace Token> \
./push_image.sh

# Or push an already-built local image without rebuilding:
LCB_IMAGE_REGISTRY=myregistry.com/team ./push_image.sh --no-build
# Or push an already-built local image without rebuilding (name the build explicitly):
LCB_IMAGE_REGISTRY=myregistry.com/team LCB_IMAGE_TAG=<sha> ./push_image.sh --no-build
```

Each build publishes an immutable `:<sha>` tag, so **re-pushing an existing `:<sha>` is refused** to protect it. Pass `--force` to overwrite deliberately (e.g. re-running a partially failed push):

```bash
LCB_IMAGE_REGISTRY=myregistry.com/team HF_TOKEN=<token> ./push_image.sh --force
```

**Cross-architecture builds.** To build for an architecture other than the host's (e.g. build `arm64` on an
Expand All @@ -227,11 +233,12 @@ docker run --privileged --rm tonistiigi/binfmt --install all

#### Pull (consumer / eval side)

No `HF_TOKEN` needed. By default the image is re-tagged locally as `lcb-service:latest`, so the
[hardened run command](#hardened-run-command) and the scorer's `ws://localhost:13835/evaluate` expectation work unchanged.
No `HF_TOKEN` needed. Set `LCB_IMAGE_TAG` to the build (short SHA) you want to pull. By default the image is re-tagged
locally as `lcb-service:latest`, so the [hardened run command](#hardened-run-command) and the scorer's
`ws://localhost:13835/evaluate` expectation work unchanged.

```bash
LCB_IMAGE_REGISTRY=myregistry.com/team ./pull_image.sh
LCB_IMAGE_REGISTRY=myregistry.com/team LCB_IMAGE_TAG=<sha> ./pull_image.sh
```

### (Only if using enroot) Generating a .sqsh file for enroot
Expand All @@ -240,8 +247,8 @@ The pull script can produce an enroot `.sqsh` from the pulled image with the `--
[enroot](https://github.com/NVIDIA/enroot/tree/main) (e.g. SLURM clusters):

```bash
LCB_IMAGE_REGISTRY=myregistry.com/team ./pull_image.sh --sqsh # writes lcb_service.sqsh
LCB_IMAGE_REGISTRY=myregistry.com/team ./pull_image.sh --sqsh out.sqsh # custom output path
LCB_IMAGE_REGISTRY=myregistry.com/team LCB_IMAGE_TAG=<sha> ./pull_image.sh --sqsh # writes lcb_service.sqsh
LCB_IMAGE_REGISTRY=myregistry.com/team LCB_IMAGE_TAG=<sha> ./pull_image.sh --sqsh out.sqsh # custom output path
```

This runs `enroot import --output <file> dockerd://${LCB_IMAGE_REF}` on the just-pulled image. Running the service via enroot is
Expand Down
Loading
Loading