Skip to content

Refactor bootstrap into array-driven step functions#17

Merged
yorch merged 3 commits intomainfrom
claude/add-processing-progress-feedback-34Ai1
Feb 1, 2026
Merged

Refactor bootstrap into array-driven step functions#17
yorch merged 3 commits intomainfrom
claude/add-processing-progress-feedback-34Ai1

Conversation

@yorch
Copy link
Copy Markdown
Owner

@yorch yorch commented Feb 1, 2026

Summary

  • Extract each installation phase into a named step function (e.g. stepInstallDocker, stepInstallNeoVim)
  • Define a STEPS array listing all step functions; TOTAL_STEPS is derived from ${#STEPS[@]}
  • Replace inline script with a simple for STEP in "${STEPS[@]}" loop
  • Restores [N/TOTAL] progress format — the total is now always accurate since it is computed from the array length

Adding or removing a step now only requires defining the function and adding/removing one line in the STEPS array.

Test plan

  • Verify bash -n bootstrap.sh passes (syntax check)
  • Run bash run-tests.sh ubuntu:24.04 to confirm full bootstrap succeeds
  • Verify step counter output shows correct [N/13] format throughout
  • Verify spinner and elapsed time display during long operations

Summary by CodeRabbit

  • Refactor
    • Reworked the bootstrap/install flow into a modular, step-driven pipeline (replacing the previous monolithic sequence). Installation and cleanup actions run via a centralized steps sequence, improving clarity and error handling. Progress display now shows [Current/Total] step counters. Initial environment validation checks are preserved.

✏️ Tip: You can customize this high-level summary in your review settings.

Copilot AI review requested due to automatic review settings February 1, 2026 19:26
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Feb 1, 2026

Warning

Rate limit exceeded

@yorch has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 21 minutes and 15 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📝 Walkthrough

Walkthrough

Refactors bootstrap.sh from a linear inline setup into a modular, data-driven pipeline: introduces 13 step functions, a STEPS array with TOTAL_STEPS/CURRENT_STEP, and a loop-driven executor. Progress logging now shows [CURRENT/TOTAL]. Initial environment validation remains unchanged.

Changes

Cohort / File(s) Summary
Core script (steps)
bootstrap.sh
Replaced inline sequential setup with 13 discrete step functions (stepUpgradePackages, stepSetTimezone, stepSetLocales, stepInstallTools, stepInstallDocker, stepInstallDockerCompose, stepInstallDockerSwitch, stepInstallNeoVim, stepInstallSpeedTest, stepSetupPython, stepInstallZshPrezto, stepCleanupPackages, stepCleanupCaches). Each step encapsulates previous installation/cleanup logic.
Execution & logging
bootstrap.sh
Added STEPS array, TOTAL_STEPS, and CURRENT_STEP; introduced loop-based execution of steps and changed logStep() output to use [CURRENT/TOTAL] progress format. Initial validation (Ubuntu/version/arch) retained.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

Poem

🐰 I hopped through scripts at dawn,

rearranged each step till neat,
arrays and counters lead me on,
modular carrots, tidy treats,
bootstrap bounces on quick feet 🥕

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: refactoring the bootstrap script from inline sequential execution to a modular, array-driven approach with step functions.
Docstring Coverage ✅ Passed Docstring coverage is 93.33% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch claude/add-processing-progress-feedback-34Ai1

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refactors the bootstrap script to use an array-driven architecture for installation steps, improving maintainability and code organization. The refactoring extracts inline installation code into named step functions and uses a simple loop to execute them.

Changes:

  • Extracted 13 installation phases into individual step functions (e.g., stepInstallDocker, stepSetupPython)
  • Introduced a STEPS array containing all step function names, with TOTAL_STEPS dynamically calculated from array length
  • Replaced sequential inline installation code with a for-loop that iterates through the STEPS array
  • Updated step counter format from [Step N] to [N/TOTAL] for better progress visibility

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread bootstrap.sh Outdated
local SPEEDTEST_BIN="${USR_BIN_DIR}/speedtest-cli"
local SPEEDTEST_REPO="sivel/speedtest-cli"
local SPEEDTEST_ASSET="speedtest.py"
if ! [ -e ${SPEEDTEST_BIN} ]; then
Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variable ${SPEEDTEST_BIN} should be quoted in the test condition. Without quotes, the test may fail if the path contains spaces or special characters. This is inconsistent with line 319 where ${DOCKER_COMPOSE_BIN} is properly quoted as "${DOCKER_COMPOSE_BIN}". Consider changing to: if ! [ -e "${SPEEDTEST_BIN}" ]; then

Suggested change
if ! [ -e ${SPEEDTEST_BIN} ]; then
if ! [ -e "${SPEEDTEST_BIN}" ]; then

Copilot uses AI. Check for mistakes.
Comment thread bootstrap.sh Outdated
function stepSetupPython() {
logStep "Making sure python exists..."
local PYTHON_BIN=/usr/bin/python
if ! [ -x "$(command -v python)" ] || ! [ -e ${PYTHON_BIN} ]; then
Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variable ${PYTHON_BIN} should be quoted in the test condition. Without quotes, the test may fail if the path contains spaces or special characters. This is inconsistent with line 319 where ${DOCKER_COMPOSE_BIN} is properly quoted as "${DOCKER_COMPOSE_BIN}". Consider changing to: if ! [ -x "$(command -v python)" ] || ! [ -e "${PYTHON_BIN}" ]; then

Suggested change
if ! [ -x "$(command -v python)" ] || ! [ -e ${PYTHON_BIN} ]; then
if ! [ -x "$(command -v python)" ] || ! [ -e "${PYTHON_BIN}" ]; then

Copilot uses AI. Check for mistakes.
Comment thread bootstrap.sh Outdated
Comment on lines +354 to +355
runCmdAndLog update-alternatives --set vi $(which nvim)
runCmdAndLog update-alternatives --set vim $(which nvim)
Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The command substitution $(which nvim) should be quoted. Without quotes, the command may fail if the path contains spaces or special characters. Consider changing to: runCmdAndLog update-alternatives --set vi "$(which nvim)" and runCmdAndLog update-alternatives --set vim "$(which nvim)"

Suggested change
runCmdAndLog update-alternatives --set vi $(which nvim)
runCmdAndLog update-alternatives --set vim $(which nvim)
runCmdAndLog update-alternatives --set vi "$(which nvim)"
runCmdAndLog update-alternatives --set vim "$(which nvim)"

Copilot uses AI. Check for mistakes.
Comment thread bootstrap.sh Outdated
Comment on lines +332 to +337
if ! [ -e ${DOCKER_COMPOSE_SWITCH_BIN} ]; then
logStep "Installing Docker Switch..."
downloadBinaryLatestRelease "${DOCKER_COMPOSE_SWITCH_REPO}" "${DOCKER_COMPOSE_SWITCH_ASSET}" "${DOCKER_COMPOSE_SWITCH_BIN}"
# Set Docker Compose Switch to replace Docker Compose v1
runCmdAndLog update-alternatives \
--install ${USR_BIN_DIR}/docker-compose \
Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variable ${USR_BIN_DIR} should be quoted when used in arguments. Without quotes, the command may fail if the path contains spaces or special characters. Consider changing to: --install "${USR_BIN_DIR}/docker-compose"

Suggested change
if ! [ -e ${DOCKER_COMPOSE_SWITCH_BIN} ]; then
logStep "Installing Docker Switch..."
downloadBinaryLatestRelease "${DOCKER_COMPOSE_SWITCH_REPO}" "${DOCKER_COMPOSE_SWITCH_ASSET}" "${DOCKER_COMPOSE_SWITCH_BIN}"
# Set Docker Compose Switch to replace Docker Compose v1
runCmdAndLog update-alternatives \
--install ${USR_BIN_DIR}/docker-compose \
if ! [ -e "${DOCKER_COMPOSE_SWITCH_BIN}" ]; then
logStep "Installing Docker Switch..."
downloadBinaryLatestRelease "${DOCKER_COMPOSE_SWITCH_REPO}" "${DOCKER_COMPOSE_SWITCH_ASSET}" "${DOCKER_COMPOSE_SWITCH_BIN}"
# Set Docker Compose Switch to replace Docker Compose v1
runCmdAndLog update-alternatives \
--install "${USR_BIN_DIR}/docker-compose" \

Copilot uses AI. Check for mistakes.
Comment thread bootstrap.sh Outdated
logStep "Cleanup caches..."
runCmdAndLog ${APT_CMD} clean
for STEP in "${STEPS[@]}"; do
${STEP}
Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variable ${STEP} should be quoted when used in command execution. While function names are unlikely to contain spaces, quoting is a best practice for consistency and to prevent unexpected behavior. Consider changing to: "${STEP}"

Suggested change
${STEP}
"${STEP}"

Copilot uses AI. Check for mistakes.
Comment thread bootstrap.sh Outdated
local DOCKER_COMPOSE_SWITCH_BIN="${USR_BIN_DIR}/compose-switch"
local DOCKER_COMPOSE_SWITCH_REPO="docker/compose-switch"
local DOCKER_COMPOSE_SWITCH_ASSET="docker-compose-linux-amd64"
if ! [ -e ${DOCKER_COMPOSE_SWITCH_BIN} ]; then
Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variable ${DOCKER_COMPOSE_SWITCH_BIN} should be quoted in the test condition. Without quotes, the test may fail if the path contains spaces or special characters. This is inconsistent with line 319 where ${DOCKER_COMPOSE_BIN} is properly quoted as "${DOCKER_COMPOSE_BIN}". Consider changing to: if ! [ -e "${DOCKER_COMPOSE_SWITCH_BIN}" ]; then

Suggested change
if ! [ -e ${DOCKER_COMPOSE_SWITCH_BIN} ]; then
if ! [ -e "${DOCKER_COMPOSE_SWITCH_BIN}" ]; then

Copilot uses AI. Check for mistakes.
Comment thread bootstrap.sh
local DOCKER_COMPOSE_SWITCH_ASSET="docker-compose-linux-amd64"
if ! [ -e ${DOCKER_COMPOSE_SWITCH_BIN} ]; then
logStep "Installing Docker Switch..."
downloadBinaryLatestRelease "${DOCKER_COMPOSE_SWITCH_REPO}" "${DOCKER_COMPOSE_SWITCH_ASSET}" "${DOCKER_COMPOSE_SWITCH_BIN}"
Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The downloadBinaryLatestRelease invocation for Docker Compose Switch downloads a remote binary from the docker/compose-switch GitHub repository and marks it executable without any checksum or signature verification. If that repository or the delivery channel is compromised, attackers can replace docker-compose with a malicious binary that runs with the privileges of any user invoking it. Mitigate by pinning to a specific, trusted release/commit and verifying the downloaded binary against an expected hash or signature before installation.

Copilot uses AI. Check for mistakes.
Comment thread bootstrap.sh
local SPEEDTEST_ASSET="speedtest.py"
if ! [ -e ${SPEEDTEST_BIN} ]; then
logStep "Installing SpeedTest CLI..."
downloadBinaryLatestRelease "${SPEEDTEST_REPO}" "${SPEEDTEST_ASSET}" "${SPEEDTEST_BIN}" "raw"
Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This downloadBinaryLatestRelease call pulls the speedtest.py asset from the third-party sivel/speedtest-cli GitHub repository and installs it as an executable without any integrity check. A compromise of that repository or the transport could lead to arbitrary code execution whenever speedtest-cli is run on systems bootstrapped by this script. To reduce this risk, pin to a specific version or commit and verify the downloaded script against a known checksum or signature before making it executable.

Copilot uses AI. Check for mistakes.
Comment thread bootstrap.sh

if [ -x "${ZSH_BIN}" ]; then
if ! [ -d "${PREZTO_DIR}" ]; then
runCmdAndLog git clone --recursive "${PREZTO_REPO_URL}" "${PREZTO_DIR}"
Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This step clones the sorin-ionescu/prezto repository from GitHub at a mutable branch head and wires it into the login shell without any pinning or integrity verification. If that third-party repository is ever compromised, an attacker can execute arbitrary code in every Zsh session for this user, with access to any loaded credentials or secrets. Hardening this step requires pinning to a specific commit or signed tag and verifying it before using it as part of the login shell configuration.

Copilot uses AI. Check for mistakes.
Comment thread bootstrap.sh
if ! [ -e "${DOCKER_COMPOSE_BIN}" ]; then
mkdir -p "${DOCKER_CLI_PLUGINS_DIR}"
logStep "Installing Docker Compose..."
downloadBinaryLatestRelease "${DOCKER_COMPOSE_REPO}" "${DOCKER_COMPOSE_ASSET}" "${DOCKER_COMPOSE_BIN}"
Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The call to downloadBinaryLatestRelease here fetches and installs the Docker Compose binary from GitHub releases without any integrity verification before making it executable via downloadLatestReleaseArtifact/chmod +x. If the docker/compose repository or the network path is compromised, a malicious binary could be installed and later executed with the same privileges as this bootstrap script. Consider pinning to a specific release (or commit) and validating the downloaded artifact against a known checksum or signature before using it.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

🤖 Fix all issues with AI agents
In `@bootstrap.sh`:
- Around line 248-253: Add a standard bash doc block before each new step
function (e.g., stepUpgradePackages and the other new step functions)
documenting usage, arguments, return values, and examples; specifically insert a
comment block immediately above the function definition that includes a brief
description, a "Usage:" line showing the function call, an "Args:" section (or
"None" if no args), a "Returns:" section describing exit codes, and an
"Example:" demonstrating invocation so it matches the repository's doc block
template and coding guidelines.
- Around line 313-326: In stepInstallDockerCompose, ensure all actions are
executed through runCmdAndLog and add a completion log: replace direct calls to
mkdir -p and downloadBinaryLatestRelease with runCmdAndLog invocations (or
update downloadBinaryLatestRelease to internally call runCmdAndLog) so the
directory creation and the download are logged, and after a successful download
call logStep (e.g., "Docker Compose installed.") to mirror the install-complete
pattern used by other installers.
- Around line 361-372: The stepInstallSpeedTest function currently calls
downloadBinaryLatestRelease directly and lacks a completion log; change it to
invoke downloadBinaryLatestRelease through runCmdAndLog (so output is captured)
when installing SPEEDTEST_BIN and then add a logStep call after successful
download to indicate completion; locate the stepInstallSpeedTest function and
replace the direct downloadBinaryLatestRelease invocation with runCmdAndLog
calling downloadBinaryLatestRelease (preserving arguments SPEEDTEST_REPO,
SPEEDTEST_ASSET, SPEEDTEST_BIN, "raw") and add a final logStep "SpeedTest CLI
installed." (or similar) in the else branch pattern.
- Around line 290-311: The install branch of function stepInstallDocker logs the
start but never logs successful completion; add a completion log after the final
runCmdAndLog call. Specifically, inside stepInstallDocker, after the block that
runs ${APT_INSTALL} docker-ce docker-ce-cli containerd.io docker-compose-plugin,
call logStep (or the project's standard success log function) with a message
like "Docker installation complete" so the install pattern (check, start log,
runCmdAndLog..., completion log) is followed; ensure you reference the same
symbols (stepInstallDocker, runCmdAndLog, logStep) when making the change.
- Around line 374-385: In stepSetupPython(), the symlink creation currently
calls ln -s directly and isn't logged; replace the direct ln invocation with
runCmdAndLog so the command output is captured and add a completion log after
successful symlink creation. Specifically, in function stepSetupPython(), when
detecting python3 at PYTHON3_BIN, call runCmdAndLog to create the symlink from
PYTHON3_BIN to PYTHON_BIN (instead of ln -s) and then call log (or logStep) to
record completion of the symlink step.
- Around line 271-288: The stepInstallTools function installs many packages
without checking for existing installs or logging completion; update it to
follow the required pattern by checking each tool before installing (use command
-v or similar), log start for each tool, call runCmdAndLog with APT_INSTALL for
only missing tools (referencing stepInstallTools and runCmdAndLog), and add a
final logStep or logInfo message indicating completion for each installed tool
and for the entire step. Ensure you reference the same APT_INSTALL variable and
keep the list of packages but iterate/guard per-package so already-installed
tools are skipped and successful installs are logged.
- Around line 328-344: The stepInstallDockerSwitch function currently calls
downloadBinaryLatestRelease directly and lacks a completion log; change the
download invocation to run via runCmdAndLog (e.g., wrap the
downloadBinaryLatestRelease call so failures are logged/handled) and after the
update-alternatives runCmdAndLog call add an explicit success log (use logStep
or similar) referencing DOCKER_COMPOSE_SWITCH_BIN and the function name
stepInstallDockerSwitch so the install start, command execution, and completion
are all logged.
- Around line 387-415: In stepInstallZshPrezto, route the raw git/curl/chsh
invocations through runCmdAndLog and add a completion log: replace the direct
git clone, ${CURL_CMD} downloads and chsh call inside the if ! [ -d
"${PREZTO_DIR}" ] block with runCmdAndLog git clone --recursive
"${PREZTO_REPO_URL}" "${PREZTO_DIR}", runCmdAndLog ${CURL_CMD} "${PREZTORC_URL}"
-o "${PREZTO_DIR}/runcoms/zpreztorc", runCmdAndLog ${CURL_CMD} "${P10K_URL}" -o
"${HOME}/.p10k.zsh", and runCmdAndLog ${ZSH_BIN} -c '...' (or runCmdAndLog chsh
-s /bin/zsh); then add a log (e.g., log "Prezto installation complete.") after
successful setup and keep the existing "Prezto already installed." path
unchanged.
- Around line 346-359: In stepInstallNeoVim, update the unquoted command
substitution used in the update-alternatives calls to be quoted (e.g., use
"$(which nvim)" or better "$(command -v nvim)") to fix ShellCheck SC2046, and
add a completion logStep (e.g., logStep "NeoVim installed.") after the
installation/update-alternatives steps so the function follows the
check/start/install/complete logging pattern; adjust the two lines referencing
update-alternatives and append the completion log inside stepInstallNeoVim.
- Around line 262-269: In stepSetLocales the locale-gen invocation and command
check use unquoted expansions which can cause word-splitting (SC2068); update
usages of LOCALE_GEN and LOCALES to use quoted expansions: quote the command
lookup check (e.g. command -v "${LOCALE_GEN}"), and pass the generator and the
array as separate quoted args to runCmdAndLog and the locale-gen invocation by
using "${LOCALE_GEN}" and "${LOCALES[@]}" so each locale element is preserved;
keep runCmdAndLog and APT_INSTALL usage the same but quote their arguments where
applicable.

Comment thread bootstrap.sh
Comment thread bootstrap.sh
Comment thread bootstrap.sh
Comment on lines +271 to +288
function stepInstallTools() {
logStep "Installing tools..."
runCmdAndLog ${APT_INSTALL} \
byobu \
curl \
fd-find \
fzf \
git \
htop \
ripgrep \
silversearcher-ag \
software-properties-common \
tig \
unzip \
vim \
wget \
zip
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Guard tool installs and log completion.

This step installs a large tool bundle without an “already installed” check or a completion log, which breaks the required install pattern.

✅ Suggested structure
 function stepInstallTools() {
     logStep "Installing tools..."
-    runCmdAndLog ${APT_INSTALL} \
-        byobu \
-        curl \
-        fd-find \
-        fzf \
-        git \
-        htop \
-        ripgrep \
-        silversearcher-ag \
-        software-properties-common \
-        tig \
-        unzip \
-        vim \
-        wget \
-        zip
+    local TOOL_PACKAGES=(
+        byobu
+        curl
+        fd-find
+        fzf
+        git
+        htop
+        ripgrep
+        silversearcher-ag
+        software-properties-common
+        tig
+        unzip
+        vim
+        wget
+        zip
+    )
+    if ! dpkg -s "${TOOL_PACKAGES[@]}" >/dev/null 2>&1; then
+        runCmdAndLog ${APT_INSTALL} "${TOOL_PACKAGES[@]}"
+        log "Tools installed."
+    else
+        log "Tools already installed."
+    fi
 }

Based on learnings: Each tool installation must follow the pattern: check if installed, log start, run installation via runCmdAndLog, log completion.

🤖 Prompt for AI Agents
In `@bootstrap.sh` around lines 271 - 288, The stepInstallTools function installs
many packages without checking for existing installs or logging completion;
update it to follow the required pattern by checking each tool before installing
(use command -v or similar), log start for each tool, call runCmdAndLog with
APT_INSTALL for only missing tools (referencing stepInstallTools and
runCmdAndLog), and add a final logStep or logInfo message indicating completion
for each installed tool and for the entire step. Ensure you reference the same
APT_INSTALL variable and keep the list of packages but iterate/guard per-package
so already-installed tools are skipped and successful installs are logged.

Comment thread bootstrap.sh
Comment thread bootstrap.sh
Comment on lines +313 to +326
function stepInstallDockerCompose() {
# https://github.com/docker/compose
local DOCKER_CLI_PLUGINS_DIR="/usr/local/lib/docker/cli-plugins"
local DOCKER_COMPOSE_BIN="${DOCKER_CLI_PLUGINS_DIR}/docker-compose"
local DOCKER_COMPOSE_REPO="docker/compose"
local DOCKER_COMPOSE_ASSET="docker-compose-linux-$(uname -m)"
if ! [ -e "${DOCKER_COMPOSE_BIN}" ]; then
mkdir -p "${DOCKER_CLI_PLUGINS_DIR}"
logStep "Installing Docker Compose..."
downloadBinaryLatestRelease "${DOCKER_COMPOSE_REPO}" "${DOCKER_COMPOSE_ASSET}" "${DOCKER_COMPOSE_BIN}"
else
logStep "Docker Compose already installed."
fi
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Log Docker Compose install actions via runCmdAndLog and add completion log.

mkdir -p and the download path bypass runCmdAndLog, so logs are incomplete. Add completion logging and consider routing the download through runCmdAndLog (or update the helper to use it internally).

✅ Suggested fix (partial)
     if ! [ -e "${DOCKER_COMPOSE_BIN}" ]; then
-        mkdir -p "${DOCKER_CLI_PLUGINS_DIR}"
+        runCmdAndLog mkdir -p "${DOCKER_CLI_PLUGINS_DIR}"
         logStep "Installing Docker Compose..."
         downloadBinaryLatestRelease "${DOCKER_COMPOSE_REPO}" "${DOCKER_COMPOSE_ASSET}" "${DOCKER_COMPOSE_BIN}"
+        log "Docker Compose installed."
     else
         logStep "Docker Compose already installed."
     fi

Based on learnings: Each tool installation must follow the pattern: check if installed, log start, run installation via runCmdAndLog, log completion.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function stepInstallDockerCompose() {
# https://github.com/docker/compose
local DOCKER_CLI_PLUGINS_DIR="/usr/local/lib/docker/cli-plugins"
local DOCKER_COMPOSE_BIN="${DOCKER_CLI_PLUGINS_DIR}/docker-compose"
local DOCKER_COMPOSE_REPO="docker/compose"
local DOCKER_COMPOSE_ASSET="docker-compose-linux-$(uname -m)"
if ! [ -e "${DOCKER_COMPOSE_BIN}" ]; then
mkdir -p "${DOCKER_CLI_PLUGINS_DIR}"
logStep "Installing Docker Compose..."
downloadBinaryLatestRelease "${DOCKER_COMPOSE_REPO}" "${DOCKER_COMPOSE_ASSET}" "${DOCKER_COMPOSE_BIN}"
else
logStep "Docker Compose already installed."
fi
}
function stepInstallDockerCompose() {
# https://github.com/docker/compose
local DOCKER_CLI_PLUGINS_DIR="/usr/local/lib/docker/cli-plugins"
local DOCKER_COMPOSE_BIN="${DOCKER_CLI_PLUGINS_DIR}/docker-compose"
local DOCKER_COMPOSE_REPO="docker/compose"
local DOCKER_COMPOSE_ASSET="docker-compose-linux-$(uname -m)"
if ! [ -e "${DOCKER_COMPOSE_BIN}" ]; then
runCmdAndLog mkdir -p "${DOCKER_CLI_PLUGINS_DIR}"
logStep "Installing Docker Compose..."
downloadBinaryLatestRelease "${DOCKER_COMPOSE_REPO}" "${DOCKER_COMPOSE_ASSET}" "${DOCKER_COMPOSE_BIN}"
log "Docker Compose installed."
else
logStep "Docker Compose already installed."
fi
}
🧰 Tools
🪛 Shellcheck (0.11.0)

[warning] 318-318: Declare and assign separately to avoid masking return values.

(SC2155)

🤖 Prompt for AI Agents
In `@bootstrap.sh` around lines 313 - 326, In stepInstallDockerCompose, ensure all
actions are executed through runCmdAndLog and add a completion log: replace
direct calls to mkdir -p and downloadBinaryLatestRelease with runCmdAndLog
invocations (or update downloadBinaryLatestRelease to internally call
runCmdAndLog) so the directory creation and the download are logged, and after a
successful download call logStep (e.g., "Docker Compose installed.") to mirror
the install-complete pattern used by other installers.

Comment thread bootstrap.sh
Comment on lines +328 to +344
function stepInstallDockerSwitch() {
local DOCKER_COMPOSE_SWITCH_BIN="${USR_BIN_DIR}/compose-switch"
local DOCKER_COMPOSE_SWITCH_REPO="docker/compose-switch"
local DOCKER_COMPOSE_SWITCH_ASSET="docker-compose-linux-amd64"
if ! [ -e ${DOCKER_COMPOSE_SWITCH_BIN} ]; then
logStep "Installing Docker Switch..."
downloadBinaryLatestRelease "${DOCKER_COMPOSE_SWITCH_REPO}" "${DOCKER_COMPOSE_SWITCH_ASSET}" "${DOCKER_COMPOSE_SWITCH_BIN}"
# Set Docker Compose Switch to replace Docker Compose v1
runCmdAndLog update-alternatives \
--install ${USR_BIN_DIR}/docker-compose \
docker-compose \
"${DOCKER_COMPOSE_SWITCH_BIN}" \
99
else
logStep "Docker Switch already installed."
fi
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Ensure Docker Switch install is logged and completes with explicit success log.

The download path isn’t routed through runCmdAndLog, and there’s no completion log after the install.

✅ Suggested fix (add completion log)
         runCmdAndLog update-alternatives \
             --install ${USR_BIN_DIR}/docker-compose \
             docker-compose \
             "${DOCKER_COMPOSE_SWITCH_BIN}" \
             99
+        log "Docker Switch installed."
     else
         logStep "Docker Switch already installed."
     fi

Based on learnings: Each tool installation must follow the pattern: check if installed, log start, run installation via runCmdAndLog, log completion.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function stepInstallDockerSwitch() {
local DOCKER_COMPOSE_SWITCH_BIN="${USR_BIN_DIR}/compose-switch"
local DOCKER_COMPOSE_SWITCH_REPO="docker/compose-switch"
local DOCKER_COMPOSE_SWITCH_ASSET="docker-compose-linux-amd64"
if ! [ -e ${DOCKER_COMPOSE_SWITCH_BIN} ]; then
logStep "Installing Docker Switch..."
downloadBinaryLatestRelease "${DOCKER_COMPOSE_SWITCH_REPO}" "${DOCKER_COMPOSE_SWITCH_ASSET}" "${DOCKER_COMPOSE_SWITCH_BIN}"
# Set Docker Compose Switch to replace Docker Compose v1
runCmdAndLog update-alternatives \
--install ${USR_BIN_DIR}/docker-compose \
docker-compose \
"${DOCKER_COMPOSE_SWITCH_BIN}" \
99
else
logStep "Docker Switch already installed."
fi
}
function stepInstallDockerSwitch() {
local DOCKER_COMPOSE_SWITCH_BIN="${USR_BIN_DIR}/compose-switch"
local DOCKER_COMPOSE_SWITCH_REPO="docker/compose-switch"
local DOCKER_COMPOSE_SWITCH_ASSET="docker-compose-linux-amd64"
if ! [ -e ${DOCKER_COMPOSE_SWITCH_BIN} ]; then
logStep "Installing Docker Switch..."
downloadBinaryLatestRelease "${DOCKER_COMPOSE_SWITCH_REPO}" "${DOCKER_COMPOSE_SWITCH_ASSET}" "${DOCKER_COMPOSE_SWITCH_BIN}"
# Set Docker Compose Switch to replace Docker Compose v1
runCmdAndLog update-alternatives \
--install ${USR_BIN_DIR}/docker-compose \
docker-compose \
"${DOCKER_COMPOSE_SWITCH_BIN}" \
99
log "Docker Switch installed."
else
logStep "Docker Switch already installed."
fi
}
🤖 Prompt for AI Agents
In `@bootstrap.sh` around lines 328 - 344, The stepInstallDockerSwitch function
currently calls downloadBinaryLatestRelease directly and lacks a completion log;
change the download invocation to run via runCmdAndLog (e.g., wrap the
downloadBinaryLatestRelease call so failures are logged/handled) and after the
update-alternatives runCmdAndLog call add an explicit success log (use logStep
or similar) referencing DOCKER_COMPOSE_SWITCH_BIN and the function name
stepInstallDockerSwitch so the install start, command execution, and completion
are all logged.

Comment thread bootstrap.sh
Comment thread bootstrap.sh
Comment on lines +361 to +372
function stepInstallSpeedTest() {
# https://github.com/sivel/speedtest-cli
local SPEEDTEST_BIN="${USR_BIN_DIR}/speedtest-cli"
local SPEEDTEST_REPO="sivel/speedtest-cli"
local SPEEDTEST_ASSET="speedtest.py"
if ! [ -e ${SPEEDTEST_BIN} ]; then
logStep "Installing SpeedTest CLI..."
downloadBinaryLatestRelease "${SPEEDTEST_REPO}" "${SPEEDTEST_ASSET}" "${SPEEDTEST_BIN}" "raw"
else
logStep "SpeedTest CLI already installed."
fi
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Log SpeedTest install completion (and keep download output in logs).

The download path bypasses runCmdAndLog, and there’s no completion log.

✅ Suggested fix (add completion log)
     if ! [ -e ${SPEEDTEST_BIN} ]; then
         logStep "Installing SpeedTest CLI..."
         downloadBinaryLatestRelease "${SPEEDTEST_REPO}" "${SPEEDTEST_ASSET}" "${SPEEDTEST_BIN}" "raw"
+        log "SpeedTest CLI installed."
     else
         logStep "SpeedTest CLI already installed."
     fi

Based on learnings: Each tool installation must follow the pattern: check if installed, log start, run installation via runCmdAndLog, log completion.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function stepInstallSpeedTest() {
# https://github.com/sivel/speedtest-cli
local SPEEDTEST_BIN="${USR_BIN_DIR}/speedtest-cli"
local SPEEDTEST_REPO="sivel/speedtest-cli"
local SPEEDTEST_ASSET="speedtest.py"
if ! [ -e ${SPEEDTEST_BIN} ]; then
logStep "Installing SpeedTest CLI..."
downloadBinaryLatestRelease "${SPEEDTEST_REPO}" "${SPEEDTEST_ASSET}" "${SPEEDTEST_BIN}" "raw"
else
logStep "SpeedTest CLI already installed."
fi
}
function stepInstallSpeedTest() {
# https://github.com/sivel/speedtest-cli
local SPEEDTEST_BIN="${USR_BIN_DIR}/speedtest-cli"
local SPEEDTEST_REPO="sivel/speedtest-cli"
local SPEEDTEST_ASSET="speedtest.py"
if ! [ -e ${SPEEDTEST_BIN} ]; then
logStep "Installing SpeedTest CLI..."
downloadBinaryLatestRelease "${SPEEDTEST_REPO}" "${SPEEDTEST_ASSET}" "${SPEEDTEST_BIN}" "raw"
log "SpeedTest CLI installed."
else
logStep "SpeedTest CLI already installed."
fi
}
🤖 Prompt for AI Agents
In `@bootstrap.sh` around lines 361 - 372, The stepInstallSpeedTest function
currently calls downloadBinaryLatestRelease directly and lacks a completion log;
change it to invoke downloadBinaryLatestRelease through runCmdAndLog (so output
is captured) when installing SPEEDTEST_BIN and then add a logStep call after
successful download to indicate completion; locate the stepInstallSpeedTest
function and replace the direct downloadBinaryLatestRelease invocation with
runCmdAndLog calling downloadBinaryLatestRelease (preserving arguments
SPEEDTEST_REPO, SPEEDTEST_ASSET, SPEEDTEST_BIN, "raw") and add a final logStep
"SpeedTest CLI installed." (or similar) in the else branch pattern.

Comment thread bootstrap.sh
Comment thread bootstrap.sh
claude and others added 2 commits February 1, 2026 14:57
Extract each installation phase into a named function and define a STEPS
array that drives execution via a simple loop. TOTAL_STEPS is derived
from the array length, so adding or removing a step automatically keeps
the [N/TOTAL] progress counter accurate.

This replaces the previous inline script structure where the step count
had to be manually maintained as a hardcoded constant.

https://claude.ai/code/session_01Uic8AmN9C1xoxUT38FPMAy
@yorch yorch force-pushed the claude/add-processing-progress-feedback-34Ai1 branch from 5971e9b to 4d7f5a8 Compare February 1, 2026 19:59
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@bootstrap.sh`:
- Around line 450-485: The stepInstallZshPrezto function currently calls the
installer unconditionally; add a pre-check that uses command -v zsh (ZSH_BIN) to
detect an existing zsh before running runCmdAndLog ${APT_INSTALL} zsh, and only
proceed with cloning Prezto and fetching configs if zsh is missing;
specifically, move or add the ZSH_BIN assignment and an early-if that logs "ZSH
already installed." and returns when found, otherwise log the start, run
runCmdAndLog ${APT_INSTALL} zsh, re-evaluate ZSH_BIN and continue with the
existing Prezto install logic (references: stepInstallZshPrezto, ZSH_BIN,
APT_INSTALL, runCmdAndLog).
🧹 Nitpick comments (1)
bootstrap.sh (1)

162-171: Use ${*} in logStep to match the interpolation guideline.

$* should be braced for consistency with the ${VAR} rule.

🛠️ Suggested tweak
-    log "[${CURRENT_STEP}/${TOTAL_STEPS}] $*"
+    log "[${CURRENT_STEP}/${TOTAL_STEPS}] ${*}"

As per coding guidelines: Use SCREAMING_SNAKE_CASE for bash variable names and ${VAR} interpolation instead of $VAR.

Comment thread bootstrap.sh
Comment on lines +450 to +485
# Install ZSH with Prezto framework and Powerlevel10k theme
# Usage: stepInstallZshPrezto
# Arguments: none
# Returns: 0 on success, non-zero on error
# Example: stepInstallZshPrezto
function stepInstallZshPrezto() {
# https://github.com/sorin-ionescu/prezto
logStep "Installing ZSH and Prezto..."
runCmdAndLog ${APT_INSTALL} zsh
local ZSH_BIN
ZSH_BIN=$(command -v zsh)
local PREZTO_DIR="${HOME}/.zprezto"
local PREZTORC_URL="https://raw.githubusercontent.com/yorch/ubuntu-server-bootstrap/main/config/zpreztorc"
local P10K_URL="https://raw.githubusercontent.com/yorch/ubuntu-server-bootstrap/main/config/p10k.zsh"
local PREZTO_REPO_URL="https://github.com/sorin-ionescu/prezto.git"

if [ -x "${ZSH_BIN}" ]; then
if ! [ -d "${PREZTO_DIR}" ]; then
runCmdAndLog git clone --recursive "${PREZTO_REPO_URL}" "${PREZTO_DIR}"
runCmdAndLog ${CURL_CMD} "${PREZTORC_URL}" -o "${PREZTO_DIR}/runcoms/zpreztorc"
runCmdAndLog ${CURL_CMD} "${P10K_URL}" -o "${HOME}/.p10k.zsh"
runCmdAndLog ${ZSH_BIN} -c "
setopt EXTENDED_GLOB
for rcfile in \"\${HOME}\"/.zprezto/runcoms/^README.md(.N); do
ln -s \"\$rcfile\" \"\${HOME}/.\${rcfile:t}\"
done
"
runCmdAndLog chsh -s /bin/zsh
log "ZSH and Prezto installed."
else
log "Prezto already installed."
fi
else
log "ERROR - Could not find ZSH even though we tried to install it"
fi
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add a pre-check before installing ZSH.

The step installs ZSH unconditionally; the required pattern calls for checking whether it’s already present.

✅ Possible fix
 function stepInstallZshPrezto() {
     # https://github.com/sorin-ionescu/prezto
     logStep "Installing ZSH and Prezto..."
-    runCmdAndLog ${APT_INSTALL} zsh
+    if ! command -v zsh >/dev/null 2>&1; then
+        log "Installing ZSH..."
+        runCmdAndLog ${APT_INSTALL} zsh
+        log "ZSH installed."
+    else
+        log "ZSH already installed."
+    fi
     local ZSH_BIN
     ZSH_BIN=$(command -v zsh)
     local PREZTO_DIR="${HOME}/.zprezto"

Based on learnings: Each tool installation must follow the pattern: check if installed, log start, run installation via runCmdAndLog, log completion.

🤖 Prompt for AI Agents
In `@bootstrap.sh` around lines 450 - 485, The stepInstallZshPrezto function
currently calls the installer unconditionally; add a pre-check that uses command
-v zsh (ZSH_BIN) to detect an existing zsh before running runCmdAndLog
${APT_INSTALL} zsh, and only proceed with cloning Prezto and fetching configs if
zsh is missing; specifically, move or add the ZSH_BIN assignment and an early-if
that logs "ZSH already installed." and returns when found, otherwise log the
start, run runCmdAndLog ${APT_INSTALL} zsh, re-evaluate ZSH_BIN and continue
with the existing Prezto install logic (references: stepInstallZshPrezto,
ZSH_BIN, APT_INSTALL, runCmdAndLog).

@yorch yorch merged commit c199b05 into main Feb 1, 2026
5 checks passed
@yorch yorch deleted the claude/add-processing-progress-feedback-34Ai1 branch February 1, 2026 20:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants