From 536468c2a40aa6e0551cac425f4989dc549652d3 Mon Sep 17 00:00:00 2001 From: Simo Lin Date: Fri, 6 Mar 2026 09:05:23 -0800 Subject: [PATCH 1/4] refactor(docker): consolidate engine image build into reusable workflow Replace 3 near-identical workflow files and a 4-target Dockerfile with a single reusable workflow (_build-engine-image.yml) called by thin per-engine wrappers. Dockerfile changes: - Rename Dockerfile.engine to engine.Dockerfile - Replace 4 duplicate multi-target stages (custom-vllm, custom-sglang, custom-trtllm, custom-tgl) with a single parameterized stage using ENGINE build arg to select the install script at runtime - BACKEND build arg handles the tgl->sglang mapping - Single COPY of scripts/installation/ replaces 5 individual COPYs Workflow changes: - New _build-engine-image.yml reusable workflow containing all shared build logic: tag resolution, Docker build, GHCR push, cleanup - TRT-LLM source build path preserved as conditional steps (source_build_repo/source_build_ref inputs) - dry_run input for PR validation (build without push) - release-sglang-docker.yml now triggers on PRs that touch Docker/ workflow files for CI validation - All 3 wrappers reduced to ~50 lines each (from 148/144/213) - Unified tag resolution: {SMG_VERSION}-{ENGINE}-{ENGINE_VERSION} - All run: blocks use env vars instead of direct ${{ }} interpolation Signed-off-by: Simo Lin --- .github/workflows/_build-engine-image.yml | 256 ++++++++++++++++++++ .github/workflows/release-sglang-docker.yml | 146 +++-------- .github/workflows/release-trtllm-docker.yml | 206 ++-------------- .github/workflows/release-vllm-docker.yml | 132 ++-------- docker/Dockerfile.engine | 76 ------ docker/engine.Dockerfile | 61 +++++ 6 files changed, 392 insertions(+), 485 deletions(-) create mode 100644 .github/workflows/_build-engine-image.yml delete mode 100644 docker/Dockerfile.engine create mode 100644 docker/engine.Dockerfile diff --git a/.github/workflows/_build-engine-image.yml b/.github/workflows/_build-engine-image.yml new file mode 100644 index 0000000000..96887b4e15 --- /dev/null +++ b/.github/workflows/_build-engine-image.yml @@ -0,0 +1,256 @@ +name: _build-engine-image + +on: + workflow_call: + inputs: + engine: + description: 'Engine name: vllm, sglang, trtllm, tgl' + required: true + type: string + backend: + description: 'SMG_DEFAULT_BACKEND override (defaults to engine value)' + required: false + type: string + base_image_ref: + description: 'Base image (full image:tag)' + required: false + type: string + engine_repo: + description: 'Engine repo URL (empty = use engine from base image)' + required: false + type: string + engine_commit: + description: 'Engine commit/ref ("latest" for HEAD)' + required: false + default: 'latest' + type: string + smg_repo: + description: 'SMG repo URL' + required: false + default: 'https://github.com/lightseekorg/smg' + type: string + smg_commit: + description: 'SMG commit/ref ("latest" for HEAD)' + required: false + default: 'v1.1.0' + type: string + tag: + description: 'Override image tag' + required: false + type: string + # TRT-LLM source build (no-op for other engines) + source_build_repo: + description: 'Repo to build base image from source (TRT-LLM path)' + required: false + type: string + source_build_ref: + description: 'Ref/commit for source build repo' + required: false + default: 'latest' + type: string + dry_run: + description: 'Build only, do not push image' + required: false + default: false + type: boolean + +jobs: + build-and-push: + if: github.repository == 'lightseekorg/smg' + runs-on: ["8-gpu-h200"] + steps: + - name: Print inputs to summary + env: + ENGINE: ${{ inputs.engine }} + BASE_IMAGE_REF: ${{ inputs.base_image_ref }} + ENGINE_REPO: ${{ inputs.engine_repo }} + ENGINE_COMMIT: ${{ inputs.engine_commit }} + SMG_REPO: ${{ inputs.smg_repo }} + SMG_COMMIT: ${{ inputs.smg_commit }} + TAG: ${{ inputs.tag }} + SOURCE_BUILD_REPO: ${{ inputs.source_build_repo }} + SOURCE_BUILD_REF: ${{ inputs.source_build_ref }} + run: | + echo "## Inputs" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "| Parameter | Value |" >> $GITHUB_STEP_SUMMARY + echo "|-----------|-------|" >> $GITHUB_STEP_SUMMARY + echo "| engine | \`${ENGINE}\` |" >> $GITHUB_STEP_SUMMARY + echo "| base_image_ref | \`${BASE_IMAGE_REF}\` |" >> $GITHUB_STEP_SUMMARY + echo "| engine_repo | \`${ENGINE_REPO}\` |" >> $GITHUB_STEP_SUMMARY + echo "| engine_commit | \`${ENGINE_COMMIT}\` |" >> $GITHUB_STEP_SUMMARY + echo "| smg_repo | \`${SMG_REPO}\` |" >> $GITHUB_STEP_SUMMARY + echo "| smg_commit | \`${SMG_COMMIT}\` |" >> $GITHUB_STEP_SUMMARY + echo "| tag | \`${TAG}\` |" >> $GITHUB_STEP_SUMMARY + echo "| source_build_repo | \`${SOURCE_BUILD_REPO}\` |" >> $GITHUB_STEP_SUMMARY + echo "| source_build_ref | \`${SOURCE_BUILD_REF}\` |" >> $GITHUB_STEP_SUMMARY + + - name: Checkout SMG + uses: actions/checkout@v4 + + - name: Validate inputs + if: inputs.engine == 'trtllm' && inputs.base_image_ref == '' && inputs.source_build_repo == '' + run: | + echo "ERROR: For trtllm, either base_image_ref or source_build_repo must be set." >&2 + exit 1 + + # ── TRT-LLM source build (conditional) ────────────────────────────────── + + - name: Resolve source build checkout ref + id: source-ref + if: inputs.base_image_ref == '' && inputs.source_build_repo != '' + env: + SOURCE_BUILD_REF: ${{ inputs.source_build_ref }} + SOURCE_BUILD_REPO: ${{ inputs.source_build_repo }} + run: | + if [ "${SOURCE_BUILD_REF}" = "latest" ]; then + echo "ref=" >> "$GITHUB_OUTPUT" + else + echo "ref=${SOURCE_BUILD_REF}" >> "$GITHUB_OUTPUT" + fi + repo="${SOURCE_BUILD_REPO}" + repo="${repo#https://github.com/}" + repo="${repo%.git}" + repo="${repo%/}" + echo "repo=${repo}" >> "$GITHUB_OUTPUT" + + - name: Checkout source build repo + if: inputs.base_image_ref == '' && inputs.source_build_repo != '' + uses: actions/checkout@v4 + with: + repository: ${{ steps.source-ref.outputs.repo }} + ref: ${{ steps.source-ref.outputs.ref }} + token: ${{ secrets.GH_SYNC_TOKEN }} + fetch-depth: 0 + submodules: recursive + lfs: true + path: source-build-repo + + # ── Docker setup ───────────────────────────────────────────────────────── + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + with: + driver: docker + + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + # ── Resolve tag and effective base image ───────────────────────────────── + + - name: Resolve image tag and base ref + id: resolve + env: + ENGINE: ${{ inputs.engine }} + BASE_IMAGE_REF: ${{ inputs.base_image_ref }} + ENGINE_REPO: ${{ inputs.engine_repo }} + ENGINE_COMMIT: ${{ inputs.engine_commit }} + TAG_OVERRIDE: ${{ inputs.tag }} + run: | + SMG_VERSION=$(grep -m1 '^version = ' bindings/python/pyproject.toml | sed 's/version = "\(.*\)"/\1/') + + if [ -n "${TAG_OVERRIDE}" ]; then + IMAGE_TAG="${TAG_OVERRIDE}" + else + if [ -n "${ENGINE_REPO}" ]; then + ENGINE_VERSION="${ENGINE_COMMIT//\//-}" + ENGINE_VERSION="${ENGINE_VERSION// /}" + else + ENGINE_VERSION="${BASE_IMAGE_REF##*:}" + ENGINE_VERSION="${ENGINE_VERSION//\//-}" + fi + IMAGE_TAG="${SMG_VERSION}-${ENGINE}-${ENGINE_VERSION}" + fi + + echo "image_tag=${IMAGE_TAG}" >> "$GITHUB_OUTPUT" + + # Determine effective base image ref and engine repo + if [ -n "${BASE_IMAGE_REF}" ]; then + echo "base_image_ref=${BASE_IMAGE_REF}" >> "$GITHUB_OUTPUT" + echo "effective_engine_repo=${ENGINE_REPO}" >> "$GITHUB_OUTPUT" + else + # Source build path: engine is baked into the base image + echo "base_image_ref=smg-build/release:${IMAGE_TAG}" >> "$GITHUB_OUTPUT" + echo "effective_engine_repo=" >> "$GITHUB_OUTPUT" + fi + + echo "**Image tag:** \`${IMAGE_TAG}\`" >> $GITHUB_STEP_SUMMARY + + # ── TRT-LLM: build base image from source (conditional) ───────────────── + + - name: Build base image from source + if: inputs.base_image_ref == '' && inputs.source_build_repo != '' + env: + IMAGE_TAG: ${{ steps.resolve.outputs.image_tag }} + run: | + make -C source-build-repo/docker release_build \ + CUDA_ARCHS="100-real" \ + IMAGE_TAG="${IMAGE_TAG}" \ + IMAGE_NAME="smg-build" + echo "**Build base image from source:** done" >> $GITHUB_STEP_SUMMARY + + # ── Build engine image ─────────────────────────────────────────────────── + + - name: Build image + uses: docker/build-push-action@v6 + with: + context: . + file: docker/engine.Dockerfile + push: false + load: true + build-args: | + BASE_IMAGE_REF=${{ steps.resolve.outputs.base_image_ref }} + ENGINE=${{ inputs.engine }} + BACKEND=${{ inputs.backend }} + ENGINE_REPO=${{ steps.resolve.outputs.effective_engine_repo }} + ENGINE_COMMIT=${{ inputs.engine_commit }} + SMG_REPO=${{ inputs.smg_repo }} + SMG_COMMIT=${{ inputs.smg_commit }} + tags: smg-build:${{ steps.resolve.outputs.image_tag }} + cache-from: type=gha + cache-to: type=gha,mode=max + + # ── Push to GHCR ──────────────────────────────────────────────────────── + + - name: Push image to GHCR + if: ${{ !inputs.dry_run }} + id: push-ghcr + env: + IMAGE_TAG: ${{ steps.resolve.outputs.image_tag }} + run: | + BASE_IMAGE_TAG="smg-build:${IMAGE_TAG}" + TARGET_IMAGE="ghcr.io/${{ github.repository_owner }}/smg:${IMAGE_TAG}" + echo "image_name=${TARGET_IMAGE}" >> "$GITHUB_OUTPUT" + docker tag "${BASE_IMAGE_TAG}" "${TARGET_IMAGE}" + docker push "${TARGET_IMAGE}" + echo "**Pushed:** \`${TARGET_IMAGE}\`" >> $GITHUB_STEP_SUMMARY + + # ── Cleanup ────────────────────────────────────────────────────────────── + + - name: Clean up local images + if: always() + env: + IMAGE_TAG: ${{ steps.resolve.outputs.image_tag }} + PUSHED_IMAGE: ${{ steps.push-ghcr.outputs.image_name }} + BASE_REF: ${{ steps.resolve.outputs.base_image_ref }} + run: | + docker rmi "smg-build:${IMAGE_TAG}" || true + docker rmi "${PUSHED_IMAGE}" || true + if [ -n "${BASE_REF}" ]; then + docker rmi "${BASE_REF}" || true + fi + + - name: Summary + if: always() + env: + IMAGE_TAG: ${{ steps.resolve.outputs.image_tag }} + IMAGE_NAME: ${{ steps.push-ghcr.outputs.image_name }} + run: | + echo "## Image" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Image tag:** \`${IMAGE_TAG}\`" >> $GITHUB_STEP_SUMMARY + echo "**Image name:** \`${IMAGE_NAME}\`" >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/release-sglang-docker.yml b/.github/workflows/release-sglang-docker.yml index 569efed597..1c372589e5 100644 --- a/.github/workflows/release-sglang-docker.yml +++ b/.github/workflows/release-sglang-docker.yml @@ -1,28 +1,33 @@ -name: Release SMG_SGLang Docker Image +name: Release SMG+SGLang Docker Image -run-name: > - Release SMG_SGLang image | - base_image_ref=${{ github.event.inputs.base_image_ref }} | - sglang_repo=${{ github.event.inputs.sglang_repo }} | - sglang_commit=${{ github.event.inputs.sglang_commit }} | - smg_repo=${{ github.event.inputs.smg_repo }} | - smg_commit=${{ github.event.inputs.smg_commit }} | +run-name: >- + SMG+SGLang | + base=${{ inputs.base_image_ref || 'lmsysorg/sglang:v0.5.9' }} | + engine=${{ inputs.sglang_commit || 'latest' }} | + smg=${{ inputs.smg_commit || 'latest' }} | by @${{ github.actor }} on: + pull_request: + branches: [main] + paths: + - 'docker/engine.Dockerfile' + - '.github/workflows/_build-engine-image.yml' + - '.github/workflows/release-*-docker.yml' + - 'scripts/installation/**' workflow_dispatch: inputs: base_image_ref: - description: 'Base image (full image:tag, e.g. lmsysorg/sglang:v0.5.9)' + description: 'Base image (e.g. lmsysorg/sglang:v0.5.9)' default: 'lmsysorg/sglang:v0.5.9' required: true type: string sglang_repo: - description: 'SGLang repo URL, official: https://github.com/sgl-project/sglang.git, if empty, will not refresh engine code and use the one in base image' + description: 'SGLang repo URL (empty = use engine from base image)' required: false type: string sglang_commit: - description: 'SGLang commit SHA or ref (use "latest" for HEAD)' + description: 'SGLang commit/ref ("latest" for HEAD)' required: false default: 'latest' type: string @@ -32,116 +37,25 @@ on: default: 'https://github.com/lightseekorg/smg' type: string smg_commit: - description: 'SMG commit SHA or ref (use "latest" for HEAD)' + description: 'SMG commit/ref ("latest" for HEAD)' required: false default: 'v1.1.0' type: string tag: - description: 'Override image tag (e.g. v1.1.0-sglang-v0.5.9). If empty, tag is auto-generated.' + description: 'Override image tag (e.g. v1.1.0-sglang-v0.5.9)' required: false type: string jobs: - build-and-push: - if: github.repository == 'lightseekorg/smg' - runs-on: ["8-gpu-h200"] - steps: - - name: Print inputs to summary - run: | - echo "## Inputs" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "| Parameter | Value |" >> $GITHUB_STEP_SUMMARY - echo "|-----------|-------|" >> $GITHUB_STEP_SUMMARY - echo "| base_image_ref | \`${{ inputs.base_image_ref }}\` |" >> $GITHUB_STEP_SUMMARY - echo "| sglang_repo | \`${{ inputs.sglang_repo }}\` |" >> $GITHUB_STEP_SUMMARY - echo "| sglang_commit | \`${{ inputs.sglang_commit }}\` |" >> $GITHUB_STEP_SUMMARY - echo "| smg_repo | \`${{ inputs.smg_repo }}\` |" >> $GITHUB_STEP_SUMMARY - echo "| smg_commit | \`${{ inputs.smg_commit }}\` |" >> $GITHUB_STEP_SUMMARY - echo "| tag | \`${{ inputs.tag }}\` |" >> $GITHUB_STEP_SUMMARY - - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Login to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Resolve image tag - id: resolve-tag - env: - BASE_IMAGE_REF: ${{ inputs.base_image_ref }} - SGLANG_REPO: ${{ inputs.sglang_repo }} - SGLANG_COMMIT: ${{ inputs.sglang_commit }} - run: | - SMG_VERSION=$(grep -m1 '^version = ' bindings/python/pyproject.toml | sed 's/version = "\(.*\)"/\1/') - if [ -n "${{ inputs.tag }}" ]; then - IMAGE_TAG="${{ inputs.tag }}" - else - if [ -n "${SGLANG_REPO}" ]; then - SGLANG_COMMIT="${SGLANG_COMMIT//\//-}" - SGLANG_COMMIT="${SGLANG_COMMIT// /}" - else - # No engine repo: use the base image tag as the engine version identifier - SGLANG_COMMIT="${BASE_IMAGE_REF##*:}" - SGLANG_COMMIT="${SGLANG_COMMIT//\//-}" - fi - IMAGE_TAG="${SMG_VERSION}-sglang-${SGLANG_COMMIT}" - fi - echo "image_tag=${IMAGE_TAG}" >> "$GITHUB_OUTPUT" - echo "**Image tag:** \`${IMAGE_TAG}\`" >> $GITHUB_STEP_SUMMARY - - - name: Build image - uses: docker/build-push-action@v6 - with: - context: . - file: docker/Dockerfile.engine - target: custom-sglang - push: false - load: true - build-args: | - BASE_IMAGE_REF=${{ inputs.base_image_ref }} - ENGINE_REPO=${{ inputs.sglang_repo }} - ENGINE_COMMIT=${{ inputs.sglang_commit }} - SMG_REPO=${{ inputs.smg_repo }} - SMG_COMMIT=${{ inputs.smg_commit }} - tags: smg-build:${{ steps.resolve-tag.outputs.image_tag }} - cache-from: type=gha - cache-to: type=gha,mode=max - - - name: Push image to GHCR - id: push-ghcr - env: - IMAGE_TAG: ${{ steps.resolve-tag.outputs.image_tag }} - BASE_IMAGE_TAG: smg-build:${{ steps.resolve-tag.outputs.image_tag }} - run: | - echo "**Build image:** done" >> $GITHUB_STEP_SUMMARY - TARGET_IMAGE="ghcr.io/${{ github.repository_owner }}/smg:${IMAGE_TAG}" - echo "image_name=${TARGET_IMAGE}" >> "$GITHUB_OUTPUT" - docker tag "$BASE_IMAGE_TAG" "$TARGET_IMAGE" - docker push "$TARGET_IMAGE" - echo "**Push image:** \`${TARGET_IMAGE}\`" >> $GITHUB_STEP_SUMMARY - - - name: Clean up local images - if: always() - env: - IMAGE_TAG: ${{ steps.resolve-tag.outputs.image_tag }} - run: | - docker rmi "smg-build:${IMAGE_TAG}" || true - docker rmi "${{ steps.push-ghcr.outputs.image_name }}" || true - if [ -n "${{ inputs.base_image_ref }}" ]; then - docker rmi "${{ inputs.base_image_ref }}" || true - fi - - - name: Summary - if: always() - run: | - echo "## Image" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "**Image tag:** \`${{ steps.resolve-tag.outputs.image_tag }}\`" >> $GITHUB_STEP_SUMMARY - echo "**Image name:** \`${{ steps.push-ghcr.outputs.image_name }}\`" >> $GITHUB_STEP_SUMMARY + build: + uses: ./.github/workflows/_build-engine-image.yml + with: + engine: sglang + base_image_ref: ${{ inputs.base_image_ref || 'lmsysorg/sglang:v0.5.9' }} + engine_repo: ${{ inputs.sglang_repo }} + engine_commit: ${{ inputs.sglang_commit || 'latest' }} + smg_repo: ${{ inputs.smg_repo || 'https://github.com/lightseekorg/smg' }} + smg_commit: ${{ inputs.smg_commit || 'latest' }} + tag: ${{ inputs.tag }} + dry_run: ${{ github.event_name == 'pull_request' }} + secrets: inherit diff --git a/.github/workflows/release-trtllm-docker.yml b/.github/workflows/release-trtllm-docker.yml index 03a7c7a973..058ad2ec71 100644 --- a/.github/workflows/release-trtllm-docker.yml +++ b/.github/workflows/release-trtllm-docker.yml @@ -1,27 +1,25 @@ -name: Release SMG_TRTLLM Docker Image +name: Release SMG+TRTLLM Docker Image -run-name: > - Release SMG_TRTLLM image | - base_image_ref=${{ github.event.inputs.base_image_ref }} | - trtllm_repo=${{ github.event.inputs.trtllm_repo }} | - trtllm_commit=${{ github.event.inputs.trtllm_commit }} | - smg_repo=${{ github.event.inputs.smg_repo }} | - smg_commit=${{ github.event.inputs.smg_commit }} | +run-name: >- + SMG+TRTLLM | + base=${{ inputs.base_image_ref }} | + engine=${{ inputs.trtllm_commit }} | + smg=${{ inputs.smg_commit }} | by @${{ github.actor }} on: workflow_dispatch: inputs: base_image_ref: - description: 'Base image (full image:tag, e.g. nvcr.io/nvidia/tensorrt-llm/release:1.3.0rc6). If empty, base image will be built from trtllm_repo (engine code will NOT be refreshed in that case).' + description: 'Base image (e.g. nvcr.io/nvidia/tensorrt-llm/release:1.3.0rc6). Empty = build from source.' required: false type: string trtllm_repo: - description: 'TRTLLM repo URL (required when base_image_ref is empty to build base image), e.g. https://github.com/NVIDIA/TensorRT-LLM.git' + description: 'TRTLLM repo URL (required when base_image_ref is empty to build from source)' required: false type: string trtllm_commit: - description: 'TRTLLM commit SHA or ref (use "latest" for HEAD; only used when building base image)' + description: 'TRTLLM commit/ref ("latest" for HEAD)' required: false default: 'latest' type: string @@ -31,182 +29,26 @@ on: default: 'https://github.com/lightseekorg/smg' type: string smg_commit: - description: 'SMG commit SHA or ref (use "latest" for HEAD)' + description: 'SMG commit/ref ("latest" for HEAD)' required: false default: 'v1.1.0' type: string tag: - description: 'Override image tag (e.g. v1.1.0-trtllm-1.3.0). If empty, tag is auto-generated.' + description: 'Override image tag (e.g. v1.1.0-trtllm-1.3.0)' required: false type: string -env: - GITHUB_TOKEN: ${{ secrets.ROBOT_GITHUB_TOKEN }} jobs: - build-and-push: - if: github.repository == 'lightseekorg/smg' - runs-on: ["8-gpu-h200"] - steps: - - name: Print inputs to summary - run: | - echo "## Inputs" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "| Parameter | Value |" >> $GITHUB_STEP_SUMMARY - echo "|-----------|-------|" >> $GITHUB_STEP_SUMMARY - echo "| base_image_ref | \`${{ inputs.base_image_ref }}\` |" >> $GITHUB_STEP_SUMMARY - echo "| trtllm_repo | \`${{ inputs.trtllm_repo }}\` |" >> $GITHUB_STEP_SUMMARY - echo "| trtllm_commit | \`${{ inputs.trtllm_commit }}\` |" >> $GITHUB_STEP_SUMMARY - echo "| smg_repo | \`${{ inputs.smg_repo }}\` |" >> $GITHUB_STEP_SUMMARY - echo "| smg_commit | \`${{ inputs.smg_commit }}\` |" >> $GITHUB_STEP_SUMMARY - echo "| tag | \`${{ inputs.tag }}\` |" >> $GITHUB_STEP_SUMMARY - - - name: Checkout current repo - uses: actions/checkout@v4 - - - name: Validate inputs - env: - INPUT_BASE_IMAGE_REF: ${{ inputs.base_image_ref }} - TRTLLM_REPO: ${{ inputs.trtllm_repo }} - run: | - if [ -z "${INPUT_BASE_IMAGE_REF}" ] && [ -z "${TRTLLM_REPO}" ]; then - echo "ERROR: Either base_image_ref or trtllm_repo must be set." >&2 - exit 1 - fi - - # Only check out the TensorRT-LLM repo when base_image_ref is empty, - # so we can build the base image from source. - - name: Resolve trtllm checkout ref - id: trtllm-ref - if: ${{ github.event.inputs.base_image_ref == '' }} - run: | - if [ "${{ github.event.inputs.trtllm_commit }}" = "latest" ]; then - echo "ref=" >> "$GITHUB_OUTPUT" - else - echo "ref=${{ github.event.inputs.trtllm_commit }}" >> "$GITHUB_OUTPUT" - fi - # Strip protocol and host to get owner/repo (e.g. https://github.com/NVIDIA/TensorRT-LLM.git → NVIDIA/TensorRT-LLM) - repo="${{ github.event.inputs.trtllm_repo }}" - repo="${repo#https://github.com/}" - repo="${repo%.git}" - repo="${repo%/}" - echo "repo=${repo}" >> "$GITHUB_OUTPUT" - - - name: Checkout TensorRT-LLM repo (build base image) - if: ${{ github.event.inputs.base_image_ref == '' && github.event.inputs.trtllm_repo != '' }} - uses: actions/checkout@v4 - with: - repository: ${{ steps.trtllm-ref.outputs.repo }} - ref: ${{ steps.trtllm-ref.outputs.ref }} - token: ${{ secrets.GH_SYNC_TOKEN }} - fetch-depth: 0 - submodules: recursive - lfs: true - path: tensorrt-llm - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - with: - driver: docker - - - name: Login to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - # Resolve the effective base image reference and compute the final image tag. - # base_image_ref: provided → use as-is; empty → use image built from source below - # image_tag convention: -trtllm- - - name: Resolve image refs and tag - id: resolve-base - env: - INPUT_BASE_IMAGE_REF: ${{ inputs.base_image_ref }} - TRTLLM_REPO: ${{ inputs.trtllm_repo }} - TRTLLM_COMMIT: ${{ inputs.trtllm_commit }} - run: | - SMG_VERSION=$(grep -m1 '^version = ' bindings/python/pyproject.toml | sed 's/version = "\(.*\)"/\1/') - if [ -n "${{ inputs.tag }}" ]; then - IMAGE_TAG="${{ inputs.tag }}" - else - if [ -n "${TRTLLM_REPO}" ]; then - TRTLLM_COMMIT="${TRTLLM_COMMIT//\//-}" - TRTLLM_COMMIT="${TRTLLM_COMMIT// /}" - else - # No engine repo: use the base image tag as the engine version identifier - TRTLLM_COMMIT="${INPUT_BASE_IMAGE_REF##*:}" - TRTLLM_COMMIT="${TRTLLM_COMMIT//\//-}" - fi - IMAGE_TAG="${SMG_VERSION}-trtllm-${TRTLLM_COMMIT}" - fi - echo "image_tag=${IMAGE_TAG}" >> "$GITHUB_OUTPUT" - if [ -n "${INPUT_BASE_IMAGE_REF}" ]; then - echo "base_image_ref=${INPUT_BASE_IMAGE_REF}" >> "$GITHUB_OUTPUT" - echo "engine_repo=${TRTLLM_REPO}" >> "$GITHUB_OUTPUT" - else - echo "base_image_ref=smg-build/release:${IMAGE_TAG}" >> "$GITHUB_OUTPUT" - echo "engine_repo=" >> "$GITHUB_OUTPUT" - fi - echo "=== Outputs ===" - cat "$GITHUB_OUTPUT" - echo "**Image tag:** \`${IMAGE_TAG}\`" >> $GITHUB_STEP_SUMMARY - - # Build the TensorRT-LLM base image from source when no base_image_ref is given. - # Engine code IS baked into the base image in this path. - - name: Build base image from source - if: ${{ github.event.inputs.base_image_ref == '' && github.event.inputs.trtllm_repo != '' }} - run: | - make -C tensorrt-llm/docker release_build CUDA_ARCHS="100-real" IMAGE_TAG="${{ steps.resolve-base.outputs.image_tag }}" IMAGE_NAME="smg-build" - echo "**Build base image:** done" >> $GITHUB_STEP_SUMMARY - - - name: Build image - uses: docker/build-push-action@v6 - with: - context: . - file: docker/Dockerfile.engine - target: custom-trtllm - push: false - load: true - build-args: | - BASE_IMAGE_REF=${{ steps.resolve-base.outputs.base_image_ref }} - ENGINE_REPO=${{ steps.resolve-base.outputs.engine_repo }} - ENGINE_COMMIT=${{ inputs.trtllm_commit }} - SMG_REPO=${{ inputs.smg_repo }} - SMG_COMMIT=${{ inputs.smg_commit }} - tags: smg-build:${{ steps.resolve-base.outputs.image_tag }} - cache-from: type=gha - cache-to: type=gha,mode=max - - - name: Push image to GHCR - id: push-ghcr - env: - IMAGE_TAG: ${{ steps.resolve-base.outputs.image_tag }} - BASE_IMAGE_TAG: smg-build:${{ steps.resolve-base.outputs.image_tag }} - run: | - echo "**Build image:** done" >> $GITHUB_STEP_SUMMARY - TARGET_IMAGE="ghcr.io/${{ github.repository_owner }}/smg:${IMAGE_TAG}" - echo "image_name=${TARGET_IMAGE}" >> "$GITHUB_OUTPUT" - cat "$GITHUB_OUTPUT" - - docker tag "${BASE_IMAGE_TAG}" "${TARGET_IMAGE}" - docker push "${TARGET_IMAGE}" - echo "**Push image:** \`${TARGET_IMAGE}\`" >> $GITHUB_STEP_SUMMARY - - - name: Clean up local images - if: always() - env: - IMAGE_TAG: ${{ steps.resolve-base.outputs.image_tag }} - run: | - docker rmi "smg-build:${IMAGE_TAG}" || true - docker rmi "${{ steps.push-ghcr.outputs.image_name }}" || true - if [ -n "${{ inputs.base_image_ref }}" ]; then - docker rmi "${{ inputs.base_image_ref }}" || true - fi - - - name: Summary - if: always() - run: | - echo "## Image" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "**Image tag:** \`${{ steps.resolve-base.outputs.image_tag }}\`" >> $GITHUB_STEP_SUMMARY - echo "**Image name:** \`${{ steps.push-ghcr.outputs.image_name }}\`" >> $GITHUB_STEP_SUMMARY + build: + uses: ./.github/workflows/_build-engine-image.yml + with: + engine: trtllm + base_image_ref: ${{ inputs.base_image_ref }} + engine_repo: ${{ inputs.trtllm_repo }} + engine_commit: ${{ inputs.trtllm_commit }} + smg_repo: ${{ inputs.smg_repo }} + smg_commit: ${{ inputs.smg_commit }} + tag: ${{ inputs.tag }} + source_build_repo: ${{ inputs.trtllm_repo }} + source_build_ref: ${{ inputs.trtllm_commit }} + secrets: inherit diff --git a/.github/workflows/release-vllm-docker.yml b/.github/workflows/release-vllm-docker.yml index 56e964a6ce..294d0edb5e 100644 --- a/.github/workflows/release-vllm-docker.yml +++ b/.github/workflows/release-vllm-docker.yml @@ -1,28 +1,26 @@ -name: Release SMG_vLLM Docker Image +name: Release SMG+vLLM Docker Image -run-name: > - Release SMG_vLLM image | - base_image_ref=${{ github.event.inputs.base_image_ref }} | - vllm_repo=${{ github.event.inputs.vllm_repo }} | - vllm_commit=${{ github.event.inputs.vllm_commit }} | - smg_repo=${{ github.event.inputs.smg_repo }} | - smg_commit=${{ github.event.inputs.smg_commit }} | +run-name: >- + SMG+vLLM | + base=${{ inputs.base_image_ref }} | + engine=${{ inputs.vllm_commit }} | + smg=${{ inputs.smg_commit }} | by @${{ github.actor }} on: workflow_dispatch: inputs: base_image_ref: - description: 'Base image (full image:tag, e.g. vllm/vllm-openai:v0.16.0)' + description: 'Base image (e.g. vllm/vllm-openai:v0.16.0)' default: 'vllm/vllm-openai:v0.16.0' required: true type: string vllm_repo: - description: 'vLLM repo URL, official: https://github.com/vllm-project/vllm.git, if empty, will not refresh engine code and use the one in base image' + description: 'vLLM repo URL (empty = use engine from base image)' required: false type: string vllm_commit: - description: 'vLLM commit SHA or ref (use "latest" for HEAD)' + description: 'vLLM commit/ref ("latest" for HEAD)' required: false default: 'latest' type: string @@ -32,112 +30,24 @@ on: default: 'https://github.com/lightseekorg/smg' type: string smg_commit: - description: 'SMG commit SHA or ref (use "latest" for HEAD)' + description: 'SMG commit/ref ("latest" for HEAD)' required: false default: 'v1.1.0' type: string tag: - description: 'Override image tag (e.g. v1.1.0-vllm-v0.16.0). If empty, tag is auto-generated.' + description: 'Override image tag (e.g. v1.1.0-vllm-v0.16.0)' required: false type: string jobs: build: - runs-on: ["8-gpu-h200"] - steps: - - name: Print inputs to summary - run: | - echo "## Inputs" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "| Parameter | Value |" >> $GITHUB_STEP_SUMMARY - echo "|-----------|-------|" >> $GITHUB_STEP_SUMMARY - echo "| base_image_ref | \`${{ inputs.base_image_ref }}\` |" >> $GITHUB_STEP_SUMMARY - echo "| vllm_repo | \`${{ inputs.vllm_repo }}\` |" >> $GITHUB_STEP_SUMMARY - echo "| vllm_commit | \`${{ inputs.vllm_commit }}\` |" >> $GITHUB_STEP_SUMMARY - echo "| smg_repo | \`${{ inputs.smg_repo }}\` |" >> $GITHUB_STEP_SUMMARY - echo "| smg_commit | \`${{ inputs.smg_commit }}\` |" >> $GITHUB_STEP_SUMMARY - echo "| tag | \`${{ inputs.tag }}\` |" >> $GITHUB_STEP_SUMMARY - - - name: Checkout code - uses: actions/checkout@v4 - - - name: Resolve image tag - id: resolve-tag - env: - BASE_IMAGE_REF: ${{ inputs.base_image_ref }} - VLLM_REPO: ${{ inputs.vllm_repo }} - VLLM_COMMIT: ${{ inputs.vllm_commit }} - run: | - SMG_VERSION=$(grep -m1 '^version = ' bindings/python/pyproject.toml | sed 's/version = "\(.*\)"/\1/') - if [ -n "${{ inputs.tag }}" ]; then - IMAGE_TAG="${{ inputs.tag }}" - else - if [ -n "${VLLM_REPO}" ]; then - VLLM_TAG="${VLLM_COMMIT//\//-}" - VLLM_TAG="${VLLM_TAG// /}" - else - # No engine repo: use the base image tag as the engine version identifier - VLLM_TAG="${BASE_IMAGE_REF##*:}" - VLLM_TAG="${VLLM_TAG//\//-}" - fi - IMAGE_TAG="${SMG_VERSION}-vllm-${VLLM_TAG}" - fi - echo "image_tag=${IMAGE_TAG}" >> "$GITHUB_OUTPUT" - echo "**Image tag:** \`${IMAGE_TAG}\`" >> $GITHUB_STEP_SUMMARY - - - name: Login to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Build image - uses: docker/build-push-action@v6 - with: - context: . - file: docker/Dockerfile.engine - target: custom-vllm - push: false - load: true - build-args: | - BASE_IMAGE_REF=${{ inputs.base_image_ref }} - ENGINE_REPO=${{ inputs.vllm_repo }} - ENGINE_COMMIT=${{ inputs.vllm_commit }} - SMG_REPO=${{ inputs.smg_repo }} - SMG_COMMIT=${{ inputs.smg_commit }} - tags: smg-build:${{ steps.resolve-tag.outputs.image_tag }} - cache-from: type=gha - cache-to: type=gha,mode=max - - - name: Push image to GHCR - id: push-ghcr - env: - IMAGE_TAG: ${{ steps.resolve-tag.outputs.image_tag }} - BASE_IMAGE_TAG: smg-build:${{ steps.resolve-tag.outputs.image_tag }} - run: | - echo "**Build image:** done" >> $GITHUB_STEP_SUMMARY - TARGET_IMAGE="ghcr.io/${{ github.repository_owner }}/smg:${IMAGE_TAG}" - echo "image_name=${TARGET_IMAGE}" >> "$GITHUB_OUTPUT" - docker tag "$BASE_IMAGE_TAG" "$TARGET_IMAGE" - docker push "$TARGET_IMAGE" - echo "**Push image:** \`${TARGET_IMAGE}\`" >> $GITHUB_STEP_SUMMARY - - - name: Clean up local images - if: always() - env: - IMAGE_TAG: ${{ steps.resolve-tag.outputs.image_tag }} - run: | - docker rmi "smg-build:${IMAGE_TAG}" || true - docker rmi "${{ steps.push-ghcr.outputs.image_name }}" || true - if [ -n "${{ inputs.base_image_ref }}" ]; then - docker rmi "${{ inputs.base_image_ref }}" || true - fi - - - name: Summary - if: always() - run: | - echo "## Image" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "**Image tag:** \`${{ steps.push-ghcr.outputs.image_tag }}\`" >> $GITHUB_STEP_SUMMARY - echo "**Image name:** \`${{ steps.push-ghcr.outputs.image_name }}\`" >> $GITHUB_STEP_SUMMARY + uses: ./.github/workflows/_build-engine-image.yml + with: + engine: vllm + base_image_ref: ${{ inputs.base_image_ref }} + engine_repo: ${{ inputs.vllm_repo }} + engine_commit: ${{ inputs.vllm_commit }} + smg_repo: ${{ inputs.smg_repo }} + smg_commit: ${{ inputs.smg_commit }} + tag: ${{ inputs.tag }} + secrets: inherit diff --git a/docker/Dockerfile.engine b/docker/Dockerfile.engine deleted file mode 100644 index 1436b114e6..0000000000 --- a/docker/Dockerfile.engine +++ /dev/null @@ -1,76 +0,0 @@ -# Multi-stage: each base has its own tag. Build with --target vllm|sglang|trtllm|tgl -# Build args: BASE_IMAGE_REF (full image:tag); ENGINE_REPO, ENGINE_COMMIT; SMG_REPO, SMG_COMMIT -ARG BASE_IMAGE_REF - -# Shared sources: clone engine and smg from GitHub, copy scripts -FROM alpine:3.19 AS sources -ARG ENGINE_REPO -ARG ENGINE_COMMIT -ARG SMG_REPO -ARG SMG_COMMIT -RUN apk add --no-cache git \ - && if [ -n "${ENGINE_REPO}" ] && [ -n "${ENGINE_COMMIT}" ]; then \ - if [ "${ENGINE_COMMIT}" = "latest" ]; then \ - git clone --depth 1 "${ENGINE_REPO}" /opt/engine-src; \ - else \ - git clone "${ENGINE_REPO}" /opt/engine-src \ - && ( cd /opt/engine-src && git checkout "${ENGINE_COMMIT}" ); \ - fi; \ - else mkdir -p /opt/engine-src; fi \ - && if [ "${SMG_COMMIT}" = "latest" ]; then \ - git clone --depth 1 "${SMG_REPO}" /tmp/smg-src; \ - else \ - git clone "${SMG_REPO}" /tmp/smg-src \ - && ( cd /tmp/smg-src && git checkout "${SMG_COMMIT}" ); \ - fi -COPY scripts/installation/install-vllm.sh /tmp/install-vllm.sh -COPY scripts/installation/install-sglang.sh /tmp/install-sglang.sh -COPY scripts/installation/install-tgl.sh /tmp/install-tgl.sh -COPY scripts/installation/install-trtllm.sh /tmp/install-trtllm.sh -COPY scripts/installation/install-smg.sh /tmp/install-smg.sh - - -# custom base (override with BASE_IMAGE_REF, e.g. vllm/vllm-openai:nightly) -FROM ${BASE_IMAGE_REF} AS custom-vllm - -ARG ENGINE_REPO -ENV SMG_DEFAULT_BACKEND=vllm -COPY --from=sources /opt/engine-src /opt/vllm-src -COPY --from=sources /tmp/smg-src /opt/smg-src -COPY --from=sources /tmp/install-vllm.sh /tmp/install-vllm.sh -COPY --from=sources /tmp/install-smg.sh /tmp/install-smg.sh -RUN bash /tmp/install-smg.sh /opt/smg-src -RUN if [ -n "${ENGINE_REPO}" ]; then bash /tmp/install-vllm.sh /opt/vllm-src; fi - -FROM ${BASE_IMAGE_REF} AS custom-sglang - -ARG ENGINE_REPO -ENV SMG_DEFAULT_BACKEND=sglang -COPY --from=sources /opt/engine-src /opt/sglang-src -COPY --from=sources /tmp/smg-src /opt/smg-src -COPY --from=sources /tmp/install-sglang.sh /tmp/install-sglang.sh -COPY --from=sources /tmp/install-smg.sh /tmp/install-smg.sh -RUN bash /tmp/install-smg.sh /opt/smg-src -RUN if [ -n "${ENGINE_REPO}" ]; then bash /tmp/install-sglang.sh /opt/sglang-src; fi - -FROM ${BASE_IMAGE_REF} AS custom-trtllm - -ARG ENGINE_REPO -ENV SMG_DEFAULT_BACKEND=trtllm -COPY --from=sources /opt/engine-src /opt/trtllm-src -COPY --from=sources /tmp/smg-src /opt/smg-src -COPY --from=sources /tmp/install-trtllm.sh /tmp/install-trtllm.sh -COPY --from=sources /tmp/install-smg.sh /tmp/install-smg.sh -RUN bash /tmp/install-smg.sh /opt/smg-src -RUN if [ -n "${ENGINE_REPO}" ]; then bash /tmp/install-trtllm.sh /opt/trtllm-src; fi - -FROM ${BASE_IMAGE_REF} AS custom-tgl - -ARG ENGINE_REPO -ENV SMG_DEFAULT_BACKEND=sglang -COPY --from=sources /opt/engine-src /opt/tgl-src -COPY --from=sources /tmp/smg-src /opt/smg-src -COPY --from=sources /tmp/install-tgl.sh /tmp/install-tgl.sh -COPY --from=sources /tmp/install-smg.sh /tmp/install-smg.sh -RUN bash /tmp/install-smg.sh /opt/smg-src -RUN if [ -n "${ENGINE_REPO}" ]; then bash /tmp/install-tgl.sh /opt/tgl-src; fi diff --git a/docker/engine.Dockerfile b/docker/engine.Dockerfile new file mode 100644 index 0000000000..c2addedbf1 --- /dev/null +++ b/docker/engine.Dockerfile @@ -0,0 +1,61 @@ +# Parameterized engine image builder. +# +# Build args: +# BASE_IMAGE_REF - full image:tag to start FROM +# ENGINE - engine name: vllm | sglang | trtllm | tgl +# BACKEND - SMG_DEFAULT_BACKEND value (defaults to ENGINE; tgl overrides to sglang) +# ENGINE_REPO - if set, engine source is cloned and install-.sh runs +# ENGINE_COMMIT - commit/ref for ENGINE_REPO ("latest" = HEAD) +# SMG_REPO - SMG source repo URL +# SMG_COMMIT - commit/ref for SMG_REPO ("latest" = HEAD) +# +# Usage: +# docker build --build-arg BASE_IMAGE_REF=lmsysorg/sglang:v0.5.9 \ +# --build-arg ENGINE=sglang \ +# --build-arg SMG_REPO=https://github.com/lightseekorg/smg \ +# --build-arg SMG_COMMIT=v1.1.0 \ +# -f docker/engine.Dockerfile . + +ARG BASE_IMAGE_REF + +# ── sources stage: clone repos, stage install scripts ──────────────────────── +FROM alpine:3.19 AS sources +ARG ENGINE_REPO +ARG ENGINE_COMMIT +ARG SMG_REPO +ARG SMG_COMMIT +RUN apk add --no-cache git \ + && if [ -n "${ENGINE_REPO}" ] && [ -n "${ENGINE_COMMIT}" ]; then \ + if [ "${ENGINE_COMMIT}" = "latest" ]; then \ + git clone --depth 1 "${ENGINE_REPO}" /opt/engine-src; \ + else \ + git clone "${ENGINE_REPO}" /opt/engine-src \ + && ( cd /opt/engine-src && git checkout "${ENGINE_COMMIT}" ); \ + fi; \ + else mkdir -p /opt/engine-src; fi \ + && if [ "${SMG_COMMIT}" = "latest" ]; then \ + git clone --depth 1 "${SMG_REPO}" /tmp/smg-src; \ + else \ + git clone "${SMG_REPO}" /tmp/smg-src \ + && ( cd /tmp/smg-src && git checkout "${SMG_COMMIT}" ); \ + fi +COPY scripts/installation/ /tmp/scripts/ + +# ── final stage: install SMG + conditionally install engine ────────────────── +FROM ${BASE_IMAGE_REF} + +ARG ENGINE=sglang +ARG BACKEND +ARG ENGINE_REPO + +ENV SMG_DEFAULT_BACKEND=${BACKEND:-${ENGINE}} + +COPY --from=sources /opt/engine-src /opt/engine-src +COPY --from=sources /tmp/smg-src /opt/smg-src +COPY --from=sources /tmp/scripts/ /tmp/scripts/ + +RUN bash /tmp/scripts/install-smg.sh /opt/smg-src + +RUN if [ -n "${ENGINE_REPO}" ]; then \ + bash /tmp/scripts/install-${ENGINE}.sh /opt/engine-src; \ + fi From 5a8df73bc3cbbcae728698c203ceec66e89c3166 Mon Sep 17 00:00:00 2001 From: Simo Lin Date: Fri, 6 Mar 2026 10:59:41 -0800 Subject: [PATCH 2/4] fix(docker): address PR review comments for engine image build - Skip GHCR login when dry_run is true (no push needed) - Validate ENGINE arg against allowlist (vllm|sglang|trtllm|tgl) in both the reusable workflow and Dockerfile to prevent injection - Add SMG_REPO/SMG_COMMIT guards in Dockerfile sources stage - Remove pull_request trigger from sglang wrapper to avoid running untrusted fork code on self-hosted GPU runners - Fix SMG commit fallback from 'latest' to pinned 'v1.1.0' in sglang wrapper run-name and job inputs Signed-off-by: Wei Gong Signed-off-by: Simo Lin --- .github/workflows/_build-engine-image.yml | 16 +++++++++++++--- .github/workflows/release-sglang-docker.yml | 12 ++---------- docker/engine.Dockerfile | 12 +++++++++--- 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/.github/workflows/_build-engine-image.yml b/.github/workflows/_build-engine-image.yml index 96887b4e15..5f7bbfb306 100644 --- a/.github/workflows/_build-engine-image.yml +++ b/.github/workflows/_build-engine-image.yml @@ -89,10 +89,19 @@ jobs: uses: actions/checkout@v4 - name: Validate inputs - if: inputs.engine == 'trtllm' && inputs.base_image_ref == '' && inputs.source_build_repo == '' + env: + ENGINE: ${{ inputs.engine }} + BASE_IMAGE_REF: ${{ inputs.base_image_ref }} + SOURCE_BUILD_REPO: ${{ inputs.source_build_repo }} run: | - echo "ERROR: For trtllm, either base_image_ref or source_build_repo must be set." >&2 - exit 1 + case "${ENGINE}" in + vllm|sglang|trtllm|tgl) ;; + *) echo "ERROR: Unknown engine '${ENGINE}'. Must be one of: vllm, sglang, trtllm, tgl." >&2; exit 1 ;; + esac + if [ "${ENGINE}" = "trtllm" ] && [ -z "${BASE_IMAGE_REF}" ] && [ -z "${SOURCE_BUILD_REPO}" ]; then + echo "ERROR: For trtllm, either base_image_ref or source_build_repo must be set." >&2 + exit 1 + fi # ── TRT-LLM source build (conditional) ────────────────────────────────── @@ -134,6 +143,7 @@ jobs: driver: docker - name: Login to GitHub Container Registry + if: ${{ !inputs.dry_run }} uses: docker/login-action@v3 with: registry: ghcr.io diff --git a/.github/workflows/release-sglang-docker.yml b/.github/workflows/release-sglang-docker.yml index 1c372589e5..e0d43c40de 100644 --- a/.github/workflows/release-sglang-docker.yml +++ b/.github/workflows/release-sglang-docker.yml @@ -4,17 +4,10 @@ run-name: >- SMG+SGLang | base=${{ inputs.base_image_ref || 'lmsysorg/sglang:v0.5.9' }} | engine=${{ inputs.sglang_commit || 'latest' }} | - smg=${{ inputs.smg_commit || 'latest' }} | + smg=${{ inputs.smg_commit || 'v1.1.0' }} | by @${{ github.actor }} on: - pull_request: - branches: [main] - paths: - - 'docker/engine.Dockerfile' - - '.github/workflows/_build-engine-image.yml' - - '.github/workflows/release-*-docker.yml' - - 'scripts/installation/**' workflow_dispatch: inputs: base_image_ref: @@ -55,7 +48,6 @@ jobs: engine_repo: ${{ inputs.sglang_repo }} engine_commit: ${{ inputs.sglang_commit || 'latest' }} smg_repo: ${{ inputs.smg_repo || 'https://github.com/lightseekorg/smg' }} - smg_commit: ${{ inputs.smg_commit || 'latest' }} + smg_commit: ${{ inputs.smg_commit || 'v1.1.0' }} tag: ${{ inputs.tag }} - dry_run: ${{ github.event_name == 'pull_request' }} secrets: inherit diff --git a/docker/engine.Dockerfile b/docker/engine.Dockerfile index c2addedbf1..8376f29ea2 100644 --- a/docker/engine.Dockerfile +++ b/docker/engine.Dockerfile @@ -25,6 +25,8 @@ ARG ENGINE_COMMIT ARG SMG_REPO ARG SMG_COMMIT RUN apk add --no-cache git \ + && if [ -z "${SMG_REPO}" ] || [ -z "${SMG_COMMIT}" ]; then \ + echo "ERROR: SMG_REPO and SMG_COMMIT must be set" >&2; exit 1; fi \ && if [ -n "${ENGINE_REPO}" ] && [ -n "${ENGINE_COMMIT}" ]; then \ if [ "${ENGINE_COMMIT}" = "latest" ]; then \ git clone --depth 1 "${ENGINE_REPO}" /opt/engine-src; \ @@ -56,6 +58,10 @@ COPY --from=sources /tmp/scripts/ /tmp/scripts/ RUN bash /tmp/scripts/install-smg.sh /opt/smg-src -RUN if [ -n "${ENGINE_REPO}" ]; then \ - bash /tmp/scripts/install-${ENGINE}.sh /opt/engine-src; \ - fi +RUN case "${ENGINE}" in \ + vllm|sglang|trtllm|tgl) ;; \ + *) echo "ERROR: Unknown ENGINE '${ENGINE}'" >&2; exit 1 ;; \ + esac \ + && if [ -n "${ENGINE_REPO}" ]; then \ + bash /tmp/scripts/install-${ENGINE}.sh /opt/engine-src; \ + fi From d49caed3ff778af3c84a4d2ef311ead8b04bd302 Mon Sep 17 00:00:00 2001 From: Simo Lin Date: Fri, 6 Mar 2026 12:20:51 -0800 Subject: [PATCH 3/4] fix(docker): sanitize repo URLs in summary, restrict source-build to trtllm - Sanitize repo URL inputs in step summary to strip credentials before logging to GITHUB_STEP_SUMMARY - Add explicit engine=trtllm guard to all source-build conditional steps (resolve ref, checkout, build) to prevent accidental source builds for other engines - Add validation that source_build_repo is only accepted for trtllm Signed-off-by: Wei Gong Signed-off-by: Simo Lin --- .github/workflows/_build-engine-image.yml | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/.github/workflows/_build-engine-image.yml b/.github/workflows/_build-engine-image.yml index 5f7bbfb306..d63323dd93 100644 --- a/.github/workflows/_build-engine-image.yml +++ b/.github/workflows/_build-engine-image.yml @@ -71,18 +71,20 @@ jobs: SOURCE_BUILD_REPO: ${{ inputs.source_build_repo }} SOURCE_BUILD_REF: ${{ inputs.source_build_ref }} run: | + # Strip credentials from repo URLs for safe summary output + sanitize() { local v="$1"; v="${v#https://}"; v="${v#http://}"; v="${v##*@}"; v="${v%.git}"; printf '%s' "$v"; } echo "## Inputs" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "| Parameter | Value |" >> $GITHUB_STEP_SUMMARY echo "|-----------|-------|" >> $GITHUB_STEP_SUMMARY echo "| engine | \`${ENGINE}\` |" >> $GITHUB_STEP_SUMMARY echo "| base_image_ref | \`${BASE_IMAGE_REF}\` |" >> $GITHUB_STEP_SUMMARY - echo "| engine_repo | \`${ENGINE_REPO}\` |" >> $GITHUB_STEP_SUMMARY + echo "| engine_repo | \`$(sanitize "${ENGINE_REPO}")\` |" >> $GITHUB_STEP_SUMMARY echo "| engine_commit | \`${ENGINE_COMMIT}\` |" >> $GITHUB_STEP_SUMMARY - echo "| smg_repo | \`${SMG_REPO}\` |" >> $GITHUB_STEP_SUMMARY + echo "| smg_repo | \`$(sanitize "${SMG_REPO}")\` |" >> $GITHUB_STEP_SUMMARY echo "| smg_commit | \`${SMG_COMMIT}\` |" >> $GITHUB_STEP_SUMMARY echo "| tag | \`${TAG}\` |" >> $GITHUB_STEP_SUMMARY - echo "| source_build_repo | \`${SOURCE_BUILD_REPO}\` |" >> $GITHUB_STEP_SUMMARY + echo "| source_build_repo | \`$(sanitize "${SOURCE_BUILD_REPO}")\` |" >> $GITHUB_STEP_SUMMARY echo "| source_build_ref | \`${SOURCE_BUILD_REF}\` |" >> $GITHUB_STEP_SUMMARY - name: Checkout SMG @@ -102,12 +104,16 @@ jobs: echo "ERROR: For trtllm, either base_image_ref or source_build_repo must be set." >&2 exit 1 fi + if [ "${ENGINE}" != "trtllm" ] && [ -n "${SOURCE_BUILD_REPO}" ]; then + echo "ERROR: source_build_repo is only supported for engine=trtllm." >&2 + exit 1 + fi # ── TRT-LLM source build (conditional) ────────────────────────────────── - name: Resolve source build checkout ref id: source-ref - if: inputs.base_image_ref == '' && inputs.source_build_repo != '' + if: inputs.engine == 'trtllm' && inputs.base_image_ref == '' && inputs.source_build_repo != '' env: SOURCE_BUILD_REF: ${{ inputs.source_build_ref }} SOURCE_BUILD_REPO: ${{ inputs.source_build_repo }} @@ -124,7 +130,7 @@ jobs: echo "repo=${repo}" >> "$GITHUB_OUTPUT" - name: Checkout source build repo - if: inputs.base_image_ref == '' && inputs.source_build_repo != '' + if: inputs.engine == 'trtllm' && inputs.base_image_ref == '' && inputs.source_build_repo != '' uses: actions/checkout@v4 with: repository: ${{ steps.source-ref.outputs.repo }} @@ -193,7 +199,7 @@ jobs: # ── TRT-LLM: build base image from source (conditional) ───────────────── - name: Build base image from source - if: inputs.base_image_ref == '' && inputs.source_build_repo != '' + if: inputs.engine == 'trtllm' && inputs.base_image_ref == '' && inputs.source_build_repo != '' env: IMAGE_TAG: ${{ steps.resolve.outputs.image_tag }} run: | From be0b83da14b6054c90effd8f355ff6ae906d7054 Mon Sep 17 00:00:00 2001 From: Simo Lin Date: Fri, 6 Mar 2026 12:24:21 -0800 Subject: [PATCH 4/4] fix(docker): restore pull_request trigger for sglang with dry_run Re-add pull_request trigger on docker/workflow/install-script paths so PRs get a build validation. dry_run=true skips GHCR login and push. Signed-off-by: Wei Gong Signed-off-by: Simo Lin --- .github/workflows/release-sglang-docker.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/release-sglang-docker.yml b/.github/workflows/release-sglang-docker.yml index e0d43c40de..b25a1c8c80 100644 --- a/.github/workflows/release-sglang-docker.yml +++ b/.github/workflows/release-sglang-docker.yml @@ -8,6 +8,13 @@ run-name: >- by @${{ github.actor }} on: + pull_request: + branches: [main] + paths: + - 'docker/engine.Dockerfile' + - '.github/workflows/_build-engine-image.yml' + - '.github/workflows/release-*-docker.yml' + - 'scripts/installation/**' workflow_dispatch: inputs: base_image_ref: @@ -50,4 +57,5 @@ jobs: smg_repo: ${{ inputs.smg_repo || 'https://github.com/lightseekorg/smg' }} smg_commit: ${{ inputs.smg_commit || 'v1.1.0' }} tag: ${{ inputs.tag }} + dry_run: ${{ github.event_name == 'pull_request' }} secrets: inherit