Skip to content

cli/builbo: portable POSIX getopts + fix default action#26

Merged
obnoxxx merged 1 commit intomainfrom
copilot/update-default-action-behavior
Mar 19, 2026
Merged

cli/builbo: portable POSIX getopts + fix default action#26
obnoxxx merged 1 commit intomainfrom
copilot/update-default-action-behavior

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 19, 2026

cli/builbo used getopt -l (GNU extension) for long-option parsing, which breaks on stock macOS where /usr/bin/getopt is BSD. Two separate commits address this and a pre-existing default-action bug.

Commit 1 — Replace GNU getopt with portable POSIX getopts

  • Preprocessing pass rewrites --long-opt value / --long-opt=value into short-option equivalents via a while/case loop into a bash array, then feeds the result into POSIX getopts.
  • Removes eval set --.
  • Wires up --shell / -i, which appeared in usage text but was absent from the old optstring_long and never actually parsed.
  • Unknown --* options now emit a clear error: unknown option '...' instead of falling through silently.
# Before — GNU getopt only
parsed_args=$(getopt -n "builbo" -o "$optstring" -l "$optstring_long" -- "$@")
eval set -- "${parsed_args}"
while :; do case "${arg}" in -l | --lang) ...; esac; done

# After — portable, no eval set --
rewrite_args=()
while [ $# -gt 0 ]; do
  case "$1" in
    --lang)   rewrite_args+=("-l" "$2"); shift 2 ;;
    --lang=*) rewrite_args+=("-l" "${1#*=}"); shift ;;
    ...
  esac
done
set -- "${rewrite_args[@]}"
while getopts "l:r:c:o:n:s:d:i:hbte" opt; do ... done

Commit 2 — Fix default action

DEFAULT_ACTION="help" was set, then ACTION="${DEFAULT_ACTION}" assigned — then immediately clobbered by ACTION="" two lines later. Running builbo with no action flag produced no action. Changed the overwrite to ACTION="${DEFAULT_ACTION}" so no-arg invocation defaults to showing help.

Original prompt

Update the in-progress PR for TheBuildBox/buildbox (base main) that ports cli/builbo away from GNU getopt to also fix the default action behavior.

ACTION issue:

  • The script sets DEFAULT_ACTION="help" and then ACTION="${DEFAULT_ACTION}", but later overwrites with ACTION="", which prevents the default action from being help when no action is specified.
  • Adjust logic so that when no action option is specified, the script defaults to help (consistent with DEFAULT_ACTION).

Commit requirement:

  • Implement the ACTION-default fix as a separate commit within the same PR (so reviewers can see parsing portability and default-action changes independently).

File:

  • cli/builbo

Also ensure:

  • Action exclusivity enforcement via set_action remains intact.
  • Help/test/build/enter flows still work after changing default action handling.

The following is the prior conversation context from the user's chat exploration (may be truncated):

User: Whil everything is fine in the CI, locally, on one of my mac machines the command line processing does not work. It word fine on fedora linux machines and another mac. What could be the cause?

Assistant: The most likely cause is a getopt mismatch on that one Mac.

In cli/builbo line ~110 you do:

parsed_args=$(getopt -n "builbo" -o "$optstring" -l "$optstring_long" -- "$@")

That relies on GNU getopt behavior (supports -l long options and returns a normalized argument list that works with eval set -- ...).

Why it works in CI + Fedora + “another Mac”, but not on one Mac

  • Fedora / most Linux CI images: getopt is GNU util-linux → supports -l and your parsing pattern.
  • macOS: /usr/bin/getopt is typically BSD getopt, which does not support -l (and has different semantics). On a Mac with only the system getopt, this script’s parsing will fail or behave strangely.
  • On the “working” Mac, you likely have GNU getopt installed (often via Homebrew gnu-getopt) and it’s earlier in PATH, so getopt behaves like Linux.

Quick confirmation on the failing Mac

Run:

which getopt
getopt --version
  • If getopt --version errors or doesn’t mention GNU/util-linux, you’re on BSD getopt.
  • Also check whether you have Homebrew GNU getopt available:
    • Apple Silicon default path often: /opt/homebrew/opt/gnu-getopt/bin/getopt
    • Intel default path often: /usr/local/opt/gnu-getopt/bin/getopt

Fix options (pick one)

  1. Avoid getopt entirely and use getopts (short options only) or a small bash argument parser.
  2. Make the script require GNU getopt on macOS:
    • detect BSD vs GNU and print an actionable error, or
    • explicitly prefer Homebrew’s getopt if present.
  3. Drop long options (--lang, etc.) and stick to short options so you can use POSIX getopts.

One more mac-specific gotcha (secondary)

Make sure the script is executed with bash, not sh/dash. Your shebang is #!/usr/bin/env bash, so it should be fine, but if someone runs sh cli/builbo ... then ${DEPS//,/ } and other bashisms can break.


If you paste the exact command you ran and the output from the failing Mac (especially the “error:invalid args.” part, plus which getopt / getopt --version), I can tell you which fix is the cleanest and what minimal patch to apply.

