Skip to content

fix: macOS compatibility and shell robustness fixes - #105

Open
fericsepi wants to merge 1 commit into
RchGrav:mainfrom
frankapps-io:fix/macos-compat-and-unbound-vars
Open

fix: macOS compatibility and shell robustness fixes#105
fericsepi wants to merge 1 commit into
RchGrav:mainfrom
frankapps-io:fix/macos-compat-and-unbound-vars

Conversation

@fericsepi

@fericsepi fericsepi commented Apr 12, 2026

Copy link
Copy Markdown

featuring:

  • replace awk template substitution with pure bash loop in Dockerfile generation to avoid BSD awk issues with multi-line -v values on macOS
  • guard unbound variables (user_mcp_file, project_mcp_file) with to prevent errors under set -u
  • rename --verbose to --debug for claudebox debug output so --verbose can pass through to claude CLI

Summary by Sourcery

Improve macOS compatibility and shell robustness in Dockerfile generation, CLI flags, and container cleanup.

Bug Fixes:

  • Replace awk-based Dockerfile templating with a bash loop to avoid BSD awk issues on macOS when handling multi-line substitutions.
  • Guard optional MCP temp file variables with default expansions to prevent errors under set -u during container cleanup.

Enhancements:

  • Rename the claudebox debug flag from --verbose to --debug so --verbose can pass through to the underlying Claude CLI.

- replace awk template substitution with pure bash loop in Dockerfile generation to avoid BSD awk issues with multi-line -v values on macOS
- guard unbound variables (user_mcp_file, project_mcp_file) with  to prevent errors under set -u
- rename --verbose to --debug for claudebox debug output so --verbose can pass through to claude CLI
@sourcery-ai

sourcery-ai Bot commented Apr 12, 2026

Copy link
Copy Markdown
Reviewer's guide (collapsed on small PRs)

Reviewer's Guide

Replaces awk-based Dockerfile templating with a pure Bash loop for better macOS compatibility, renames the claudebox debug flag from --verbose to --debug so --verbose can pass through to the Claude CLI, and guards optional MCP temp files against unset-variable errors under set -u.

Sequence diagram for CLI debug flag handling and Claude CLI passthrough

sequenceDiagram
    actor User
    participant ClaudeboxCLI as claudebox_shell
    participant CliLib as cli_sh
    participant DockerLib as docker_sh
    participant ClaudeCLI as claude_cli

    User->>ClaudeboxCLI: Run claudebox with flags
    ClaudeboxCLI->>CliLib: parse_cli_args argv
    CliLib->>CliLib: Identify HOST_ONLY_FLAGS
    CliLib->>CliLib: process_host_flags
    CliLib-->>ClaudeboxCLI: VERBOSE exported if --debug present

    alt debug_enabled
        ClaudeboxCLI->>DockerLib: run_claudebox_container with VERBOSE
    else no_debug
        ClaudeboxCLI->>DockerLib: run_claudebox_container
    end

    Note over ClaudeboxCLI,ClaudeCLI: --verbose is no longer consumed by claudebox

    ClaudeboxCLI->>ClaudeCLI: Invoke claude with passthrough flags including --verbose
    ClaudeCLI-->>ClaudeboxCLI: Claude output
    ClaudeboxCLI-->>User: Display result
Loading

Class diagram for updated shell modules and flags/cleanup behavior

classDiagram
    class main_sh {
        +string base_dockerfile
        +string final_dockerfile
        +string profile_installations
        +string labels
        +generate_project_dockerfile(base_dockerfile, profile_installations, labels) string
    }

    class cli_sh {
        +string[] HOST_ONLY_FLAGS
        +string[] CONTROL_FLAGS
        +string[] SCRIPT_COMMANDS
        +string[] CLI_HOST_FLAGS
        +parse_cli_args(argc, argv)
        +process_host_flags()
    }

    class docker_sh {
        +string~user_mcp_file~
        +string~project_mcp_file~
        +run_claudebox_container()
        +cleanup_temp_files()
    }

    main_sh --> docker_sh : uses
    main_sh --> cli_sh : uses

    %% Flag behavior details
    class HostOnlyFlags {
        +--debug
        +rebuild
    }

    cli_sh --> HostOnlyFlags : defines

    class DebugFlagHandling {
        +bool VERBOSE
        +handle_debug_flag(flag)
    }

    cli_sh --> DebugFlagHandling : exports VERBOSE when flag is --debug

    class OptionalMcpFiles {
        +string user_mcp_file_optional
        +string project_mcp_file_optional
        +guard_unset_variables()
    }

    docker_sh --> OptionalMcpFiles : guards with parameter expansion
