#!/bin/bash # issue-edit.sh - Edit an issue on GitHub or Gitea # Usage: issue-edit.sh -i [-t ] [-b <body>] [-l <labels>] [-m <milestone>] set -e SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "$SCRIPT_DIR/detect-platform.sh" # Parse arguments ISSUE_NUMBER="" TITLE="" BODY="" LABELS="" MILESTONE="" # Usage-error contract (R4, 2026-08-28): usage errors print to STDERR and exit 2, # distinct from provider, credential, and verification failures (exit 1), so a # caller or stop gate can tell an invocation defect from a delivery blocker. usage_error() { echo "Error: $*" >&2 echo "Usage: issue-edit.sh -i <issue_number> [-t <title>] [-b <body>] [-l <labels>] [-m <milestone>] (see --help)" >&2 exit 2 } while [[ $# -gt 0 ]]; do case $1 in -i|--issue) [[ $# -ge 2 && "$2" != - && "$2" != --* && ! "$2" =~ ^-[[:alnum:]] ]] || usage_error "option $1 requires a value (option-like values are rejected; bare - is reserved)" ISSUE_NUMBER="$2" shift 2 ;; -t|--title) [[ $# -ge 2 && "$2" != - && "$2" != --* && ! "$2" =~ ^-[[:alnum:]] ]] || usage_error "option $1 requires a value (option-like values are rejected; bare - is reserved)" TITLE="$2" shift 2 ;; -b|--body) [[ $# -ge 2 && "$2" != - && "$2" != --* && ! "$2" =~ ^-[[:alnum:]] ]] || usage_error "option $1 requires a value (option-like values are rejected; bare - is reserved)" BODY="$2" shift 2 ;; -l|--labels) [[ $# -ge 2 && "$2" != - && "$2" != --* && ! "$2" =~ ^-[[:alnum:]] ]] || usage_error "option $1 requires a value (option-like values are rejected; bare - is reserved)" LABELS="$2" shift 2 ;; -m|--milestone) [[ $# -ge 2 && "$2" != - && "$2" != --* && ! "$2" =~ ^-[[:alnum:]] ]] || usage_error "option $1 requires a value (option-like values are rejected; bare - is reserved)" MILESTONE="$2" shift 2 ;; -h|--help) echo "Usage: issue-edit.sh -i <issue_number> [-t <title>] [-b <body>] [-l <labels>] [-m <milestone>]" echo "" echo "Options:" echo " -i, --issue Issue number (required)" echo " -t, --title New title" echo " -b, --body New body/description" echo " -l, --labels Labels (comma-separated, replaces existing)" echo " -m, --milestone Milestone name" echo " -h, --help Show this help" echo "" echo "Exit codes: 0 success; 2 usage error (stderr); 1 provider/credential/verification failure." exit 0 ;; *) usage_error "unknown option: $1" ;; esac done if [[ -z "$ISSUE_NUMBER" ]]; then usage_error "issue number is required (-i/--issue)" fi detect_platform >/dev/null if [[ "$PLATFORM" == "github" ]]; then CMD=(gh issue edit "$ISSUE_NUMBER") [[ -n "$TITLE" ]] && CMD+=(--title "$TITLE") [[ -n "$BODY" ]] && CMD+=(--body "$BODY") [[ -n "$LABELS" ]] && CMD+=(--add-label "$LABELS") [[ -n "$MILESTONE" ]] && CMD+=(--milestone "$MILESTONE") 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) || { echo "Error: Could not resolve Gitea repo slug from remote" >&2 exit 1 } REPO_LOGIN=$(get_gitea_login) || { echo "Error: Could not resolve Gitea login for remote host" >&2 exit 1 } CMD=(tea issue edit "$ISSUE_NUMBER" --repo "$REPO_SLUG" --login "$REPO_LOGIN") [[ -n "$TITLE" ]] && CMD+=(--title "$TITLE") [[ -n "$BODY" ]] && CMD+=(--description "$BODY") [[ -n "$LABELS" ]] && CMD+=(--add-labels "$LABELS") [[ -n "$MILESTONE" ]] && CMD+=(--milestone "$MILESTONE") 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" exit 1 fi