From d326e6572a31776acb792ba3bf7b04a680291df2 Mon Sep 17 00:00:00 2001 From: marcie Date: Fri, 28 Aug 2026 18:26:10 -0500 Subject: [PATCH] framework tools/git: strict value guards + provider-failure normalization family-wide (codex blockers on PR #1464) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Value guards now reject option-like values: --anything always, and single-dash flag shapes (-h, -i). Previously -b --help consumed --help as the body and performed the write (codex example: issue-close -i --help proceeding to 'Closed GitHub issue #--help'). Multi-char dash-leading text (-start of a list) stays a legal value. - Every remaining direct provider exec (gh/tea/CMD arrays across issue-create/edit/assign/list/view, milestone-*, pr-create/edit, pr-close, issue-close/reopen) wrapped with rc capture and normalized to exit 1 with a stderr message — provider exit 2 no longer collides with the reserved usage-error status. - All 16 usage-contract suites gained option-like and short-flag arms (16/16 green). Existing suites re-verified; test-pr-edit and test-issue-create-interactive-auth fail identically with these changes stashed (environment-coupled, not regressions; documented). --- .../framework/tools/git/issue-assign.sh | 28 +++++++++++++------ .../mosaic/framework/tools/git/issue-close.sh | 8 ++++-- .../framework/tools/git/issue-comment.sh | 6 ++-- .../framework/tools/git/issue-create.sh | 12 ++++---- .../mosaic/framework/tools/git/issue-edit.sh | 18 +++++++----- .../mosaic/framework/tools/git/issue-list.sh | 20 +++++++------ .../framework/tools/git/issue-reopen.sh | 12 +++++--- .../mosaic/framework/tools/git/issue-view.sh | 6 ++-- .../mosaic/framework/tools/git/lane-brief.sh | 10 +++---- .../framework/tools/git/milestone-close.sh | 10 +++++-- .../framework/tools/git/milestone-create.sh | 22 ++++++++++----- .../framework/tools/git/milestone-list.sh | 10 +++++-- .../mosaic/framework/tools/git/pr-close.sh | 12 +++++--- .../mosaic/framework/tools/git/pr-create.sh | 18 ++++++------ .../mosaic/framework/tools/git/pr-edit.sh | 26 ++++++++++------- .../mosaic/framework/tools/git/pr-review.sh | 24 ++++++++++------ .../git/test-issue-assign-usage-contract.sh | 7 +++++ .../git/test-issue-close-usage-contract.sh | 7 +++++ .../git/test-issue-comment-usage-contract.sh | 7 +++++ .../git/test-issue-create-usage-contract.sh | 7 +++++ .../git/test-issue-edit-usage-contract.sh | 7 +++++ .../git/test-issue-list-usage-contract.sh | 7 +++++ .../git/test-issue-reopen-usage-contract.sh | 7 +++++ .../git/test-issue-view-usage-contract.sh | 7 +++++ .../git/test-lane-brief-usage-contract.sh | 7 +++++ .../test-milestone-close-usage-contract.sh | 7 +++++ .../test-milestone-create-usage-contract.sh | 7 +++++ .../git/test-milestone-list-usage-contract.sh | 7 +++++ .../tools/git/test-pr-close-usage-contract.sh | 7 +++++ .../git/test-pr-create-usage-contract.sh | 7 +++++ .../tools/git/test-pr-edit-usage-contract.sh | 7 +++++ .../git/test-pr-review-usage-contract.sh | 7 +++++ 32 files changed, 264 insertions(+), 90 deletions(-) diff --git a/packages/mosaic/framework/tools/git/issue-assign.sh b/packages/mosaic/framework/tools/git/issue-assign.sh index a899eec0..c1c26c0c 100755 --- a/packages/mosaic/framework/tools/git/issue-assign.sh +++ b/packages/mosaic/framework/tools/git/issue-assign.sh @@ -47,22 +47,22 @@ usage_error() { while [[ $# -gt 0 ]]; do case $1 in -i|--issue) - [[ $# -ge 2 ]] || usage_error "option $1 requires a value" + [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)" ISSUE="$2" shift 2 ;; -a|--assignee) - [[ $# -ge 2 ]] || usage_error "option $1 requires a value" + [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)" ASSIGNEE="$2" shift 2 ;; -l|--labels) - [[ $# -ge 2 ]] || usage_error "option $1 requires a value" + [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)" LABELS="$2" shift 2 ;; -m|--milestone) - [[ $# -ge 2 ]] || usage_error "option $1 requires a value" + [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)" MILESTONE="$2" shift 2 ;; @@ -90,20 +90,28 @@ PLATFORM=$(detect_platform) case "$PLATFORM" in github) if [[ -n "$ASSIGNEE" ]]; then - gh issue edit "$ISSUE" --add-assignee "$ASSIGNEE" + prov_rc=0 + gh issue edit "$ISSUE" --add-assignee "$ASSIGNEE" || prov_rc=$? + [[ "$prov_rc" -eq 0 ]] || { echo "Error: provider command failed (exit ${prov_rc}; provider failure, not a usage error)" >&2; exit 1; } fi if [[ "$REMOVE_ASSIGNEE" == true ]]; then # Get current assignees and remove them CURRENT=$(gh issue view "$ISSUE" --json assignees -q '.assignees[].login' 2>/dev/null | tr '\n' ',') if [[ -n "$CURRENT" ]]; then - gh issue edit "$ISSUE" --remove-assignee "${CURRENT%,}" + prov_rc=0 + gh issue edit "$ISSUE" --remove-assignee "${CURRENT%,}" || prov_rc=$? + [[ "$prov_rc" -eq 0 ]] || { echo "Error: provider command failed (exit ${prov_rc}; provider failure, not a usage error)" >&2; exit 1; } fi fi if [[ -n "$LABELS" ]]; then - gh issue edit "$ISSUE" --add-label "$LABELS" + prov_rc=0 + gh issue edit "$ISSUE" --add-label "$LABELS" || prov_rc=$? + [[ "$prov_rc" -eq 0 ]] || { echo "Error: provider command failed (exit ${prov_rc}; provider failure, not a usage error)" >&2; exit 1; } fi if [[ -n "$MILESTONE" ]]; then - gh issue edit "$ISSUE" --milestone "$MILESTONE" + prov_rc=0 + gh issue edit "$ISSUE" --milestone "$MILESTONE" || prov_rc=$? + [[ "$prov_rc" -eq 0 ]] || { echo "Error: provider command failed (exit ${prov_rc}; provider failure, not a usage error)" >&2; exit 1; } fi echo "Issue #$ISSUE updated successfully" ;; @@ -142,7 +150,9 @@ case "$PLATFORM" in fi if [[ "$NEEDS_EDIT" == true ]]; then - "${CMD[@]}" + prov_rc=0 + "${CMD[@]}" || prov_rc=$? + [[ "$prov_rc" -eq 0 ]] || { echo "Error: provider command failed (exit ${prov_rc}; provider failure, not a usage error)" >&2; exit 1; } echo "Issue #$ISSUE updated successfully" else echo "No changes specified" diff --git a/packages/mosaic/framework/tools/git/issue-close.sh b/packages/mosaic/framework/tools/git/issue-close.sh index 58850292..7109247c 100755 --- a/packages/mosaic/framework/tools/git/issue-close.sh +++ b/packages/mosaic/framework/tools/git/issue-close.sh @@ -25,14 +25,14 @@ usage_error() { while [[ $# -gt 0 ]]; do case $1 in -i|--issue) - [[ $# -ge 2 ]] || usage_error "option $1 requires a value" + [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)" ISSUE_NUMBER="$2" shift 2 ;; -b|--body|-c|--comment) # R1 (2026-08-28): --body is the canonical flag; -c/--comment stays # a backward-compatible alias. - [[ $# -ge 2 ]] || usage_error "option $1 requires a value" + [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)" COMMENT="$2" shift 2 ;; @@ -134,7 +134,9 @@ elif [[ "$PLATFORM" == "gitea" ]]; then exit 1 } fi - tea issue close "$ISSUE_NUMBER" --repo "$OWNER/$REPO" --login "$GITEA_LOGIN_NAME" + prov_rc=0 + tea issue close "$ISSUE_NUMBER" --repo "$OWNER/$REPO" --login "$GITEA_LOGIN_NAME" || prov_rc=$? + [[ "$prov_rc" -eq 0 ]] || { echo "Error: provider command failed (exit ${prov_rc}; provider failure, not a usage error)" >&2; exit 1; } else echo "No tea login configured for $(get_remote_host); using authenticated Gitea API fallback." >&2 if [[ -n "$COMMENT" ]]; then diff --git a/packages/mosaic/framework/tools/git/issue-comment.sh b/packages/mosaic/framework/tools/git/issue-comment.sh index e5858b35..93231c99 100755 --- a/packages/mosaic/framework/tools/git/issue-comment.sh +++ b/packages/mosaic/framework/tools/git/issue-comment.sh @@ -46,7 +46,7 @@ usage_error() { while [[ $# -gt 0 ]]; do case $1 in -i|--issue) - [[ $# -ge 2 ]] || usage_error "option $1 requires a value" + [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)" ISSUE_NUMBER="$2" shift 2 ;; @@ -54,12 +54,12 @@ while [[ $# -gt 0 ]]; do # R1 (2026-08-28): --body is the canonical flag, matching # issue-create/issue-edit/pr-create/pr-edit; -c/--comment stays a # backward-compatible alias. - [[ $# -ge 2 ]] || usage_error "option $1 requires a value" + [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)" COMMENT="$2" shift 2 ;; -l|--login) - [[ $# -ge 2 ]] || usage_error "option $1 requires a value" + [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)" LOGIN_OVERRIDE="$2" shift 2 ;; diff --git a/packages/mosaic/framework/tools/git/issue-create.sh b/packages/mosaic/framework/tools/git/issue-create.sh index 1cb378b4..3e42e14a 100755 --- a/packages/mosaic/framework/tools/git/issue-create.sh +++ b/packages/mosaic/framework/tools/git/issue-create.sh @@ -91,22 +91,22 @@ usage_error() { while [[ $# -gt 0 ]]; do case $1 in -t|--title) - [[ $# -ge 2 ]] || usage_error "option $1 requires a value" + [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)" TITLE="$2" shift 2 ;; -b|--body) - [[ $# -ge 2 ]] || usage_error "option $1 requires a value" + [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)" BODY="$2" shift 2 ;; -l|--labels) - [[ $# -ge 2 ]] || usage_error "option $1 requires a value" + [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)" LABELS="$2" shift 2 ;; -m|--milestone) - [[ $# -ge 2 ]] || usage_error "option $1 requires a value" + [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)" MILESTONE="$2" shift 2 ;; @@ -144,7 +144,9 @@ case "$PLATFORM" in [[ -n "$BODY" ]] && CMD+=(--body "$BODY") [[ -n "$LABELS" ]] && CMD+=(--label "$LABELS") [[ -n "$MILESTONE" ]] && CMD+=(--milestone "$MILESTONE") - "${CMD[@]}" + prov_rc=0 + "${CMD[@]}" || prov_rc=$? + [[ "$prov_rc" -eq 0 ]] || { echo "Error: provider command failed (exit ${prov_rc}; provider failure, not a usage error)" >&2; exit 1; } ;; gitea) if command -v tea >/dev/null 2>&1; then diff --git a/packages/mosaic/framework/tools/git/issue-edit.sh b/packages/mosaic/framework/tools/git/issue-edit.sh index 94ae9d9d..f40a2f34 100755 --- a/packages/mosaic/framework/tools/git/issue-edit.sh +++ b/packages/mosaic/framework/tools/git/issue-edit.sh @@ -26,27 +26,27 @@ usage_error() { while [[ $# -gt 0 ]]; do case $1 in -i|--issue) - [[ $# -ge 2 ]] || usage_error "option $1 requires a value" + [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)" ISSUE_NUMBER="$2" shift 2 ;; -t|--title) - [[ $# -ge 2 ]] || usage_error "option $1 requires a value" + [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)" TITLE="$2" shift 2 ;; -b|--body) - [[ $# -ge 2 ]] || usage_error "option $1 requires a value" + [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)" BODY="$2" shift 2 ;; -l|--labels) - [[ $# -ge 2 ]] || usage_error "option $1 requires a value" + [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)" LABELS="$2" shift 2 ;; -m|--milestone) - [[ $# -ge 2 ]] || usage_error "option $1 requires a value" + [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)" MILESTONE="$2" shift 2 ;; @@ -82,7 +82,9 @@ if [[ "$PLATFORM" == "github" ]]; then [[ -n "$BODY" ]] && CMD+=(--body "$BODY") [[ -n "$LABELS" ]] && CMD+=(--add-label "$LABELS") [[ -n "$MILESTONE" ]] && CMD+=(--milestone "$MILESTONE") - "${CMD[@]}" + prov_rc=0 + "${CMD[@]}" || prov_rc=$? + [[ "$prov_rc" -eq 0 ]] || { echo "Error: provider command failed (exit ${prov_rc}; provider failure, not a usage error)" >&2; exit 1; } echo "Updated GitHub issue #$ISSUE_NUMBER" elif [[ "$PLATFORM" == "gitea" ]]; then REPO_SLUG=$(get_repo_slug) || { @@ -98,7 +100,9 @@ elif [[ "$PLATFORM" == "gitea" ]]; then [[ -n "$BODY" ]] && CMD+=(--description "$BODY") [[ -n "$LABELS" ]] && CMD+=(--add-labels "$LABELS") [[ -n "$MILESTONE" ]] && CMD+=(--milestone "$MILESTONE") - "${CMD[@]}" + prov_rc=0 + "${CMD[@]}" || prov_rc=$? + [[ "$prov_rc" -eq 0 ]] || { echo "Error: provider command failed (exit ${prov_rc}; provider failure, not a usage error)" >&2; exit 1; } echo "Updated Gitea issue #$ISSUE_NUMBER" else echo "Error: Unknown platform" diff --git a/packages/mosaic/framework/tools/git/issue-list.sh b/packages/mosaic/framework/tools/git/issue-list.sh index 2c905d49..534c52bb 100755 --- a/packages/mosaic/framework/tools/git/issue-list.sh +++ b/packages/mosaic/framework/tools/git/issue-list.sh @@ -50,32 +50,32 @@ usage_error() { while [[ $# -gt 0 ]]; do case $1 in -s|--state) - [[ $# -ge 2 ]] || usage_error "option $1 requires a value" + [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)" STATE="$2" shift 2 ;; -l|--label) - [[ $# -ge 2 ]] || usage_error "option $1 requires a value" + [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)" LABEL="$2" shift 2 ;; -m|--milestone) - [[ $# -ge 2 ]] || usage_error "option $1 requires a value" + [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)" MILESTONE="$2" shift 2 ;; -a|--assignee) - [[ $# -ge 2 ]] || usage_error "option $1 requires a value" + [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)" ASSIGNEE="$2" shift 2 ;; -n|--limit) - [[ $# -ge 2 ]] || usage_error "option $1 requires a value" + [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)" LIMIT="$2" shift 2 ;; -r|--repo) - [[ $# -ge 2 ]] || usage_error "option $1 requires a value" + [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)" REPO_OVERRIDE="$2" shift 2 ;; @@ -108,7 +108,9 @@ case "$PLATFORM" in [[ -n "$LABEL" ]] && CMD+=(--label "$LABEL") [[ -n "$MILESTONE" ]] && CMD+=(--milestone "$MILESTONE") [[ -n "$ASSIGNEE" ]] && CMD+=(--assignee "$ASSIGNEE") - "${CMD[@]}" + prov_rc=0 + "${CMD[@]}" || prov_rc=$? + [[ "$prov_rc" -eq 0 ]] || { echo "Error: provider command failed (exit ${prov_rc}; provider failure, not a usage error)" >&2; exit 1; } ;; gitea) if [[ -n "$REPO_OVERRIDE" ]]; then @@ -127,7 +129,9 @@ case "$PLATFORM" in [[ -n "$MILESTONE" ]] && CMD+=(--milestones "$MILESTONE") # Note: tea may not support assignee filter directly in all versions. [[ -n "$ASSIGNEE" ]] && echo "Note: Assignee filtering may require manual review for Gitea" >&2 - "${CMD[@]}" + prov_rc=0 + "${CMD[@]}" || prov_rc=$? + [[ "$prov_rc" -eq 0 ]] || { echo "Error: provider command failed (exit ${prov_rc}; provider failure, not a usage error)" >&2; exit 1; } ;; *) echo "Error: Could not detect git platform" >&2 diff --git a/packages/mosaic/framework/tools/git/issue-reopen.sh b/packages/mosaic/framework/tools/git/issue-reopen.sh index b686f370..58e80382 100755 --- a/packages/mosaic/framework/tools/git/issue-reopen.sh +++ b/packages/mosaic/framework/tools/git/issue-reopen.sh @@ -24,14 +24,14 @@ usage_error() { while [[ $# -gt 0 ]]; do case $1 in -i|--issue) - [[ $# -ge 2 ]] || usage_error "option $1 requires a value" + [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)" ISSUE_NUMBER="$2" shift 2 ;; -b|--body|-c|--comment) # R1 (2026-08-28): --body is the canonical flag; -c/--comment stays # a backward-compatible alias. - [[ $# -ge 2 ]] || usage_error "option $1 requires a value" + [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)" COMMENT="$2" shift 2 ;; @@ -116,9 +116,13 @@ elif [[ "$PLATFORM" == "gitea" ]]; then REPO_ARGS=$(get_gitea_repo_args || true) if [[ -n "$REPO_ARGS" ]]; then if [[ -n "$COMMENT" ]]; then - tea issue comment "$ISSUE_NUMBER" "$COMMENT" $REPO_ARGS + prov_rc=0 + tea issue comment "$ISSUE_NUMBER" "$COMMENT" $REPO_ARGS || prov_rc=$? + [[ "$prov_rc" -eq 0 ]] || { echo "Error: provider command failed (exit ${prov_rc}; provider failure, not a usage error)" >&2; exit 1; } fi - tea issue reopen "$ISSUE_NUMBER" $REPO_ARGS + prov_rc=0 + tea issue reopen "$ISSUE_NUMBER" $REPO_ARGS || prov_rc=$? + [[ "$prov_rc" -eq 0 ]] || { echo "Error: provider command failed (exit ${prov_rc}; provider failure, not a usage error)" >&2; exit 1; } else echo "No tea login configured for $(get_remote_host); using authenticated Gitea API fallback." >&2 if [[ -n "$COMMENT" ]]; then diff --git a/packages/mosaic/framework/tools/git/issue-view.sh b/packages/mosaic/framework/tools/git/issue-view.sh index 1de25ad6..4d45ce87 100755 --- a/packages/mosaic/framework/tools/git/issue-view.sh +++ b/packages/mosaic/framework/tools/git/issue-view.sh @@ -82,7 +82,7 @@ if comments: while [[ $# -gt 0 ]]; do case $1 in -i|--issue) - [[ $# -ge 2 ]] || usage_error "option $1 requires a value" + [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)" ISSUE_NUMBER="$2" shift 2 ;; @@ -109,7 +109,9 @@ fi detect_platform >/dev/null if [[ "$PLATFORM" == "github" ]]; then - gh issue view "$ISSUE_NUMBER" + prov_rc=0 + gh issue view "$ISSUE_NUMBER" || prov_rc=$? + [[ "$prov_rc" -eq 0 ]] || { echo "Error: provider command failed (exit ${prov_rc}; provider failure, not a usage error)" >&2; exit 1; } elif [[ "$PLATFORM" == "gitea" ]]; then if command -v tea >/dev/null 2>&1; then # --comments is what makes tea print the comment bodies (#1357 F3). diff --git a/packages/mosaic/framework/tools/git/lane-brief.sh b/packages/mosaic/framework/tools/git/lane-brief.sh index 3201ab3d..5d1df846 100755 --- a/packages/mosaic/framework/tools/git/lane-brief.sh +++ b/packages/mosaic/framework/tools/git/lane-brief.sh @@ -37,11 +37,11 @@ usage_error() { } while [[ $# -gt 0 ]]; do case "$1" in - -r|--repo) [[ $# -ge 2 ]] || usage_error "option $1 requires a value"; REPO="$2"; shift 2 ;; - -m|--milestone) [[ $# -ge 2 ]] || usage_error "option $1 requires a value"; MILESTONE="$2"; shift 2 ;; - -l|--label|--labels) [[ $# -ge 2 ]] || usage_error "option $1 requires a value"; LABEL="$2"; shift 2 ;; - -L|--login) [[ $# -ge 2 ]] || usage_error "option $1 requires a value"; LOGIN="$2"; shift 2 ;; - -n|--limit) [[ $# -ge 2 ]] || usage_error "option $1 requires a value"; LIMIT="$2"; shift 2 ;; + -r|--repo) [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"; REPO="$2"; shift 2 ;; + -m|--milestone) [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"; MILESTONE="$2"; shift 2 ;; + -l|--label|--labels) [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"; LABEL="$2"; shift 2 ;; + -L|--login) [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"; LOGIN="$2"; shift 2 ;; + -n|--limit) [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"; LIMIT="$2"; shift 2 ;; -h|--help) grep '^#' "$0" | sed 's/^# \?//'; exit 0 ;; *) usage_error "unknown option: $1" ;; esac diff --git a/packages/mosaic/framework/tools/git/milestone-close.sh b/packages/mosaic/framework/tools/git/milestone-close.sh index fc541db9..92162bb8 100755 --- a/packages/mosaic/framework/tools/git/milestone-close.sh +++ b/packages/mosaic/framework/tools/git/milestone-close.sh @@ -21,7 +21,7 @@ TITLE="" while [[ $# -gt 0 ]]; do case $1 in -t|--title) - [[ $# -ge 2 ]] || usage_error "option $1 requires a value" + [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)" TITLE="$2" shift 2 ;; @@ -46,14 +46,18 @@ fi detect_platform >/dev/null if [[ "$PLATFORM" == "github" ]]; then - gh api -X PATCH "/repos/{owner}/{repo}/milestones/$(gh api "/repos/{owner}/{repo}/milestones" --jq ".[] | select(.title==\"$TITLE\") | .number")" -f state=closed + prov_rc=0 + gh api -X PATCH "/repos/{owner}/{repo}/milestones/$(gh api "/repos/{owner}/{repo}/milestones" --jq ".[] | select(.title==\"$TITLE\") | .number")" -f state=closed || prov_rc=$? + [[ "$prov_rc" -eq 0 ]] || { echo "Error: provider command failed (exit ${prov_rc}; provider failure, not a usage error)" >&2; exit 1; } echo "Closed GitHub milestone: $TITLE" elif [[ "$PLATFORM" == "gitea" ]]; then REPO_ARGS=$(get_gitea_repo_args) || { echo "Error: Could not resolve Gitea repo/login for remote host" >&2 exit 1 } - tea milestone close "$TITLE" $REPO_ARGS + prov_rc=0 + tea milestone close "$TITLE" $REPO_ARGS || prov_rc=$? + [[ "$prov_rc" -eq 0 ]] || { echo "Error: provider command failed (exit ${prov_rc}; provider failure, not a usage error)" >&2; exit 1; } echo "Closed Gitea milestone: $TITLE" else echo "Error: Unknown platform" diff --git a/packages/mosaic/framework/tools/git/milestone-create.sh b/packages/mosaic/framework/tools/git/milestone-create.sh index b063e1fe..35361c0f 100755 --- a/packages/mosaic/framework/tools/git/milestone-create.sh +++ b/packages/mosaic/framework/tools/git/milestone-create.sh @@ -51,17 +51,17 @@ usage_error() { while [[ $# -gt 0 ]]; do case $1 in -t|--title) - [[ $# -ge 2 ]] || usage_error "option $1 requires a value" + [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)" TITLE="$2" shift 2 ;; -d|--desc) - [[ $# -ge 2 ]] || usage_error "option $1 requires a value" + [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)" DESCRIPTION="$2" shift 2 ;; --due) - [[ $# -ge 2 ]] || usage_error "option $1 requires a value" + [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)" DUE_DATE="$2" shift 2 ;; @@ -84,14 +84,18 @@ PLATFORM=$(detect_platform) if [[ "$LIST_ONLY" == true ]]; then case "$PLATFORM" in github) - gh api repos/:owner/:repo/milestones --jq '.[] | "\(.number)\t\(.title)\t\(.state)\t\(.open_issues)/\(.closed_issues) issues"' + prov_rc=0 + gh api repos/:owner/:repo/milestones --jq '.[] | "\(.number)\t\(.title)\t\(.state)\t\(.open_issues)/\(.closed_issues) issues"' || prov_rc=$? + [[ "$prov_rc" -eq 0 ]] || { echo "Error: provider command failed (exit ${prov_rc}; provider failure, not a usage error)" >&2; exit 1; } ;; gitea) REPO_ARGS=$(get_gitea_repo_args) || { echo "Error: Could not resolve Gitea repo/login for remote host" >&2 exit 1 } - tea milestones list $REPO_ARGS + prov_rc=0 + tea milestones list $REPO_ARGS || prov_rc=$? + [[ "$prov_rc" -eq 0 ]] || { echo "Error: provider command failed (exit ${prov_rc}; provider failure, not a usage error)" >&2; exit 1; } ;; *) echo "Error: Could not detect git platform" >&2 @@ -118,7 +122,9 @@ case "$PLATFORM" in + (if $d != "" then {"description": $d} else {} end) + (if $due != "" then {"due_on": ($due + "T00:00:00Z")} else {} end)') - gh api repos/:owner/:repo/milestones --method POST --input - <<< "$JSON_PAYLOAD" + prov_rc=0 + gh api repos/:owner/:repo/milestones --method POST --input - <<< "$JSON_PAYLOAD" || prov_rc=$? + [[ "$prov_rc" -eq 0 ]] || { echo "Error: provider command failed (exit ${prov_rc}; provider failure, not a usage error)" >&2; exit 1; } echo "Milestone '$TITLE' created successfully" ;; gitea) @@ -129,7 +135,9 @@ case "$PLATFORM" in CMD=(tea milestones create --title "$TITLE") [[ -n "$DESCRIPTION" ]] && CMD+=(--description "$DESCRIPTION") [[ -n "$DUE_DATE" ]] && CMD+=(--deadline "$DUE_DATE") - "${CMD[@]}" $REPO_ARGS + prov_rc=0 + "${CMD[@]}" $REPO_ARGS || prov_rc=$? + [[ "$prov_rc" -eq 0 ]] || { echo "Error: provider command failed (exit ${prov_rc}; provider failure, not a usage error)" >&2; exit 1; } echo "Milestone '$TITLE' created successfully" ;; *) diff --git a/packages/mosaic/framework/tools/git/milestone-list.sh b/packages/mosaic/framework/tools/git/milestone-list.sh index 01f6b761..64beff8e 100755 --- a/packages/mosaic/framework/tools/git/milestone-list.sh +++ b/packages/mosaic/framework/tools/git/milestone-list.sh @@ -21,7 +21,7 @@ STATE="open" while [[ $# -gt 0 ]]; do case $1 in -s|--state) - [[ $# -ge 2 ]] || usage_error "option $1 requires a value" + [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)" STATE="$2" shift 2 ;; @@ -42,13 +42,17 @@ done detect_platform >/dev/null if [[ "$PLATFORM" == "github" ]]; then - gh api "/repos/{owner}/{repo}/milestones?state=$STATE" --jq '.[] | "\(.title) (\(.state)) - \(.open_issues) open, \(.closed_issues) closed"' + prov_rc=0 + gh api "/repos/{owner}/{repo}/milestones?state=$STATE" --jq '.[] | "\(.title) (\(.state)) - \(.open_issues) open, \(.closed_issues) closed"' || prov_rc=$? + [[ "$prov_rc" -eq 0 ]] || { echo "Error: provider command failed (exit ${prov_rc}; provider failure, not a usage error)" >&2; exit 1; } elif [[ "$PLATFORM" == "gitea" ]]; then REPO_ARGS=$(get_gitea_repo_args) || { echo "Error: Could not resolve Gitea repo/login for remote host" >&2 exit 1 } - tea milestone list $REPO_ARGS + prov_rc=0 + tea milestone list $REPO_ARGS || prov_rc=$? + [[ "$prov_rc" -eq 0 ]] || { echo "Error: provider command failed (exit ${prov_rc}; provider failure, not a usage error)" >&2; exit 1; } else echo "Error: Unknown platform" exit 1 diff --git a/packages/mosaic/framework/tools/git/pr-close.sh b/packages/mosaic/framework/tools/git/pr-close.sh index e2a75c14..f05a803f 100755 --- a/packages/mosaic/framework/tools/git/pr-close.sh +++ b/packages/mosaic/framework/tools/git/pr-close.sh @@ -24,14 +24,14 @@ usage_error() { while [[ $# -gt 0 ]]; do case $1 in -n|--number) - [[ $# -ge 2 ]] || usage_error "option $1 requires a value" + [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)" PR_NUMBER="$2" shift 2 ;; -b|--body|-c|--comment) # R1 (2026-08-28): --body is the canonical flag; -c/--comment stays # a backward-compatible alias. - [[ $# -ge 2 ]] || usage_error "option $1 requires a value" + [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)" COMMENT="$2" shift 2 ;; @@ -79,9 +79,13 @@ if [[ "$PLATFORM" == "github" ]]; then echo "Closed GitHub PR #$PR_NUMBER" elif [[ "$PLATFORM" == "gitea" ]]; then if [[ -n "$COMMENT" ]]; then - tea pr comment "$PR_NUMBER" "$COMMENT" $(get_gitea_repo_args) + prov_rc=0 + tea pr comment "$PR_NUMBER" "$COMMENT" $(get_gitea_repo_args) || prov_rc=$? + [[ "$prov_rc" -eq 0 ]] || { echo "Error: provider command failed (exit ${prov_rc}; provider failure, not a usage error)" >&2; exit 1; } fi - tea pr close "$PR_NUMBER" $(get_gitea_repo_args) + prov_rc=0 + tea pr close "$PR_NUMBER" $(get_gitea_repo_args) || prov_rc=$? + [[ "$prov_rc" -eq 0 ]] || { echo "Error: provider command failed (exit ${prov_rc}; provider failure, not a usage error)" >&2; exit 1; } echo "Closed Gitea PR #$PR_NUMBER" else echo "Error: Unknown platform" diff --git a/packages/mosaic/framework/tools/git/pr-create.sh b/packages/mosaic/framework/tools/git/pr-create.sh index a5bf6c7b..6f599c0f 100755 --- a/packages/mosaic/framework/tools/git/pr-create.sh +++ b/packages/mosaic/framework/tools/git/pr-create.sh @@ -149,37 +149,37 @@ usage_error() { while [[ $# -gt 0 ]]; do case $1 in -t|--title) - [[ $# -ge 2 ]] || usage_error "option $1 requires a value" + [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)" TITLE="$2" shift 2 ;; -b|--body) - [[ $# -ge 2 ]] || usage_error "option $1 requires a value" + [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)" BODY="$2" shift 2 ;; -B|--base) - [[ $# -ge 2 ]] || usage_error "option $1 requires a value" + [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)" BASE_BRANCH="$2" shift 2 ;; -H|--head) - [[ $# -ge 2 ]] || usage_error "option $1 requires a value" + [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)" HEAD_BRANCH="$2" shift 2 ;; -l|--labels) - [[ $# -ge 2 ]] || usage_error "option $1 requires a value" + [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)" LABELS="$2" shift 2 ;; -m|--milestone) - [[ $# -ge 2 ]] || usage_error "option $1 requires a value" + [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)" MILESTONE="$2" shift 2 ;; -i|--issue) - [[ $# -ge 2 ]] || usage_error "option $1 requires a value" + [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)" ISSUE="$2" shift 2 ;; @@ -280,7 +280,9 @@ case "$PLATFORM" in [[ -n "$LABELS" ]] && CMD+=(--label "$LABELS") [[ -n "$MILESTONE" ]] && CMD+=(--milestone "$MILESTONE") [[ "$DRAFT" == true ]] && CMD+=(--draft) - "${CMD[@]}" + prov_rc=0 + "${CMD[@]}" || prov_rc=$? + [[ "$prov_rc" -eq 0 ]] || { echo "Error: provider command failed (exit ${prov_rc}; provider failure, not a usage error)" >&2; exit 1; } ;; gitea) # tea pull create syntax. Always pass --repo because tea repo inference diff --git a/packages/mosaic/framework/tools/git/pr-edit.sh b/packages/mosaic/framework/tools/git/pr-edit.sh index f5db9fb4..ac167865 100755 --- a/packages/mosaic/framework/tools/git/pr-edit.sh +++ b/packages/mosaic/framework/tools/git/pr-edit.sh @@ -62,19 +62,19 @@ usage_error() { while [[ $# -gt 0 ]]; do case "$1" in - -n|--number) [[ $# -ge 2 ]] || usage_error "option $1 requires a value"; PR_NUMBER="${2:-}"; shift 2 ;; - -t|--title) [[ $# -ge 2 ]] || usage_error "option $1 requires a value"; TITLE="${2:-}"; shift 2 ;; - -b|--body) [[ $# -ge 2 ]] || usage_error "option $1 requires a value"; BODY="${2:-}"; shift 2 ;; - -B|--base) [[ $# -ge 2 ]] || usage_error "option $1 requires a value"; BASE_BRANCH="${2:-}"; shift 2 ;; + -n|--number) [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"; PR_NUMBER="${2:-}"; shift 2 ;; + -t|--title) [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"; TITLE="${2:-}"; shift 2 ;; + -b|--body) [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"; BODY="${2:-}"; shift 2 ;; + -B|--base) [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"; BASE_BRANCH="${2:-}"; shift 2 ;; --draft) [[ "$DRAFT_MODE" != "ready" ]] || { echo "Error: --draft and --ready are mutually exclusive" >&2; exit 2; } DRAFT_MODE="draft"; shift ;; --ready) [[ "$DRAFT_MODE" != "draft" ]] || { echo "Error: --draft and --ready are mutually exclusive" >&2; exit 2; } DRAFT_MODE="ready"; shift ;; - -l|--login) [[ $# -ge 2 ]] || usage_error "option $1 requires a value"; LOGIN_OVERRIDE="${2:-}"; shift 2 ;; - -r|--repo) [[ $# -ge 2 ]] || usage_error "option $1 requires a value"; REPO_OVERRIDE="${2:-}"; shift 2 ;; - -H|--host) [[ $# -ge 2 ]] || usage_error "option $1 requires a value"; HOST_OVERRIDE="${2:-}"; shift 2 ;; + -l|--login) [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"; LOGIN_OVERRIDE="${2:-}"; shift 2 ;; + -r|--repo) [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"; REPO_OVERRIDE="${2:-}"; shift 2 ;; + -H|--host) [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)"; HOST_OVERRIDE="${2:-}"; shift 2 ;; -h|--help) usage 0 ;; *) echo "Unknown option: $1" >&2; usage ;; esac @@ -105,12 +105,18 @@ case "$PLATFORM" in [[ -n "$TITLE" ]] && CMD+=(--title "$TITLE") [[ -n "$BODY" ]] && CMD+=(--body "$BODY") [[ -n "$BASE_BRANCH" ]] && CMD+=(--base "$BASE_BRANCH") - "${CMD[@]}" + prov_rc=0 + "${CMD[@]}" || prov_rc=$? + [[ "$prov_rc" -eq 0 ]] || { echo "Error: provider command failed (exit ${prov_rc}; provider failure, not a usage error)" >&2; exit 1; } fi if [[ "$DRAFT_MODE" == "draft" ]]; then - gh pr ready "$PR_NUMBER" --undo + prov_rc=0 + gh pr ready "$PR_NUMBER" --undo || prov_rc=$? + [[ "$prov_rc" -eq 0 ]] || { echo "Error: provider command failed (exit ${prov_rc}; provider failure, not a usage error)" >&2; exit 1; } elif [[ "$DRAFT_MODE" == "ready" ]]; then - gh pr ready "$PR_NUMBER" + prov_rc=0 + gh pr ready "$PR_NUMBER" || prov_rc=$? + [[ "$prov_rc" -eq 0 ]] || { echo "Error: provider command failed (exit ${prov_rc}; provider failure, not a usage error)" >&2; exit 1; } fi ;; gitea) diff --git a/packages/mosaic/framework/tools/git/pr-review.sh b/packages/mosaic/framework/tools/git/pr-review.sh index c5e9028b..716e3fe7 100755 --- a/packages/mosaic/framework/tools/git/pr-review.sh +++ b/packages/mosaic/framework/tools/git/pr-review.sh @@ -55,34 +55,34 @@ usage_error() { while [[ $# -gt 0 ]]; do case $1 in -n|--number) - [[ $# -ge 2 ]] || usage_error "option $1 requires a value" + [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)" PR_NUMBER="$2" shift 2 ;; -a|--action) - [[ $# -ge 2 ]] || usage_error "option $1 requires a value" + [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)" ACTION="$2" shift 2 ;; -b|--body|-c|--comment) # R1 (2026-08-28): --body is the canonical flag; -c/--comment stays # a backward-compatible alias. - [[ $# -ge 2 ]] || usage_error "option $1 requires a value" + [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)" COMMENT="$2" shift 2 ;; -l|--login) - [[ $# -ge 2 ]] || usage_error "option $1 requires a value" + [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)" LOGIN_OVERRIDE="$2" shift 2 ;; -r|--repo) - [[ $# -ge 2 ]] || usage_error "option $1 requires a value" + [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)" REPO_OVERRIDE="$2" shift 2 ;; -H|--host) - [[ $# -ge 2 ]] || usage_error "option $1 requires a value" + [[ $# -ge 2 && "$2" != --* && ! "$2" =~ ^-[a-zA-Z]$ ]] || usage_error "option $1 requires a value (option-like values are rejected)" HOST_OVERRIDE="$2" shift 2 ;; @@ -711,14 +711,18 @@ PY if [[ "$PLATFORM" == "github" ]]; then case $ACTION in approve) - gh pr review "$PR_NUMBER" --approve ${COMMENT:+--body "$COMMENT"} + gh_rc=0 + gh pr review "$PR_NUMBER" --approve ${COMMENT:+--body "$COMMENT"} || gh_rc=$? + [[ "$gh_rc" -eq 0 ]] || { echo "Error: GitHub approve failed (gh exit $gh_rc; provider failure, not a usage error)" >&2; exit 1; } echo "Approved GitHub PR #$PR_NUMBER" ;; request-changes) if [[ -z "$COMMENT" ]]; then usage_error "comment required for request-changes (-b/--body)" fi - gh pr review "$PR_NUMBER" --request-changes --body "$COMMENT" + gh_rc=0 + gh pr review "$PR_NUMBER" --request-changes --body "$COMMENT" || gh_rc=$? + [[ "$gh_rc" -eq 0 ]] || { echo "Error: GitHub request-changes failed (gh exit $gh_rc; provider failure, not a usage error)" >&2; exit 1; } echo "Requested changes on GitHub PR #$PR_NUMBER" ;; comment) @@ -726,7 +730,9 @@ if [[ "$PLATFORM" == "github" ]]; then echo "Error: Comment required" exit 1 fi - gh pr review "$PR_NUMBER" --comment --body "$COMMENT" + gh_rc=0 + gh pr review "$PR_NUMBER" --comment --body "$COMMENT" || gh_rc=$? + [[ "$gh_rc" -eq 0 ]] || { echo "Error: GitHub review comment failed (gh exit $gh_rc; provider failure, not a usage error)" >&2; exit 1; } echo "Added review comment to GitHub PR #$PR_NUMBER" ;; *) diff --git a/packages/mosaic/framework/tools/git/test-issue-assign-usage-contract.sh b/packages/mosaic/framework/tools/git/test-issue-assign-usage-contract.sh index c7e537a6..8eb35e0a 100755 --- a/packages/mosaic/framework/tools/git/test-issue-assign-usage-contract.sh +++ b/packages/mosaic/framework/tools/git/test-issue-assign-usage-contract.sh @@ -99,6 +99,13 @@ for flag in -i -a -l -m --issue --assignee --labels --milestone; do expect_stderr "requires a value" "value-less $flag message on stderr" done +# 4a. An option-like value is a MISSING value, not a value (codex PR #1464: +# -b --help previously consumed --help as the body and performed the write). +expect_rc 2 "option-like value rejected" -i 5 -a --help +expect_rc 2 "short flag value rejected" -i 5 -a -h +expect_stderr "requires a value" "short flag value message on stderr" +expect_stderr "requires a value" "option-like value message on stderr" + # 4b. Parser-failure arms (1-4) must have performed ZERO provider contact. if [[ -s "$PROBE_LOG" ]]; then echo "FAIL: a parser-failure arm contacted a provider:" >&2 diff --git a/packages/mosaic/framework/tools/git/test-issue-close-usage-contract.sh b/packages/mosaic/framework/tools/git/test-issue-close-usage-contract.sh index 9dfba9f0..b0ef6ae4 100755 --- a/packages/mosaic/framework/tools/git/test-issue-close-usage-contract.sh +++ b/packages/mosaic/framework/tools/git/test-issue-close-usage-contract.sh @@ -97,6 +97,13 @@ for flag in -i -b -c --issue --body --comment; do expect_stderr "requires a value" "value-less $flag message on stderr" done +# 4a. An option-like value is a MISSING value, not a value (codex PR #1464: +# -b --help previously consumed --help as the body and performed the write). +expect_rc 2 "option-like value rejected" -i 5 -b --help +expect_rc 2 "short flag value rejected" -i 5 -b -h +expect_stderr "requires a value" "short flag value message on stderr" +expect_stderr "requires a value" "option-like value message on stderr" + # 4b. Parser-failure arms (1-4) must have performed ZERO provider contact. if [[ -s "$PROBE_LOG" ]]; then echo "FAIL: a parser-failure arm contacted a provider:" >&2 diff --git a/packages/mosaic/framework/tools/git/test-issue-comment-usage-contract.sh b/packages/mosaic/framework/tools/git/test-issue-comment-usage-contract.sh index e525357a..d80a89a4 100755 --- a/packages/mosaic/framework/tools/git/test-issue-comment-usage-contract.sh +++ b/packages/mosaic/framework/tools/git/test-issue-comment-usage-contract.sh @@ -124,6 +124,13 @@ for flag in -i -b -c -l --issue --body --comment --login; do expect_stderr "requires a value" "value-less $flag message on stderr" done +# 4a. An option-like value is a MISSING value, not a value (codex PR #1464: +# -b --help previously consumed --help as the body and performed the write). +expect_rc 2 "option-like value rejected" -i 5 -b --help +expect_rc 2 "short flag value rejected" -i 5 -b -h +expect_stderr "requires a value" "short flag value message on stderr" +expect_stderr "requires a value" "option-like value message on stderr" + # 6. Alias acceptance at parse level: both -b and -c carry a value past # parsing; the wrapper then fails at platform detection (not a git repo) # nonzero but NOT as a usage error (rc must not be 2). diff --git a/packages/mosaic/framework/tools/git/test-issue-create-usage-contract.sh b/packages/mosaic/framework/tools/git/test-issue-create-usage-contract.sh index 850487a5..1cdbfc7a 100755 --- a/packages/mosaic/framework/tools/git/test-issue-create-usage-contract.sh +++ b/packages/mosaic/framework/tools/git/test-issue-create-usage-contract.sh @@ -99,6 +99,13 @@ for flag in -t -b -l -m --title --body --labels --milestone; do expect_stderr "requires a value" "value-less $flag message on stderr" done +# 4a. An option-like value is a MISSING value, not a value (codex PR #1464: +# -b --help previously consumed --help as the body and performed the write). +expect_rc 2 "option-like value rejected" -t smoke -b --help +expect_rc 2 "short flag value rejected" -t smoke -b -h +expect_stderr "requires a value" "short flag value message on stderr" +expect_stderr "requires a value" "option-like value message on stderr" + # 4b. Parser-failure arms (1-4) must have performed ZERO provider contact. if [[ -s "$PROBE_LOG" ]]; then echo "FAIL: a parser-failure arm contacted a provider:" >&2 diff --git a/packages/mosaic/framework/tools/git/test-issue-edit-usage-contract.sh b/packages/mosaic/framework/tools/git/test-issue-edit-usage-contract.sh index 48334bd5..0f939f9d 100755 --- a/packages/mosaic/framework/tools/git/test-issue-edit-usage-contract.sh +++ b/packages/mosaic/framework/tools/git/test-issue-edit-usage-contract.sh @@ -99,6 +99,13 @@ for flag in -i -t -b -l -m --issue --title --body --labels --milestone; do expect_stderr "requires a value" "value-less $flag message on stderr" done +# 4a. An option-like value is a MISSING value, not a value (codex PR #1464: +# -b --help previously consumed --help as the body and performed the write). +expect_rc 2 "option-like value rejected" -i 5 -b --help +expect_rc 2 "short flag value rejected" -i 5 -b -h +expect_stderr "requires a value" "short flag value message on stderr" +expect_stderr "requires a value" "option-like value message on stderr" + # 4b. Parser-failure arms (1-4) must have performed ZERO provider contact. if [[ -s "$PROBE_LOG" ]]; then echo "FAIL: a parser-failure arm contacted a provider:" >&2 diff --git a/packages/mosaic/framework/tools/git/test-issue-list-usage-contract.sh b/packages/mosaic/framework/tools/git/test-issue-list-usage-contract.sh index 88870bbd..75aaae46 100755 --- a/packages/mosaic/framework/tools/git/test-issue-list-usage-contract.sh +++ b/packages/mosaic/framework/tools/git/test-issue-list-usage-contract.sh @@ -97,6 +97,13 @@ for flag in -s -l -m -a -n -r --state --label --milestone --assignee --limit --r expect_stderr "requires a value" "value-less $flag message on stderr" done +# 4a. An option-like value is a MISSING value, not a value (codex PR #1464: +# -b --help previously consumed --help as the body and performed the write). +expect_rc 2 "option-like value rejected" -s --help +expect_rc 2 "short flag value rejected" -s -h +expect_stderr "requires a value" "short flag value message on stderr" +expect_stderr "requires a value" "option-like value message on stderr" + # 4b. Parser-failure arms (1-4) must have performed ZERO provider contact. if [[ -s "$PROBE_LOG" ]]; then echo "FAIL: a parser-failure arm contacted a provider:" >&2 diff --git a/packages/mosaic/framework/tools/git/test-issue-reopen-usage-contract.sh b/packages/mosaic/framework/tools/git/test-issue-reopen-usage-contract.sh index f60b004d..ff7e7343 100755 --- a/packages/mosaic/framework/tools/git/test-issue-reopen-usage-contract.sh +++ b/packages/mosaic/framework/tools/git/test-issue-reopen-usage-contract.sh @@ -97,6 +97,13 @@ for flag in -i -b -c --issue --body --comment; do expect_stderr "requires a value" "value-less $flag message on stderr" done +# 4a. An option-like value is a MISSING value, not a value (codex PR #1464: +# -b --help previously consumed --help as the body and performed the write). +expect_rc 2 "option-like value rejected" -i 5 -b --help +expect_rc 2 "short flag value rejected" -i 5 -b -h +expect_stderr "requires a value" "short flag value message on stderr" +expect_stderr "requires a value" "option-like value message on stderr" + # 4b. Parser-failure arms (1-4) must have performed ZERO provider contact. if [[ -s "$PROBE_LOG" ]]; then echo "FAIL: a parser-failure arm contacted a provider:" >&2 diff --git a/packages/mosaic/framework/tools/git/test-issue-view-usage-contract.sh b/packages/mosaic/framework/tools/git/test-issue-view-usage-contract.sh index 09170fb9..d2ff9182 100755 --- a/packages/mosaic/framework/tools/git/test-issue-view-usage-contract.sh +++ b/packages/mosaic/framework/tools/git/test-issue-view-usage-contract.sh @@ -99,6 +99,13 @@ for flag in -i --issue; do expect_stderr "requires a value" "value-less $flag message on stderr" done +# 4a. An option-like value is a MISSING value, not a value (codex PR #1464: +# -b --help previously consumed --help as the body and performed the write). +expect_rc 2 "option-like value rejected" -i --help +expect_rc 2 "short flag value rejected" -i -h +expect_stderr "requires a value" "short flag value message on stderr" +expect_stderr "requires a value" "option-like value message on stderr" + # 4b. Parser-failure arms (1-4) must have performed ZERO provider contact. if [[ -s "$PROBE_LOG" ]]; then echo "FAIL: a parser-failure arm contacted a provider:" >&2 diff --git a/packages/mosaic/framework/tools/git/test-lane-brief-usage-contract.sh b/packages/mosaic/framework/tools/git/test-lane-brief-usage-contract.sh index c832abec..64c08ebc 100755 --- a/packages/mosaic/framework/tools/git/test-lane-brief-usage-contract.sh +++ b/packages/mosaic/framework/tools/git/test-lane-brief-usage-contract.sh @@ -99,6 +99,13 @@ for flag in -r -m -l -L -n --repo --milestone --label --login --limit; do expect_stderr "requires a value" "value-less $flag message on stderr" done +# 4a. An option-like value is a MISSING value, not a value (codex PR #1464: +# -b --help previously consumed --help as the body and performed the write). +expect_rc 2 "option-like value rejected" -r owner/repo -m --help +expect_rc 2 "short flag value rejected" -r owner/repo -m -h +expect_stderr "requires a value" "short flag value message on stderr" +expect_stderr "requires a value" "option-like value message on stderr" + # 4b. Parser-failure arms (1-4) must have performed ZERO provider contact. if [[ -s "$PROBE_LOG" ]]; then echo "FAIL: a parser-failure arm contacted a provider:" >&2 diff --git a/packages/mosaic/framework/tools/git/test-milestone-close-usage-contract.sh b/packages/mosaic/framework/tools/git/test-milestone-close-usage-contract.sh index bd674910..c7f38771 100755 --- a/packages/mosaic/framework/tools/git/test-milestone-close-usage-contract.sh +++ b/packages/mosaic/framework/tools/git/test-milestone-close-usage-contract.sh @@ -99,6 +99,13 @@ for flag in -t --title; do expect_stderr "requires a value" "value-less $flag message on stderr" done +# 4a. An option-like value is a MISSING value, not a value (codex PR #1464: +# -b --help previously consumed --help as the body and performed the write). +expect_rc 2 "option-like value rejected" -t --help --help +expect_rc 2 "short flag value rejected" -t --help -h +expect_stderr "requires a value" "short flag value message on stderr" +expect_stderr "requires a value" "option-like value message on stderr" + # 4b. Parser-failure arms (1-4) must have performed ZERO provider contact. if [[ -s "$PROBE_LOG" ]]; then echo "FAIL: a parser-failure arm contacted a provider:" >&2 diff --git a/packages/mosaic/framework/tools/git/test-milestone-create-usage-contract.sh b/packages/mosaic/framework/tools/git/test-milestone-create-usage-contract.sh index 2e08587d..e267f9dc 100755 --- a/packages/mosaic/framework/tools/git/test-milestone-create-usage-contract.sh +++ b/packages/mosaic/framework/tools/git/test-milestone-create-usage-contract.sh @@ -99,6 +99,13 @@ for flag in -t -d --due --title --desc; do expect_stderr "requires a value" "value-less $flag message on stderr" done +# 4a. An option-like value is a MISSING value, not a value (codex PR #1464: +# -b --help previously consumed --help as the body and performed the write). +expect_rc 2 "option-like value rejected" -t smoke -d --help +expect_rc 2 "short flag value rejected" -t smoke -d -h +expect_stderr "requires a value" "short flag value message on stderr" +expect_stderr "requires a value" "option-like value message on stderr" + # 4b. Parser-failure arms (1-4) must have performed ZERO provider contact. if [[ -s "$PROBE_LOG" ]]; then echo "FAIL: a parser-failure arm contacted a provider:" >&2 diff --git a/packages/mosaic/framework/tools/git/test-milestone-list-usage-contract.sh b/packages/mosaic/framework/tools/git/test-milestone-list-usage-contract.sh index d6865b81..64b894f4 100755 --- a/packages/mosaic/framework/tools/git/test-milestone-list-usage-contract.sh +++ b/packages/mosaic/framework/tools/git/test-milestone-list-usage-contract.sh @@ -98,6 +98,13 @@ for flag in -s --state; do expect_stderr "requires a value" "value-less $flag message on stderr" done +# 4a. An option-like value is a MISSING value, not a value (codex PR #1464: +# -b --help previously consumed --help as the body and performed the write). +expect_rc 2 "option-like value rejected" -s --help +expect_rc 2 "short flag value rejected" -s -h +expect_stderr "requires a value" "short flag value message on stderr" +expect_stderr "requires a value" "option-like value message on stderr" + # 4b. Parser-failure arms (1-4) must have performed ZERO provider contact. if [[ -s "$PROBE_LOG" ]]; then echo "FAIL: a parser-failure arm contacted a provider:" >&2 diff --git a/packages/mosaic/framework/tools/git/test-pr-close-usage-contract.sh b/packages/mosaic/framework/tools/git/test-pr-close-usage-contract.sh index 734bb150..6d380e59 100755 --- a/packages/mosaic/framework/tools/git/test-pr-close-usage-contract.sh +++ b/packages/mosaic/framework/tools/git/test-pr-close-usage-contract.sh @@ -100,6 +100,13 @@ for flag in -n -b -c --number --body --comment; do expect_stderr "requires a value" "value-less $flag message on stderr" done +# 4a. An option-like value is a MISSING value, not a value (codex PR #1464: +# -b --help previously consumed --help as the body and performed the write). +expect_rc 2 "option-like value rejected" -n 5 -b --help +expect_rc 2 "short flag value rejected" -n 5 -b -h +expect_stderr "requires a value" "short flag value message on stderr" +expect_stderr "requires a value" "option-like value message on stderr" + # 4b. Parser-failure arms (1-4) must have performed ZERO provider contact. if [[ -s "$PROBE_LOG" ]]; then echo "FAIL: a parser-failure arm contacted a provider:" >&2 diff --git a/packages/mosaic/framework/tools/git/test-pr-create-usage-contract.sh b/packages/mosaic/framework/tools/git/test-pr-create-usage-contract.sh index a7d6c952..ad23472c 100755 --- a/packages/mosaic/framework/tools/git/test-pr-create-usage-contract.sh +++ b/packages/mosaic/framework/tools/git/test-pr-create-usage-contract.sh @@ -99,6 +99,13 @@ for flag in -t -b -B -H -l -m -i --title --body --base --head --labels --milesto expect_stderr "requires a value" "value-less $flag message on stderr" done +# 4a. An option-like value is a MISSING value, not a value (codex PR #1464: +# -b --help previously consumed --help as the body and performed the write). +expect_rc 2 "option-like value rejected" -t smoke -b --help +expect_rc 2 "short flag value rejected" -t smoke -b -h +expect_stderr "requires a value" "short flag value message on stderr" +expect_stderr "requires a value" "option-like value message on stderr" + # 4b. Parser-failure arms (1-4) must have performed ZERO provider contact. if [[ -s "$PROBE_LOG" ]]; then echo "FAIL: a parser-failure arm contacted a provider:" >&2 diff --git a/packages/mosaic/framework/tools/git/test-pr-edit-usage-contract.sh b/packages/mosaic/framework/tools/git/test-pr-edit-usage-contract.sh index e816f69d..71448ca9 100755 --- a/packages/mosaic/framework/tools/git/test-pr-edit-usage-contract.sh +++ b/packages/mosaic/framework/tools/git/test-pr-edit-usage-contract.sh @@ -107,6 +107,13 @@ for flag in -n -t -b -B -l -r -H --number --title --body --base --login --repo - expect_stderr "requires a value" "value-less $flag message on stderr" done +# 4a. An option-like value is a MISSING value, not a value (codex PR #1464: +# -b --help previously consumed --help as the body and performed the write). +expect_rc 2 "option-like value rejected" -n 5 -t smoke -b --help +expect_rc 2 "short flag value rejected" -n 5 -t smoke -b -h +expect_stderr "requires a value" "short flag value message on stderr" +expect_stderr "requires a value" "option-like value message on stderr" + # 4b. Parser-failure arms (1-4) must have performed ZERO provider contact. if [[ -s "$PROBE_LOG" ]]; then echo "FAIL: a parser-failure arm contacted a provider:" >&2 diff --git a/packages/mosaic/framework/tools/git/test-pr-review-usage-contract.sh b/packages/mosaic/framework/tools/git/test-pr-review-usage-contract.sh index a66b2075..13dbf50b 100755 --- a/packages/mosaic/framework/tools/git/test-pr-review-usage-contract.sh +++ b/packages/mosaic/framework/tools/git/test-pr-review-usage-contract.sh @@ -115,6 +115,13 @@ for flag in -n -a -b -c -l -r --number --action --body --comment --login --repo; expect_stderr "requires a value" "value-less $flag message on stderr" done +# 4a. An option-like value is a MISSING value, not a value (codex PR #1464: +# -b --help previously consumed --help as the body and performed the write). +expect_rc 2 "option-like value rejected" -n 5 -a comment -b --help +expect_rc 2 "short flag value rejected" -n 5 -a comment -b -h +expect_stderr "requires a value" "short flag value message on stderr" +expect_stderr "requires a value" "option-like value message on stderr" + # 4b. Parser-failure arms (1-4) must have performed ZERO provider contact. if [[ -s "$PROBE_LOG" ]]; then echo "FAIL: a parser-failure arm contacted a provider:" >&2