Loading

File-Level Changes

Change Details Files
Replace awk-based Dockerfile placeholder substitution with a pure Bash implementation to avoid BSD awk issues on macOS.
  • Iterate over each line of the base Dockerfile using a Bash while-read loop.
  • Detect lines containing the PROFILE_INSTALLATIONS and LABELS placeholders via Bash regex matches.
  • Append the appropriate injected blocks or the original line to a final Dockerfile string, preserving newlines.
  • Remove dependency on awk -v multi-line variable handling for template substitution.
main.sh
Adjust CLI host flags so claudebox uses --debug for internal debugging and leaves --verbose for the underlying Claude CLI.
  • Update HOST_ONLY_FLAGS to include --debug instead of --verbose for host-level handling.
  • Change host flag processing to set VERBOSE when --debug is provided.
lib/cli.sh
Harden temporary MCP file cleanup against unset-variable errors when running with set -u.
  • Wrap references to user_mcp_file in ${var:-} expansions before string and file tests.
  • Wrap references to project_mcp_file in ${var:-} expansions before string and file tests.
lib/docker.sh

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've found 1 issue, and left some high level feedback:

  • In the Dockerfile templating guard, if grep -q '{{PROFILE_INSTALLATIONS}}' <<<"$final_dockerfile" grep -q '{{LABELS}}' <<<"$final_dockerfile"; then is missing a logical operator (||/&&) between the two grep calls, which will cause a syntax error; add the appropriate connector.
  • In the new bash-based template substitution, the regex on the right-hand side of [[ "$line" =~ ... ]] is quoted, which makes Bash treat it as a literal string rather than a regex; remove the surrounding quotes to ensure the character classes and anchors work as intended.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In the Dockerfile templating guard, `if grep -q '{{PROFILE_INSTALLATIONS}}' <<<"$final_dockerfile" grep -q '{{LABELS}}' <<<"$final_dockerfile"; then` is missing a logical operator (`||`/`&&`) between the two `grep` calls, which will cause a syntax error; add the appropriate connector.
- In the new bash-based template substitution, the regex on the right-hand side of `[[ "$line" =~ ... ]]` is quoted, which makes Bash treat it as a literal string rather than a regex; remove the surrounding quotes to ensure the character classes and anchors work as intended.

## Individual Comments

### Comment 1
<location path="main.sh" line_range="589-599" />
<code_context>
+    # Uses pure bash to avoid BSD awk issues with multi-line -v values on macOS
+    local final_dockerfile=""
+    while IFS= read -r line; do
+        if [[ "$line" =~ ^[[:space:]]*\{\{[[:space:]]*PROFILE_INSTALLATIONS[[:space:]]*\}\}[[:space:]]*$ ]]; then
+            final_dockerfile+="$profile_installations"$'\n'
+        elif [[ "$line" =~ ^[[:space:]]*\{\{[[:space:]]*LABELS[[:space:]]*\}\}[[:space:]]*$ ]]; then
+            final_dockerfile+="$labels"$'\n'
</code_context>
<issue_to_address>
**suggestion:** Double-check newline behavior when injecting multi-line `profile_installations`/`labels` blocks

Because `final_dockerfile+="$profile_installations"$'\n'` always appends a newline, any multi-line value that already ends with `\n` will now get an extra blank line. Previously, `awk` emitted `pi` unchanged, so this is a behavior change. If that extra blank line can affect Dockerfile parsing (e.g., around `RUN` or label blocks), consider trimming a trailing newline before appending (e.g. `final_dockerfile+="${profile_installations%$'\n'}"$'\n'`) or constructing the output via `printf` to match the old semantics.

