with one click
update-contrib
// Update a ClickHouse third-party library (contrib submodule) to a new version. Handles fork management, submodule pointer bumps, CMake adaptation, and source code fixes. Use when the user wants to bump a dependency.
// Update a ClickHouse third-party library (contrib submodule) to a new version. Handles fork management, submodule pointer bumps, CMake adaptation, and source code fixes. Use when the user wants to bump a dependency.
| name | update-contrib |
| description | Update a ClickHouse third-party library (contrib submodule) to a new version. Handles fork management, submodule pointer bumps, CMake adaptation, and source code fixes. Use when the user wants to bump a dependency. |
| argument-hint | <library-name> [target-version] |
| disable-model-invocation | false |
| allowed-tools | Agent, Task, Bash, Read, Write, Edit, Glob, Grep, WebFetch, AskUserQuestion |
Bump a git submodule under contrib/ to a new version, adapting CMake build files and ClickHouse source code as needed.
$0 (required): Library name as it appears under contrib/ (e.g., curl, openssl, arrow)$1 (optional): Target version — a git tag, branch, or commit SHA. If omitted, the latest upstream release tag is used.ClickHouse vendors all third-party libraries as git submodules under contrib/. Each library has:
contrib/<lib>/ — the submodule (upstream repo or a ClickHouse fork)contrib/<lib>-cmake/ — ClickHouse's own CMake build files (upstream CMake is deleted after checkout)Submodule URLs point to either upstream directly (unpatched) or a fork at github.com/ClickHouse/<lib> (when patches are needed). Forks use branches named ClickHouse/<version> — never ClickHouse/master or ClickHouse/main.
Some contrib libraries are built through Rust rather than through contrib/<lib>-cmake/. In that case, the important integration points are typically:
rust/workspace/<lib>/Cargo.tomlrust/workspace/<lib>/CMakeLists.txtrust/workspace/Cargo.lockcontrib/rust_vendor/For these libraries, updating the submodule often requires regenerating rust/workspace/Cargo.lock, re-vendoring crates with rust/vendor.sh, and verifying the Rust/C++ bridge build rather than editing contrib/<lib>-cmake/.
Throughout this skill, $LIB refers to the library name passed as $0 and $VERSION to the target version once resolved (step 2). Both must match [A-Za-z0-9._-]+ so they are safe to use unquoted in paths and branch names; reject anything else.
Extract the submodule URL, current commit, and whether it uses a fork:
# Submodule URL
git config --file .gitmodules --get "submodule.contrib/$LIB.url"
# Current pinned commit
git -C "contrib/$LIB" rev-parse HEAD
# Current version (nearest tag)
git -C "contrib/$LIB" describe --tags --abbrev=0 2>/dev/null || echo "no tags"
Determine whether the URL points to a ClickHouse fork (github.com/ClickHouse/...) or upstream directly.
Check if a contrib/<lib>-cmake/ directory exists:
ls contrib/${LIB}-cmake/CMakeLists.txt 2>/dev/null
Also check whether the library is integrated through the Rust workspace:
ls rust/workspace/${LIB}/Cargo.toml rust/workspace/${LIB}/CMakeLists.txt 2>/dev/null
grep -R "contrib/${LIB}\|${LIB}" rust/workspace/ --include='Cargo.toml' --include='CMakeLists.txt'
If it is a Rust-based contrib library, inspect:
rust/workspace/<lib>/Cargo.toml for dependency path, crate name, and enabled featuresrust/workspace/<lib>/CMakeLists.txt for generated headers, copied headers, and CMake target namesrust/workspace/Cargo.lock to understand what will need to be regeneratedIf $1 was provided, use it. Otherwise, find the latest release:
# For upstream repos
git -C "contrib/$LIB" fetch origin --tags
git -C "contrib/$LIB" tag -l --sort=-v:refname | head -20
# For ClickHouse forks, also check the upstream remote
If the library uses a ClickHouse fork, also check the upstream repo for the latest release. Use gh release list or git ls-remote --tags against the upstream URL if available.
Present the current and target versions to the user with AskUserQuestion for confirmation before proceeding. VERSION is whatever the user confirmed here — either $1 if it was passed in, or a tag picked from the fresh upstream tag list. Verify it matches the charset above before using it in branch names or commits.
If the submodule points to a ClickHouse fork, identify existing patches that need re-applying:
# Find the ClickHouse/ branch currently tracked
git -C "contrib/$LIB" branch -r | grep 'origin/ClickHouse/'
# List ClickHouse-specific commits (not in upstream)
UPSTREAM_TAG=$(git -C "contrib/$LIB" describe --tags --abbrev=0)
git -C "contrib/$LIB" log --oneline "$UPSTREAM_TAG"..HEAD
Report the patches found. These will need to be cherry-picked into the new ClickHouse/ branch after the fork is updated.
git checkout -b "bump-${LIB}-${VERSION}"
If the library uses a ClickHouse fork and has patches:
gh API), create a new branch ClickHouse/<new-version> from the upstream tagClickHouse/<old-version> branchClickHouse/<new-version> branch ready locally; do not push it yetBefore doing this, check previous ClickHouse PRs for the same library to understand the expected file set, commit style, and whether earlier updates touched lockfiles, vendored dependencies, or auxiliary integration code.
Do not push any branch yet. Pushes should happen only after:
If the library has no ClickHouse patches but uses a fork, consider switching to the upstream URL directly (update .gitmodules).
If the fork management requires manual work on GitHub (creating branches, cherry-picking in the fork), use AskUserQuestion to instruct the user on what to do, then wait for confirmation before proceeding.
cd "contrib/$LIB"
git fetch origin
git checkout <target-version-or-branch>
cd ../..
git add "contrib/$LIB"
If the submodule URL changed (fork to upstream, or new fork URL), also update .gitmodules:
git config --file .gitmodules "submodule.contrib/$LIB.url" "<new-url>"
git add .gitmodules
This is the critical step that determines what CMake and source changes are needed:
# List files added/removed between old and new version
OLD_COMMIT=$(git diff --cached --submodule=short "contrib/$LIB" | grep -oP '^\-Subproject commit \K\w+')
NEW_COMMIT=$(git diff --cached --submodule=short "contrib/$LIB" | grep -oP '^\+Subproject commit \K\w+')
# In the submodule, diff the file trees
cd "contrib/$LIB"
git diff --stat "$OLD_COMMIT..$NEW_COMMIT"
git diff --name-status "$OLD_COMMIT..$NEW_COMMIT" -- '*.c' '*.cpp' '*.cc' '*.h' '*.hpp' 'CMakeLists.txt'
cd ../..
Pay attention to:
src/For Rust-based contrib libraries, also diff the integration surface used by ClickHouse rather than only the whole upstream tree. In particular, inspect changes under paths such as:
crates/c-api/rust/workspace/<lib>/Cargo.tomlIf the public C/C++ API changes are additive only, the update may still be low-risk even if the full upstream diff is huge.
If contrib/<lib>-cmake/CMakeLists.txt exists, update it to reflect the new version:
find_package, check_c_compiler_flag, check_cxx_compiler_flag, check_c_source_compiles, check_include_file, check_symbol_exists, cmake_push_check_state, CMAKE_REQUIRED_FLAGS, or any Check* CMake modules — these are forbidden by CI style checks for hermetic, cross-compiled buildsIf contrib/<lib>-cmake/ has config headers (e.g., config.h.in, curl_config.h), check if they need updates for new version defines or feature flags.
If the library is integrated through Rust instead of contrib/<lib>-cmake/:
rust/workspace/<lib>/Cargo.toml still points to the correct path/package and whether the enabled features are still validrust/workspace/<lib>/CMakeLists.txt still matches the upstream public headers and feature flagsrust/workspace/Cargo.lock if dependency resolution changedrust/vendor.shImportant for Rust-based contrib updates:
rust/vendor.sh re-vendors dependencies for the whole Rust workspace, not just the target librarycontrib/rust_vendor/contrib/rust_vendor is itself a submodule pointing at github.com/ClickHouse/rust_vendor. Changes under it must be committed inside that submodule on a branch named bump-${LIB}-${VERSION}, then the submodule pointer in ClickHouse must be bumped to the new commit. Only push the rust_vendor branch after the user explicitly confirms.rust/workspace/Cargo.lock and the updated contrib/rust_vendor submodule pointer in the final commit in ClickHouseSearch for usages of the library in src/:
# Find includes of the library's headers
grep -r "#include.*<$LIB" src/ --include='*.h' --include='*.cpp' -l
grep -r "#include.*\"$LIB" src/ --include='*.h' --include='*.cpp' -l
# Check the library's CHANGELOG or migration guide for API changes
Common adaptation patterns:
Build the project to find compilation errors. Redirect ninja output to a log file in the build directory and use a subagent to analyze the results. If a build directory does not exist yet, configure one first (see docs/en/development/build.md — typically cmake -S . -B build, or use an existing build_* directory):
ninja -C build > build/build_bump_${LIB}.log 2>&1
Fix errors iteratively, committing each logical fix separately.
After all fixes, verify the build succeeds:
ninja -C build > build/build_bump_${LIB}.log 2>&1
Use a Task subagent to analyze the build log and return a concise summary.
For Rust-based contrib libraries, first discover the actual Rust/CMake target names before building them. Do not guess target names. Use:
ninja -C <build-dir> -t targets | grep -i "$LIB"
Examples:
_cargo-build__ch_rust_<lib> rather than _ch_rust_<lib>clickhouse, not clickhouse-serverIf a build failure mentions a copied upstream header, check whether:
If the build fails, fix the errors and repeat. Common issues:
Only after the build is validated successfully should you ask the user whether it is ok to push now. Until the user confirms, keep both the main repo branch and any fork branch local-only.
Identify and run tests related to the library. Redirect output to a log file:
# Functional tests that might exercise the library
grep -rl "$LIB" tests/queries/0_stateless/ --include='*.sql' --include='*.sh' -l | head -20
Create the commit. Use the title format Bump \` from <old_version> to <new_version>` for straightforward bumps, or a more descriptive title if the update has a specific motivation.
Before committing, make sure you stage all affected integration artifacts, not just the submodule pointer. In particular, for Rust-based contrib updates this usually means:
git add "contrib/$LIB" rust/workspace/Cargo.lock contrib/rust_vendor
Here contrib/rust_vendor stages the bumped submodule pointer after its own branch (bump-${LIB}-${VERSION} in the rust_vendor repo) has been committed — the individual vendored files are not tracked in the ClickHouse repo directly.
If there were build integration or source fixes, stage those too.
Commit message guidelines:
Use \` orBump `` to ` or a description of the motivationExamples of good commit messages from past PRs:
Use \simdjson` v4.2.4`Bump \curl` to 8.12.1`Update Boost from 1.83 to 1.90Fix CVE-2023-0286 / CVE-2023-5678Update librdkafka to fix lock-order-inversion in queue refcountBefore creating the PR, ensure any required branches have been pushed and that the user has already confirmed pushing is ok. This includes both the ClickHouse feature branch, any ClickHouse/<version> branch in a contrib fork, and any bump-${LIB}-${VERSION} branch in rust_vendor.
Use the PR template at .github/PULL_REQUEST_TEMPLATE.md — do not inline a custom structure. Fill in:
Not for changelog, Bug Fix (for CVE fixes), Build/Testing/Packaging Improvement, or ImprovementCreate the PR with:
gh pr create --draft --title "<title>" --body-file .github/PULL_REQUEST_TEMPLATE.md
Then edit the body to fill in the description, chosen changelog category, and changelog entry.
Some libraries require co-bumping dependencies. Check contrib/CMakeLists.txt for documented chains:
arrow requires: snappy, thrift, double-conversionavro requires: snappyamqpcpp requires: libuvcassandra requires: libuvazure requires: curlcyrus-sasl requires: krb5librdkafka requires: libgsaslrocksdb requires: jemalloc, snappy, zlib, lz4, zstd, liburinggoogle-cloud-cpp requires: grpc, protobuf, abseil-cpp, nlohmann-json, crc32cusearch requires: FP16, SimSIMDmongo-cxx-driver requires: mongo-c-driveraws, aws-c-auth, aws-c-cal, aws-c-common, aws-c-compression, aws-c-event-stream, aws-c-http, aws-c-io, aws-c-mqtt, aws-c-s3, aws-c-sdkutils, aws-checksums, aws-crt-cppIf the target library appears in a chain, check whether its dependencies also need updating.
After pushing, CI will automatically:
submodule changed labelcheck_submodules.sh which validates:
.gitmodules entries have corresponding directorieshttps://github.com/.gitmodules with [submodule entries inside submodules)check_cpp.sh which validates contrib/*-cmake/ directories don't use forbidden CMake patternscontrib/update-submodules.sh deletes upstream CMake files after checkout — ClickHouse relies exclusively on its own *-cmake/ files.gitmodules file must not use branch = ... tags