diff --git a/docs/tools.md b/docs/tools.md index 362c6c25..18988946 100644 --- a/docs/tools.md +++ b/docs/tools.md @@ -163,23 +163,59 @@ For details on the Doxygen configuration and CI pipeline, see the ### Bump the pinned Rust submodule +Update to the commit from the Rust SDK release with the newest publication +time (including prereleases, excluding drafts): + +```bash +./scripts/update-rust-sdk.sh +``` + +The script requires an authenticated GitHub CLI (`gh auth login`, or `GH_TOKEN`) +when `--hash` is omitted. To select a specific commit without `gh`, pass its full +or abbreviated hash: + ```bash -cd client-sdk-cpp -git fetch origin -git switch -c try-rust-main origin/main +./scripts/update-rust-sdk.sh --hash 1a477bc +``` -# Sync submodule URLs and check out what origin/main pins (recursively): -git submodule sync --recursive -git submodule update --init --recursive --checkout +The script checks out the selected Rust commit and synchronizes its nested +submodules. It leaves the parent index untouched; review and stage the gitlink +when ready. `--hash` also permits unreleased commits for local development; CI +requires a published release for the pinned Rust commit. -# If the nested submodule under yuv-sys didn't materialize, force it: -git -C client-sdk-rust/yuv-sys submodule sync --recursive -git -C client-sdk-rust/yuv-sys submodule update --init --recursive --checkout +Review the selected commits before committing the gitlink update: -# Sanity check: -git submodule status --recursive +```bash +git diff --submodule=log -- client-sdk-rust +git add -- client-sdk-rust ``` +### Bump the pinned C++ example collection + +Update to the most recent commit on the `main` branch of the examples +repository: + +```bash +./scripts/update-cpp-example-collection.sh +``` + +To select a specific commit, pass its full or abbreviated hash: + +```bash +./scripts/update-cpp-example-collection.sh --hash 7e76bea +``` + +Review and stage the examples gitlink when ready: + +```bash +git diff --submodule=log -- cpp-example-collection +git add -- cpp-example-collection +``` + +Both updaters run from any working directory, initialize the selected submodule +if necessary, detach its checkout at the selected commit, and synchronize nested +submodules using checkout mode. + ### If `yuv-sys` fails to build ```bash diff --git a/scripts/update-cpp-example-collection.sh b/scripts/update-cpp-example-collection.sh new file mode 100755 index 00000000..57d502f5 --- /dev/null +++ b/scripts/update-cpp-example-collection.sh @@ -0,0 +1,88 @@ +#!/usr/bin/env bash +# +# Copyright 2026 LiveKit +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -euo pipefail + +script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +repo_root="$(cd "${script_dir}/.." && pwd -P)" +examples_repo="livekit-examples/cpp-example-collection" +examples_remote="https://github.com/${examples_repo}.git" +default_branch="main" + +usage() { + cat <<'EOF' +Usage: ./scripts/update-cpp-example-collection.sh [--hash HASH] + +Update the cpp-example-collection submodule used by the C++ SDK. + +Options: + --hash HASH + Use the specified examples commit. You can use a short or full SHA-1 + hash. + -h, --help + Show this help and exit. + +When --hash is omitted, the script uses the most recent commit on the main +branch of livekit-examples/cpp-example-collection. +EOF +} + +requested_hash="" +hash_provided=false + +while (($#)); do + case "$1" in + --hash) + shift + if (($# == 0)); then + echo "ERROR: --hash requires a value" >&2 + exit 2 + fi + requested_hash="$1" + hash_provided=true + shift + ;; + --hash=*) + requested_hash="${1#--hash=}" + hash_provided=true + shift + ;; + -h|--help) + usage + exit 0 + ;; + *) + echo "ERROR: unknown argument: $1" >&2 + usage >&2 + exit 2 + ;; + esac +done + +if [[ "$hash_provided" == true && ! "$requested_hash" =~ ^[0-9a-fA-F]{7,40}$ ]]; then + echo "ERROR: --hash must be a 7- to 40-character hexadecimal Git SHA-1 hash" >&2 + exit 2 +fi + +# Shared checkout logic keeps the two dependency updaters consistent. +source "${script_dir}/update-submodule-common.sh" +check_update_prerequisites + +fetch_ref="" +if [[ "$hash_provided" == false ]]; then + fetch_ref="refs/heads/${default_branch}" +fi +update_submodule cpp-example-collection "$examples_remote" "$fetch_ref" diff --git a/scripts/update-rust-sdk.sh b/scripts/update-rust-sdk.sh new file mode 100755 index 00000000..2acac10f --- /dev/null +++ b/scripts/update-rust-sdk.sh @@ -0,0 +1,107 @@ +#!/usr/bin/env bash +# +# Copyright 2026 LiveKit +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -euo pipefail + +script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +repo_root="$(cd "${script_dir}/.." && pwd -P)" +rust_repo="livekit/client-sdk-rust" +rust_remote="https://github.com/${rust_repo}.git" + +usage() { + cat <<'EOF' +Usage: ./scripts/update-rust-sdk.sh [--hash HASH] + +Update the client-sdk-rust submodule used by the C++ SDK. + +Options: + --hash HASH + Use the specified Rust SDK commit. You can use a short or full SHA-1 + hash. + -h, --help + Show this help and exit. + +When --hash is omitted, the script uses the commit tagged by the most recently +published, non-draft GitHub release (including prereleases) in +livekit/client-sdk-rust (requires gh). +EOF +} + +requested_hash="" +hash_provided=false + +while (($#)); do + case "$1" in + --hash) + shift + if (($# == 0)); then + echo "ERROR: --hash requires a value" >&2 + exit 2 + fi + requested_hash="$1" + hash_provided=true + shift + ;; + --hash=*) + requested_hash="${1#--hash=}" + hash_provided=true + shift + ;; + -h|--help) + usage + exit 0 + ;; + *) + echo "ERROR: unknown argument: $1" >&2 + usage >&2 + exit 2 + ;; + esac +done + +if [[ "$hash_provided" == true && ! "$requested_hash" =~ ^[0-9a-fA-F]{7,40}$ ]]; then + echo "ERROR: --hash must be a 7- to 40-character hexadecimal Git SHA-1 hash" >&2 + exit 2 +fi + +# Shared checkout logic keeps the two dependency updaters consistent. +source "${script_dir}/update-submodule-common.sh" +check_update_prerequisites + +if [[ "$hash_provided" == false ]]; then + if ! command -v gh >/dev/null 2>&1; then + echo "ERROR: gh not found in PATH; install GitHub CLI or provide --hash" >&2 + exit 1 + fi + echo "==> Finding the most recently published Rust SDK release" + # Sort across every page by publication time, including prereleases. API + # list order and GitHub's designated "latest" release are different concepts. + release_tag="$(gh api --hostname github.com --paginate --slurp \ + "repos/${rust_repo}/releases?per_page=100" \ + --jq '[.[][] | select(.draft == false and .published_at != null)] | + sort_by(.published_at, .id) | last | .tag_name // empty')" + if [[ -z "$release_tag" ]]; then + echo "ERROR: no published release found for ${rust_repo}" >&2 + exit 1 + fi + git check-ref-format "refs/tags/${release_tag}" >/dev/null + fetch_ref="refs/tags/${release_tag}" + echo "==> Selected release: ${release_tag}" +else + fetch_ref="" +fi + +update_submodule client-sdk-rust "$rust_remote" "$fetch_ref" diff --git a/scripts/update-submodule-common.sh b/scripts/update-submodule-common.sh new file mode 100644 index 00000000..bb672524 --- /dev/null +++ b/scripts/update-submodule-common.sh @@ -0,0 +1,99 @@ +#!/usr/bin/env bash +# +# Copyright 2026 LiveKit +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Internal helpers sourced by update-rust-sdk.sh and +# update-cpp-example-collection.sh. Requires Bash 3.2 or newer. + +check_update_prerequisites() { + if ! command -v git >/dev/null 2>&1; then + echo "ERROR: git not found in PATH" >&2 + exit 1 + fi + local top_level + top_level="$(git -C "$repo_root" rev-parse --show-toplevel)" + if [[ "$(cd "$top_level" && pwd -P)" != "$repo_root" ]]; then + echo "ERROR: ${repo_root} is not the SDK Git worktree root" >&2 + exit 1 + fi +} + +require_clean_submodule() { + local directory="$1" + local changes + # Explicitly override submodule.*.ignore and diff.ignoreSubmodules. This + # includes nested HEAD changes, staged edits, and untracked files. + changes="$(git -C "$directory" status --porcelain --untracked-files=all \ + --ignore-submodules=none)" + if [[ -n "$changes" ]]; then + echo "ERROR: ${directory} has local changes (possibly in nested submodules)." >&2 + echo "Commit, stash, or move them before updating:" >&2 + printf '%s\n' "$changes" >&2 + exit 1 + fi +} + +update_submodule() { + local name="$1" remote="$2" fetch_ref="$3" + local directory="${repo_root}/${name}" + local resolved_hash current_hash + local fetch_options=(--no-tags) + + if [[ -e "${directory}/.git" ]]; then + require_clean_submodule "$directory" + else + echo "==> Initializing ${name}" + git -C "$repo_root" submodule update --init --checkout -- "$name" + require_clean_submodule "$directory" + fi + + echo "==> Fetching ${name} from ${remote}" + # Use the same authoritative repository for resolution and fetching, even + # when a developer's origin points to a fork. Leave their remotes unchanged. + if [[ "$hash_provided" == false ]]; then + git -C "$directory" fetch --quiet --no-tags "$remote" "$fetch_ref" + resolved_hash="$(git -C "$directory" rev-parse --verify 'FETCH_HEAD^{commit}')" + else + requested_hash="$(printf '%s' "$requested_hash" | tr '[:upper:]' '[:lower:]')" + if [[ ${#requested_hash} -lt 40 ]] || + ! git -C "$directory" fetch --quiet --no-tags "$remote" "$requested_hash"; then + # Abbreviated hashes cannot be fetched directly. Fetch all branch/tag + # objects without updating local branches or overwriting local tags. + if [[ "$(git -C "$directory" rev-parse --is-shallow-repository)" == true ]]; then + fetch_options+=(--unshallow) + fi + git -C "$directory" fetch --quiet "${fetch_options[@]}" "$remote" \ + '+refs/heads/*:refs/sdk-update/heads/*' '+refs/tags/*:refs/sdk-update/tags/*' + fi + if ! resolved_hash="$(git -C "$directory" rev-parse --verify "${requested_hash}^{commit}" 2>/dev/null)" || + [[ "$resolved_hash" != "$requested_hash"* ]]; then + echo "ERROR: '${requested_hash}' does not unambiguously identify a ${name} commit" >&2 + exit 1 + fi + fi + + current_hash="$(git -C "$directory" rev-parse HEAD)" + echo "==> Selected ${name}: ${current_hash} -> ${resolved_hash}" + # Failures here can leave a partial update. Do not attempt a destructive + # rollback; show the developer exactly what needs inspection and retrying. + trap 'echo "ERROR: Update incomplete. Inspect git status and git submodule status --recursive; fix the error and rerun the updater. No changes were staged or committed." >&2' ERR + git -C "$directory" checkout --quiet --no-overwrite-ignore --detach "$resolved_hash" + git -C "$directory" submodule sync --recursive + git -C "$directory" submodule update --init --recursive --checkout + trap - ERR + + echo "==> Done. Review with: git diff --submodule=log -- ${name}" + echo "==> Stage when ready: git add -- ${name}" +}