Compare commits

...
4 Commits
Author SHA1 Message Date
GuofangandClaude Sonnet 5.5 1307211bc6 Install ws2tcp-local, add entrypoint script and Claude installation handling
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-05 09:22:44 +08:00
Guofang 606029b332 Add gcc-toolset-15 and libasan-devel to Dockerfile dependencies 2026-11-03 16:57:30 +08:00
GuofangandClaude Sonnet 5.5 7f5012dd94 Build GMP as a fat library to support older CPUs
--enable-fat selects the optimized code at run time, so the static
library also works on x86_64 CPUs without AVX, BMI2 or ADX. It is
ignored on architectures that do not support it, such as aarch64.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-28 12:45:17 +08:00
GuofangandClaude Sonnet 5.5 729ada5769 Generate a CMake file that locates the debian13 dependencies
Install ${PREFIX}/share/source-stack/source-stack.cmake as the last step. It
mirrors the image's ENV block (prefix, pkg-config and library paths) for
CMake and can restrict lookups to the prefix with GW_SOURCE_STACK_ONLY.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-28 09:56:57 +08:00
6 changed files with 167 additions and 6 deletions

No files matched your search

+1 -1
View File
@@ -461,7 +461,7 @@ RUN --mount=type=bind,from=sources,source=gmp-6.3.0.tar.xz,target=/tmp/gmp.tar.x
mkdir src \ mkdir src \
&& tar --extract --xz --file=/tmp/gmp.tar.xz --directory=src --strip-components=1 \ && tar --extract --xz --file=/tmp/gmp.tar.xz --directory=src --strip-components=1 \
&& cd src \ && cd src \
&& CFLAGS="-fPIC -std=gnu17" ./configure --prefix=/usr/local --disable-shared --enable-static \ && CFLAGS="-fPIC -std=gnu17" ./configure --prefix=/usr/local --disable-shared --enable-static --enable-fat \
&& make --jobs="$(nproc)" \ && make --jobs="$(nproc)" \
&& make install \ && make install \
&& cd .. \ && cd .. \
+23 -2
View File
@@ -567,7 +567,7 @@ RUN --mount=type=bind,from=sources,source=gmp-6.3.0.tar.xz,target=/tmp/gmp.tar.x
mkdir src \ mkdir src \
&& tar --extract --xz --file=/tmp/gmp.tar.xz --directory=src --strip-components=1 \ && tar --extract --xz --file=/tmp/gmp.tar.xz --directory=src --strip-components=1 \
&& cd src \ && cd src \
&& CFLAGS=-fPIC ./configure --prefix=${PREFIX} --libdir=${PREFIX}/lib/$(gcc -dumpmachine) --disable-shared --enable-static \ && CFLAGS=-fPIC ./configure --prefix=${PREFIX} --libdir=${PREFIX}/lib/$(gcc -dumpmachine) --disable-shared --enable-static --enable-fat \
&& make --jobs="$(nproc)" \ && make --jobs="$(nproc)" \
&& make install \ && make install \
&& cd .. \ && cd .. \
@@ -820,4 +820,25 @@ RUN --mount=type=bind,from=sources,source=duckdb-v1.5.6.tar.gz,target=/tmp/duckd
&& cmake --build build --parallel "$(nproc)" --target install \ && cmake --build build --parallel "$(nproc)" --target install \
&& rm -rf build src ui-src && rm -rf build src ui-src
CMD ["/bin/bash"] COPY source-stack.cmake.in /tmp/
RUN mkdir -p ${PREFIX}/share/source-stack \
&& sed "s|@MULTIARCH@|$(gcc -dumpmachine)|" /tmp/source-stack.cmake.in \
> ${PREFIX}/share/source-stack/source-stack.cmake \
&& rm /tmp/source-stack.cmake.in
RUN cargo install --locked --version 0.11.0 --root /usr/local ws2tcp-local \
&& rm -rf /root/.cargo/registry /root/.cargo/git
RUN bash -c 'curl -fsSL https://claude.ai/install.sh | bash'
COPY --chmod=755 entrypoint.sh /usr/local/bin/
ENV PATH="/root/.local/bin:${PATH}"
ARG CLAUDE_CODE_OAUTH_TOKEN
ENV CLAUDE_CODE_OAUTH_TOKEN=${CLAUDE_CODE_OAUTH_TOKEN}
ARG WS2TCP_LOCAL_BASIC_AUTH
ENV WS2TCP_LOCAL_BASIC_AUTH=${WS2TCP_LOCAL_BASIC_AUTH}
ENTRYPOINT ["entrypoint.sh"]
+54 -1
View File
@@ -1,10 +1,38 @@
#!/bin/sh #!/bin/sh
# Usage:
# ./build.sh build the image, asking for the password that unlocks the token
# ./build.sh --encrypt encrypt a token and print the value to paste into ENCRYPTED_TOKEN
set -eu set -eu
IMAGE_NAME="${IMAGE_NAME:-debian13-source-stack}" IMAGE_NAME="${IMAGE_NAME:-debian13-source-stack}"
PREFIX="${PREFIX:-/usr/local}" PREFIX="${PREFIX:-/usr/local}"
SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
# CLAUDE_CODE_OAUTH_TOKEN encrypted with AES-256-CBC (PBKDF2, base64). Generate with --encrypt.
ENCRYPTED_TOKEN='U2FsdGVkX1/BCTASs0tR40hsT+sngp46zANCbGZnklLN1M0wSJs/DxbQ6JDg593G09A+cVtVius4hJHRzB6caOO2KwBV0/+i7yUjr/PfreZksrdkz1DnFc7oorxXfvB7azuNEkQfAE2fZg8cnYL5jy3/LKRdAm11SUm83fU8ke8='
OPENSSL_ARGS="enc -aes-256-cbc -pbkdf2 -iter 600000 -a -A"
if ! command -v openssl >/dev/null 2>&1; then
echo "Error: OpenSSL is required." >&2
exit 1
fi
# read_secret PROMPT VARNAME: read the input with echo on, so it stays visible while typing.
read_secret() {
printf '%s' "$1" >&2
IFS= read -r "$2"
}
if [ "${1:-}" = "--encrypt" ]; then
read_secret "OAuth token: " token
read_secret "Password: " BUILD_PASSWORD
export BUILD_PASSWORD
printf '%s' "$token" | openssl $OPENSSL_ARGS -pass env:BUILD_PASSWORD
echo
exit 0
fi
SOURCES_DIR="${SOURCES_DIR:-$SCRIPT_DIR/../sources}" SOURCES_DIR="${SOURCES_DIR:-$SCRIPT_DIR/../sources}"
FETCH_SCRIPT="$SCRIPT_DIR/../fetch-sources.sh" FETCH_SCRIPT="$SCRIPT_DIR/../fetch-sources.sh"
@@ -13,6 +41,28 @@ if ! command -v podman >/dev/null 2>&1; then
exit 1 exit 1
fi fi
if [ -z "$ENCRYPTED_TOKEN" ]; then
echo "Error: ENCRYPTED_TOKEN is empty; run './build.sh --encrypt' and paste the output into build.sh." >&2
exit 1
fi
read_secret "Password for CLAUDE_CODE_OAUTH_TOKEN: " BUILD_PASSWORD
export BUILD_PASSWORD
if ! CLAUDE_CODE_OAUTH_TOKEN=$(printf '%s' "$ENCRYPTED_TOKEN" | openssl $OPENSSL_ARGS -d -pass env:BUILD_PASSWORD 2>/dev/null); then
echo "Error: wrong password or corrupted token." >&2
exit 1
fi
unset BUILD_PASSWORD
printf 'ws2tcp-local username: ' >&2
IFS= read -r WS2TCP_USER
read_secret "ws2tcp-local password: " WS2TCP_PASS
WS2TCP_LOCAL_BASIC_AUTH="$WS2TCP_USER:$WS2TCP_PASS"
unset WS2TCP_USER WS2TCP_PASS
export WS2TCP_LOCAL_BASIC_AUTH
# Exported so podman reads the value from the environment and it never appears in the command line.
export CLAUDE_CODE_OAUTH_TOKEN
SOURCES_DIR="$SOURCES_DIR" "$FETCH_SCRIPT" SOURCES_DIR="$SOURCES_DIR" "$FETCH_SCRIPT"
# macOS reports arm64 where Linux (and Debian multiarch) call it aarch64. # macOS reports arm64 where Linux (and Debian multiarch) call it aarch64.
@@ -20,6 +70,9 @@ ARCH=$(uname -m)
[ "$ARCH" = arm64 ] && ARCH=aarch64 [ "$ARCH" = arm64 ] && ARCH=aarch64
echo "Building image $IMAGE_NAME with podman (prefix $PREFIX) ..." echo "Building image $IMAGE_NAME with podman (prefix $PREFIX) ..."
exec podman build --build-context sources="$SOURCES_DIR" \ exec podman build \
--build-arg CLAUDE_CODE_OAUTH_TOKEN \
--build-arg WS2TCP_LOCAL_BASIC_AUTH \
--build-context sources="$SOURCES_DIR" \
--build-arg PREFIX="$PREFIX" --build-arg MULTIARCH="$ARCH-linux-gnu" \ --build-arg PREFIX="$PREFIX" --build-arg MULTIARCH="$ARCH-linux-gnu" \
--tag "$IMAGE_NAME" "$SCRIPT_DIR" --tag "$IMAGE_NAME" "$SCRIPT_DIR"
+34
View File
@@ -0,0 +1,34 @@
#!/bin/bash
# Start ws2tcp-local (logging to a file so it does not garble the claude TUI), point the proxy variables at it, then run claude.
set -eu
GATEWAY="wss://wangguofang.net/tunnel"
LISTEN_HOST=127.0.0.1
LISTEN_PORT=3128
LOG_FILE="${WS2TCP_LOCAL_LOG:-/tmp/ws2tcp-local.log}"
# The credentials come from WS2TCP_LOCAL_BASIC_AUTH (user:pass), set at image build time.
ws2tcp-local --gateway "$GATEWAY" --listen "$LISTEN_HOST:$LISTEN_PORT" >"$LOG_FILE" 2>&1 &
proxy_pid=$!
for _ in $(seq 1 50); do
if (exec 3<>"/dev/tcp/$LISTEN_HOST/$LISTEN_PORT") 2>/dev/null; then
break
fi
if ! kill -0 "$proxy_pid" 2>/dev/null; then
echo "Error: ws2tcp-local exited during startup. Log ($LOG_FILE):" >&2
cat "$LOG_FILE" >&2
exit 1
fi
sleep 0.2
done
export http_proxy="http://$LISTEN_HOST:$LISTEN_PORT"
export https_proxy="$http_proxy"
export HTTP_PROXY="$http_proxy"
export HTTPS_PROXY="$http_proxy"
export no_proxy="localhost,127.0.0.1"
export NO_PROXY="$no_proxy"
trap 'kill "$proxy_pid" 2>/dev/null || true' EXIT
claude "$@"
+52
View File
@@ -0,0 +1,52 @@
# Generated into the image at ${PREFIX}/share/source-stack/source-stack.cmake.
#
# Points CMake at the dependencies installed by this image. It mirrors the
# Dockerfile's ENV block (PATH, PKG_CONFIG_PATH, CMAKE_PREFIX_PATH,
# C_INCLUDE_PATH/CPLUS_INCLUDE_PATH, LIBRARY_PATH) for CMake's own lookups.
# The prefix is derived from this file's location, so it also works when the
# prefix is moved. Include it after project(), before the first find_package().
#
# GW_SOURCE_STACK_ONLY resolve libraries and packages only below the prefix
option(GW_SOURCE_STACK_ONLY
"Resolve libraries and packages only below the source-stack prefix" OFF)
get_filename_component(_ss_p "${CMAKE_CURRENT_LIST_DIR}/../.." ABSOLUTE)
set(_ss_multiarch "@MULTIARCH@")
list(PREPEND CMAKE_PREFIX_PATH "${_ss_p}")
list(PREPEND CMAKE_PROGRAM_PATH "${_ss_p}/bin")
list(PREPEND CMAKE_INCLUDE_PATH "${_ss_p}/include")
list(PREPEND CMAKE_LIBRARY_PATH "${_ss_p}/lib/${_ss_multiarch}" "${_ss_p}/lib")
# pkg_check_modules() reads PKG_CONFIG_PATH at call time.
set(_ss_pc "${_ss_p}/lib/${_ss_multiarch}/pkgconfig:${_ss_p}/lib/pkgconfig:${_ss_p}/share/pkgconfig")
if(GW_SOURCE_STACK_ONLY)
# PKG_CONFIG_LIBDIR replaces the default search path, so system .pc files are ignored.
set(ENV{PKG_CONFIG_LIBDIR} "${_ss_pc}")
set(ENV{PKG_CONFIG_PATH} "")
# Build tools come from the system; look them up before system paths are switched off.
find_program(PKG_CONFIG_EXECUTABLE NAMES pkg-config pkgconf)
set(CMAKE_FIND_USE_CMAKE_SYSTEM_PATH OFF)
set(CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH OFF)
set(CMAKE_FIND_ROOT_PATH "${_ss_p}")
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
else()
if(NOT "$ENV{PKG_CONFIG_PATH}" STREQUAL "")
set(ENV{PKG_CONFIG_PATH} "${_ss_pc}:$ENV{PKG_CONFIG_PATH}")
else()
set(ENV{PKG_CONFIG_PATH} "${_ss_pc}")
endif()
endif()
# protoc installed next to the libraries, unless the caller chose one.
if(NOT Protobuf_PROTOC_EXECUTABLE AND EXISTS "${_ss_p}/bin/protoc")
set(Protobuf_PROTOC_EXECUTABLE "${_ss_p}/bin/protoc" CACHE FILEPATH "protoc")
endif()
message(STATUS "source-stack: ${_ss_p} (${_ss_multiarch})")
unset(_ss_p)
unset(_ss_multiarch)
unset(_ss_pc)
+3 -2
View File
@@ -9,8 +9,9 @@ RUN dnf install --assumeyes --enablerepo=crb \
curl-minimal \ curl-minimal \
gcc \ gcc \
gcc-c++ \ gcc-c++ \
gcc-toolset-15-gcc-c++ \
libasan \ libasan \
gcc-toolset-15-gcc-c++ \
gcc-toolset-15-libasan-devel \
git \ git \
libtool \ libtool \
make \ make \
@@ -547,7 +548,7 @@ RUN --mount=type=bind,from=sources,source=gmp-6.3.0.tar.xz,target=/tmp/gmp.tar.x
mkdir src \ mkdir src \
&& tar --extract --xz --file=/tmp/gmp.tar.xz --directory=src --strip-components=1 \ && tar --extract --xz --file=/tmp/gmp.tar.xz --directory=src --strip-components=1 \
&& cd src \ && cd src \
&& CFLAGS=-fPIC ./configure --prefix=/usr/local --libdir=/usr/local/lib64 --disable-shared --enable-static \ && CFLAGS=-fPIC ./configure --prefix=/usr/local --libdir=/usr/local/lib64 --disable-shared --enable-static --enable-fat \
&& make --jobs="$(nproc)" \ && make --jobs="$(nproc)" \
&& make install \ && make install \
&& cd .. \ && cd .. \