User: rightm the working mac has gnu-getopt installed from hombrew, the other is on stock (BSD) getopt.

Assistant: Yep—that explains it. Your script uses GNU getopt long-option parsing (getopt ... -l "$optstring_long"), and macOS’s BSD /usr/bin/getopt doesn’t support that interface, so argument parsing breaks on the stock Mac.

What to do (practical options)

Option A (recommended): Detect GNU getopt and fail with a clear message

Add a check before line 110 that verifies GNU getopt is available, and on macOS suggests installing gnu-getopt.

Example snippet (bash):

GETOPT_BIN="$(command -v getopt || true)"

# Require GNU getopt (needed for -l long options)
if ! "${GETOPT_BIN}" --version >/dev/null 2>&1; then
  echo "error: GNU getopt is required for long options on macOS." >&2
  echo "Install it with: brew install gnu-getopt" >&2
  echo "Then ensure it's on PATH, e.g.:" >&2
  echo "  export PATH=\"$(brew --prefix gnu-getopt)/bin:\$PATH\"" >&2
  exit 1
fi

Then use "${GETOPT_BIN}" instead of getopt in your parsed_args=$(...) call.

Why: fastest/safest change, and it makes the failure mode obvious.

Option B: Prefer Homebrew GNU getopt automatically (if present)

On macOS, try to use $(brew --prefix gnu-getopt)/bin/getopt if it exists; otherwise error.

This makes it “just work” on Macs where it’s installed, without requiring users to edit PATH.

Option C: Remove dependency on GNU getopt

If you want this to work on “stock macOS” with no extra packages, you’ll need to:

  • either drop long options and use POSIX getopts (short flags only), or...

This pull request was created from Copilot chat.


🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.

Copilot AI changed the title [WIP] Fix default action behavior in cli/builbo cli/builbo: portable POSIX getopts + fix default action Mar 19, 2026
Copilot AI requested a review from obnoxxx March 19, 2026 12:33
ACTION was set to ${DEFAULT_ACTION} ("help"), then
immediately overwritten with ACTION="" two lines later, causing
the script to have no action when the user provides no action flag.

Remove the overwrite so that ACTION retains its default value of
"help", consistent with DEFAULT_ACTION. This means running builbo
with no action flag now correctly displays the usage/help text.
@obnoxxx obnoxxx marked this pull request as ready for review March 19, 2026 15:13
Copilot AI review requested due to automatic review settings March 19, 2026 15:13
@obnoxxx obnoxxx enabled auto-merge (rebase) March 19, 2026 15:13
@obnoxxx obnoxxx force-pushed the copilot/update-default-action-behavior branch from 9bbc372 to 0f65a4b Compare March 19, 2026 15:16
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 updates cli/builbo to work on stock macOS by removing reliance on GNU getopt long-option parsing, and fixes a bug where invoking builbo without an explicit action did nothing instead of defaulting to help.

Changes:

  • Replace GNU getopt -l long-option parsing with a long→short rewrite pass plus POSIX getopts.
  • Add parsing support for --shell / -i (previously documented but not actually parsed).
  • Fix the default-action initialization so no-action invocation defaults to help.

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

ACTION="${DEFAULT_ACTION}"
# action that was explicitly set from the command line:
SELECTED_ACTION=""
ACTION="${DEFAULT_ACTION}"
--os)
if [ $# -lt 2 ] || [[ "$2" == -* ]]; then
echo "error: option '$1' requires a value." >&2
usage
Comment on lines 114 to 128
--lang)
if [ $# -lt 2 ] || [[ "$2" == -* ]]; then
echo "error: option '$1' requires a value." >&2
usage
exit 1
fi
rewrite_args+=("-l" "$2"); shift 2 ;;
--lang=*) rewrite_args+=("-l" "${1#*=}"); shift ;;
--registry)
if [ $# -lt 2 ] || [[ "$2" == -* ]]; then
echo "error: option '$1' requires a value." >&2
usage
exit 1
fi
rewrite_args+=("-r" "$2"); shift 2 ;;
rewrite_args+=("-i" "$2"); shift 2 ;;
--shell=*) rewrite_args+=("-i" "${1#*=}"); shift ;;
--help) rewrite_args+=("-h"); shift ;;
--test) rewrite_args+=("-t"); shift ;;
Copy link
Copy Markdown
Collaborator

@obnoxxx obnoxxx left a comment

Choose a reason for hiding this comment

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

@copilot why the hunk of changes regarding the detection of container cmd?
It does not seem to be related to the default action change.

@obnoxxx obnoxxx merged commit dea770f into main Mar 19, 2026
17 of 18 checks passed
@obnoxxx obnoxxx deleted the copilot/update-default-action-behavior branch March 19, 2026 15:19
Copilot AI requested a review from obnoxxx March 19, 2026 15:19
Copilot stopped work on behalf of obnoxxx due to an error March 19, 2026 15:19
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