```suggestion
    # Uses pure bash to avoid BSD awk issues with multi-line -v values on macOS
    local final_dockerfile=""
    while IFS= read -r line; do
        if [[ "$line" =~ ^[[:space:]]*\{\{[[:space:]]*PROFILE_INSTALLATIONS[[:space:]]*\}\}[[:space:]]*$ ]]; then
            # Trim a single trailing newline from profile_installations to avoid adding an extra blank line
            final_dockerfile+="${profile_installations%$'\n'}"$'\n'
        elif [[ "$line" =~ ^[[:space:]]*\{\{[[:space:]]*LABELS[[:space:]]*\}\}[[:space:]]*$ ]]; then
            # Trim a single trailing newline from labels to avoid adding an extra blank line
            final_dockerfile+="${labels%$'\n'}"$'\n'
        else
            final_dockerfile+="$line"$'\n'
        fi
    done <<<"$base_dockerfile"
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread main.sh
Comment on lines +589 to +599
# Uses pure bash to avoid BSD awk issues with multi-line -v values on macOS
local final_dockerfile=""
while IFS= read -r line; do
if [[ "$line" =~ ^[[:space:]]*\{\{[[:space:]]*PROFILE_INSTALLATIONS[[:space:]]*\}\}[[:space:]]*$ ]]; then
final_dockerfile+="$profile_installations"$'\n'
elif [[ "$line" =~ ^[[:space:]]*\{\{[[:space:]]*LABELS[[:space:]]*\}\}[[:space:]]*$ ]]; then
final_dockerfile+="$labels"$'\n'
else
final_dockerfile+="$line"$'\n'
fi
done <<<"$base_dockerfile"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion: Double-check newline behavior when injecting multi-line profile_installations/labels blocks

Because final_dockerfile+="$profile_installations"$'\n' always appends a newline, any multi-line value that already ends with \n will now get an extra blank line. Previously, awk emitted pi unchanged, so this is a behavior change. If that extra blank line can affect Dockerfile parsing (e.g., around RUN or label blocks), consider trimming a trailing newline before appending (e.g. final_dockerfile+="${profile_installations%$'\n'}"$'\n') or constructing the output via printf to match the old semantics.

Suggested change
# Uses pure bash to avoid BSD awk issues with multi-line -v values on macOS
local final_dockerfile=""
while IFS= read -r line; do
if [[ "$line" =~ ^[[:space:]]*\{\{[[:space:]]*PROFILE_INSTALLATIONS[[:space:]]*\}\}[[:space:]]*$ ]]; then
final_dockerfile+="$profile_installations"$'\n'
elif [[ "$line" =~ ^[[:space:]]*\{\{[[:space:]]*LABELS[[:space:]]*\}\}[[:space:]]*$ ]]; then
final_dockerfile+="$labels"$'\n'
else
final_dockerfile+="$line"$'\n'
fi
done <<<"$base_dockerfile"
# Uses pure bash to avoid BSD awk issues with multi-line -v values on macOS
local final_dockerfile=""
while IFS= read -r line; do
if [[ "$line" =~ ^[[:space:]]*\{\{[[:space:]]*PROFILE_INSTALLATIONS[[:space:]]*\}\}[[:space:]]*$ ]]; then
# Trim a single trailing newline from profile_installations to avoid adding an extra blank line
final_dockerfile+="${profile_installations%$'\n'}"$'\n'
elif [[ "$line" =~ ^[[:space:]]*\{\{[[:space:]]*LABELS[[:space:]]*\}\}[[:space:]]*$ ]]; then
# Trim a single trailing newline from labels to avoid adding an extra blank line
final_dockerfile+="${labels%$'\n'}"$'\n'
else
final_dockerfile+="$line"$'\n'
fi
done <<<"$base_dockerfile"

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.

1 participant