11#! /usr/bin/env bash
22# mirror_langsmith_images.sh
3- # Pull each image, retag it, and push to a destination registry.
4- #
5- # Ensure that you have logged into the destination registry if credentials are required.
6- #
7- # Default mode: retag as <REGISTRY>/<original-repo>:<tag>
8- # Marketplace mode (--dest-repo): retag as <REGISTRY>/<dest-repo>:<image-name>-<tag>
9- #
10- # Examples:
11- # ./mirror_langsmith_images.sh --registry myregistry --version 0.10.66 --platform linux/arm64
12- # ./mirror_langsmith_images.sh --registry myregistry --version 0.16.0 --include-sandboxes --platform linux/amd64
13- # ./mirror_langsmith_images.sh --registry 709825985650.dkr.ecr.us-east-1.amazonaws.com \
14- # --dest-repo langchain/langchain-repository --version 0.10.67 --platform linux/amd64 --dry-run
3+ # Mirror the images configured by the LangSmith chart to a destination registry.
154
165set -euo pipefail
176
18- # Default version
19- DEFAULT_VERSION=" 0.13.9"
20- DEFAULT_OPERATOR_VERSION=" 0.1.37"
7+ SCRIPT_DIR=" $( cd " $( dirname " ${BASH_SOURCE[0]} " ) " && pwd) "
8+ VALUES_FILE=" ${SCRIPT_DIR} /../values.yaml"
9+ CHART_FILE=" ${SCRIPT_DIR} /../Chart.yaml"
10+ CONSOLIDATED_VERSION=" 0.16.21"
2111
22- # ##############################################################################
23- # CLI parsing
24- # ##############################################################################
2512REGISTRY=" "
2613VERSION=" "
2714OPERATOR_VERSION=" "
@@ -32,15 +19,15 @@ INCLUDE_SANDBOXES=false
3219
3320usage () {
3421 cat << EOF
35- Usage: $0 --registry <registry-prefix> [--dest-repo <repo>] [--version <version>] [--platform linux/arm64] [--include-sandboxes] [--dry-run]
36-
37- --registry Mandatory. Destination registry (e.g. myregistry or 12345678.dkr.ecr.us-east-1.amazonaws.com)
38- --dest-repo Single destination repo (e.g. langchain/langchain_repository). Tags become <image-name>-<version>.
39- --version Version to use for LangSmith images (default: $DEFAULT_VERSION )
40- --operator-version Version for langgraph-operator (default: $DEFAULT_OPERATOR_VERSION )
41- --platform Architecture to pull (default: linux/amd64)
42- --include-sandboxes Also mirror sandbox runtime images. Requires --platform linux/amd64.
43- --dry-run Only print the docker commands
22+ Usage: $0 --registry <registry-prefix> [--dest-repo <repo>] [--version <version>] [--operator-version <version>] [-- platform linux/arm64] [--include-sandboxes] [--dry-run]
23+
24+ --registry Mandatory. Destination registry (e.g. myregistry or 12345678.dkr.ecr.us-east-1.amazonaws.com)
25+ --dest-repo Single destination repo (e.g. langchain/langchain_repository). Tags become <image-name>-<version>.
26+ --version Override the tag for LangSmith application images
27+ --operator-version Override the tag for langgraph-operator
28+ --platform Architecture to pull (default: linux/amd64)
29+ --include-sandboxes Also mirror sandbox runtime images. Requires --platform linux/amd64.
30+ --dry-run Only print the docker commands
4431EOF
4532 exit 1
4633}
@@ -59,51 +46,109 @@ while [[ $# -gt 0 ]]; do
5946done
6047
6148[[ -z $REGISTRY ]] && { echo " ERROR: --registry is required" ; usage; }
49+ [[ -f $VALUES_FILE ]] || { echo " ERROR: values.yaml not found at ${VALUES_FILE} " >&2 ; exit 1; }
50+ [[ -f $CHART_FILE ]] || { echo " ERROR: Chart.yaml not found at ${CHART_FILE} " >&2 ; exit 1; }
6251
63- # Use provided version or default
64- VERSION=" ${VERSION:- $DEFAULT_VERSION } "
65- OPERATOR_VERSION=" ${OPERATOR_VERSION:- $DEFAULT_OPERATOR_VERSION } "
66-
67- # Build images array with the specified version
68- IMAGES=(
69- " docker.io/langchain/langsmith-ace-backend:${VERSION} "
70- " docker.io/langchain/langsmith-backend:${VERSION} "
71- " docker.io/langchain/langsmith-insights-engine:${VERSION} "
72- " docker.io/langchain/langsmith-frontend:${VERSION} "
73- " docker.io/langchain/hosted-langserve-backend:${VERSION} "
74- " docker.io/langchain/langgraph-operator:${OPERATOR_VERSION} "
75- " docker.io/langchain/langsmith-go-backend:${VERSION} "
76- " docker.io/langchain/langsmith-playground:${VERSION} "
77- " docker.io/langchain/agent-builder-tool-server:${VERSION} "
78- " docker.io/langchain/agent-builder-trigger-server:${VERSION} "
79- " docker.io/langchain/agent-builder-deep-agent:${VERSION} "
80- " docker.io/postgres:15.15"
81- " docker.io/redis:8"
82- " docker.io/clickhouse/clickhouse-server:25.12"
83- )
84-
85- if $INCLUDE_SANDBOXES ; then
86- if [[ " $PLATFORM " != " linux/amd64" ]]; then
87- echo " ERROR: --include-sandboxes requires --platform linux/amd64 because sandbox runtime images are only published for amd64." >&2
88- exit 1
52+ if $INCLUDE_SANDBOXES && [[ $PLATFORM != " linux/amd64" ]]; then
53+ echo " ERROR: --include-sandboxes requires --platform linux/amd64 because sandbox runtime images are only published for amd64." >&2
54+ exit 1
55+ fi
56+
57+ CHART_APP_VERSION=" "
58+ while IFS= read -r line; do
59+ if [[ $line =~ ^appVersion:[[:space:]]* \" ? ([^\" [:space:]# ]+) ]]; then
60+ CHART_APP_VERSION= " ${BASH_REMATCH[1]} "
61+ break
8962 fi
63+ done < " $CHART_FILE "
64+
65+ [[ -n $CHART_APP_VERSION ]] || { echo " ERROR: appVersion not found in ${CHART_FILE} " >&2 ; exit 1; }
66+
67+ version_before () {
68+ local version=$1
69+ local minimum=$2
70+ local version_major version_minor version_patch
71+ local minimum_major minimum_minor minimum_patch
72+
73+ [[ $version =~ ^v? ([0-9]+)\. ([0-9]+)\. ([0-9]+) ]] || return 1
74+ version_major=${BASH_REMATCH[1]}
75+ version_minor=${BASH_REMATCH[2]}
76+ version_patch=${BASH_REMATCH[3]}
77+ [[ $minimum =~ ^v? ([0-9]+)\. ([0-9]+)\. ([0-9]+) ]]
78+ minimum_major=${BASH_REMATCH[1]}
79+ minimum_minor=${BASH_REMATCH[2]}
80+ minimum_patch=${BASH_REMATCH[3]}
81+
82+ (( 10 #$version_major < 10 #$minimum_major )) && return 0
83+ (( 10 #$version_major > 10 #$minimum_major )) && return 1
84+ (( 10 #$version_minor < 10 #$minimum_minor )) && return 0
85+ (( 10 #$version_minor > 10 #$minimum_minor )) && return 1
86+ (( 10 #$version_patch < 10 #$minimum_patch ))
87+ }
88+
89+ IMAGES= ()
9090
91- IMAGES+=(
92- " docker.io/langchain/sandbox-host:${VERSION} "
93- )
91+ add_image () {
92+ IMAGES+=(" ${1} :${2} " )
93+ }
94+
95+ in_images= false
96+ image_key= " "
97+ current_repo= " "
98+ while IFS= read -r line; do
99+ if [[ $line == " images:" ]]; then
100+ in_images=true
101+ continue
102+ fi
103+ $in_images || continue
104+ [[ $line =~ ^[^[:space:]# ] ]] && break
105+
106+ if [[ $line =~ ^[[:space:]]{2}([[:alnum:]_]+Image):[[:space:]]* $ ]]; then
107+ image_key=" ${BASH_REMATCH[1]} "
108+ current_repo=" "
109+ elif [[ -n $image_key && $line =~ ^[[:space:]]{4}repository:[[:space:]]* \" ([^\" ]+)\" ]]; then
110+ current_repo=" ${BASH_REMATCH[1]} "
111+ elif [[ -n $current_repo && $line =~ ^[[:space:]]{4}tag:[[:space:]]* \" ([^\" ]* )\" ]]; then
112+ tag=" ${BASH_REMATCH[1]} "
113+ if [[ $current_repo == " docker.io/langchain/langgraph-operator" ]]; then
114+ tag=" ${OPERATOR_VERSION:- $tag } "
115+ elif [[ $current_repo == docker.io/langchain/* ]]; then
116+ tag=" ${VERSION:- ${tag:- $CHART_APP_VERSION } } "
117+ fi
118+
119+ if [[ $image_key != " sandboxHostImage" ]] || $INCLUDE_SANDBOXES ; then
120+ [[ -n $tag ]] || { echo " ERROR: no tag configured for ${image_key} " >&2 ; exit 1; }
121+ add_image " $current_repo " " $tag "
122+ fi
123+ image_key=" "
124+ current_repo=" "
125+ fi
126+ done < " $VALUES_FILE "
127+
128+ APP_VERSION= " ${VERSION:- $CHART_APP_VERSION } "
129+ if version_before " $APP_VERSION " " $CONSOLIDATED_VERSION " ; then
130+ for legacy_repository in \
131+ docker.io/langchain/langsmith-go-backend \
132+ docker.io/langchain/langsmith-playground \
133+ docker.io/langchain/hosted-langserve-backend \
134+ docker.io/langchain/agent-builder-tool-server \
135+ docker.io/langchain/agent-builder-trigger-server; do
136+ add_image " $legacy_repository " " $APP_VERSION "
137+ done
94138fi
95139
96- echo " Using version: ${VERSION} "
140+ [[ ${# IMAGES[@]} -gt 0 ]] || { echo " ERROR: no images found in ${VALUES_FILE} " >&2 ; exit 1; }
141+
142+ echo " App version: ${APP_VERSION} "
97143echo " Registry: ${REGISTRY} "
98144[[ -n $DEST_REPO ]] && echo " Dest repo: ${DEST_REPO} "
99145echo " Platform: ${PLATFORM} "
100146echo " Include sandboxes: ${INCLUDE_SANDBOXES} "
101147echo " Dry-run: ${DRY_RUN} "
148+ echo " Images (${# IMAGES[@]} ):"
149+ printf ' %s\n' " ${IMAGES[@]} "
102150echo
103151
104- # ##############################################################################
105- # Helper to echo or execute a docker command
106- # ##############################################################################
107152run_cmd () {
108153 if $DRY_RUN ; then
109154 printf ' [DRY-RUN] %q' " $1 "
@@ -115,20 +160,15 @@ run_cmd() {
115160 fi
116161}
117162
118- # ##############################################################################
119- # Main loop
120- # ##############################################################################
121163for SRC in " ${IMAGES[@]} " ; do
122- repo_tag=${SRC#*/ } # strip first path element (docker.io/…)
123- tag=${repo_tag##*: } # version tag
164+ repo_tag=${SRC#*/ }
165+ tag=${repo_tag##*: }
124166
125167 if [[ -n $DEST_REPO ]]; then
126- # Marketplace mode: all images → single repo, tag = <image-name>-<version>
127- image_name=${repo_tag%%:* } # e.g. langchain/langsmith-backend
128- image_name=${image_name##*/ } # e.g. langsmith-backend
168+ image_name=${repo_tag%%:* }
169+ image_name=${image_name##*/ }
129170 DEST=" ${REGISTRY} /${DEST_REPO} :${image_name} -${tag} "
130171 else
131- # Default mode: preserve original repo structure
132172 repo=${repo_tag%%:* }
133173 DEST=" ${REGISTRY} /${repo} :${tag} "
134174 fi
@@ -140,4 +180,4 @@ for SRC in "${IMAGES[@]}"; do
140180 echo
141181done
142182
143- echo " ✓ All images processed."
183+ echo " All images processed."
0 commit comments