mirror of
https://github.com/ivuorinen/dotfiles.git
synced 2026-02-08 00:50:44 +00:00
* fix(ci): replace broad permissions with specific scopes in workflows
Replace read-all/write-all with minimum required permission scopes
across all GitHub Actions workflows to follow the principle of least
privilege (SonarCloud rule githubactions:S8234).
* fix(shell): use [[ instead of [ for conditional tests
Replace single brackets with double brackets in bash conditional
expressions across 14 files (28 changes). All scripts use bash
shebangs so [[ is safe everywhere (SonarCloud rule shelldre:S7688).
* fix(shell): add explicit return statements to functions
Add return 0 as the last statement in ~46 shell functions across
17 files that previously relied on implicit return codes
(SonarCloud rule shelldre:S7682).
* fix(shell): assign positional parameters to local variables
Replace direct $1/$2/$3 usage with named local variables in _log(),
msg(), msg_err(), msg_done(), msg_run(), msg_ok(), and array_diff()
(SonarCloud rule shelldre:S7679).
* fix(python): replace dict() constructor with literal
Use {} instead of dict() for empty dictionary initialization
(SonarCloud rule python:S7498).
* fix(shell): fix husky shebang and tolerate npm outdated exit code
* docs(shell): add function docstring comments
* fix(shell): fix heredoc indentation in x-sonarcloud
* feat(python): add ruff linter and formatter configuration
* fix(ci): align megalinter config with biome, ruff, and shfmt settings
* fix(ci): disable black and yaml-prettier in megalinter config
* chore(ci): update ruff-pre-commit to v0.15.0 and fix hook name
* fix(scripts): check for .git dir before skipping clone in install-fonts
* fix(shell): address code review issues in scripts and shared.sh
- Guard wezterm show-keys failure in create-wezterm-keymaps.sh
- Stop masking git failures with return 0 in install-cheat-purebashbible.sh
- Add missing shared.sh source in install-xcode-cli-tools.sh
- Replace exit 1 with return 1 in sourced shared.sh
* fix(scripts): address code review and security findings
- Guard wezterm show-keys failure in create-wezterm-keymaps.sh
- Stop masking git failures with return 0 in install-cheat-purebashbible.sh
- Add missing shared.sh source in install-xcode-cli-tools.sh
- Replace exit 1 with return 1 in sourced shared.sh
- Remove shell=True subprocess calls in x-git-largest-files.py
* style(shell): apply shfmt formatting and add args to pre-commit hook
* fix(python): suppress bandit false positives in x-git-largest-files
* fix(python): add nosemgrep suppression for check_output call
* feat(format): add prettier for YAML formatting
Install prettier, add .prettierrc.json config (200-char width, 2-space
indent, LF endings), .prettierignore, yarn scripts (lint:prettier,
fix:prettier, format:yaml), and pre-commit hook scoped to YAML files.
* style(yaml): apply prettier formatting
* fix(scripts): address remaining code review findings
- Python: use list comprehension to filter empty strings instead of
slicing off the last element
- create-wezterm-keymaps: write to temp file and mv for atomic updates
- install-xcode-cli-tools: fix shellcheck source directive path
* fix(python): sort imports alphabetically in x-git-largest-files
* fix(lint): disable PYTHON_ISORT in MegaLinter, ruff handles it
* chore(git): add __pycache__ to gitignore
* fix(python): rename ambiguous variable l to line (E741)
* style: remove trailing whitespace and blank lines
* style(fzf): apply shfmt formatting
* style(shell): apply shfmt formatting
* docs(plans): add design documents
* style(docs): add language specifier to fenced code block
* feat(lint): add markdown-table-formatter to dev tooling
Add markdown-table-formatter as a dev dependency with yarn scripts
(lint:md-table, fix:md-table) and a local pre-commit hook to
automatically format markdown tables on commit.
627 lines
16 KiB
Bash
Executable File
627 lines
16 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
# x-sonarcloud - Fetch SonarCloud issues for LLM analysis
|
|
# Copyright (c) 2025 - Licensed under MIT
|
|
#
|
|
# Usage:
|
|
# x-sonarcloud # Auto-detect, all open issues
|
|
# x-sonarcloud --pr <number> # PR-specific issues
|
|
# x-sonarcloud --branch <name> # Branch-specific issues
|
|
# x-sonarcloud --org <org> --project-key <key> # Explicit project
|
|
# x-sonarcloud --severities BLOCKER,CRITICAL # Filter by severity
|
|
# x-sonarcloud --types BUG,VULNERABILITY # Filter by type
|
|
# x-sonarcloud --statuses OPEN,CONFIRMED # Filter by status
|
|
# x-sonarcloud --resolved # Include resolved issues
|
|
# x-sonarcloud -h|--help # Show this help
|
|
#
|
|
# Examples:
|
|
# x-sonarcloud # All open issues in project
|
|
# x-sonarcloud --pr 42 # Issues on PR #42
|
|
# x-sonarcloud --branch main # Issues on main branch
|
|
# x-sonarcloud --severities BLOCKER --types BUG # Only blocker bugs
|
|
#
|
|
# Requirements:
|
|
# - curl and jq installed
|
|
# - SONAR_TOKEN environment variable set
|
|
# - sonar-project.properties or .sonarlint/connectedMode.json for auto-detection
|
|
|
|
set -euo pipefail
|
|
|
|
# Colors for output (stderr only)
|
|
readonly RED='\033[0;31m'
|
|
readonly GREEN='\033[0;32m'
|
|
readonly YELLOW='\033[1;33m'
|
|
readonly BLUE='\033[0;34m'
|
|
readonly NC='\033[0m' # No Color
|
|
|
|
# API constants
|
|
readonly MAX_PAGE_SIZE=500
|
|
readonly MAX_TOTAL_ISSUES=10000
|
|
|
|
# Show usage information
|
|
show_usage()
|
|
{
|
|
sed -n '3,27p' "$0" | sed 's/^# //' | sed 's/^#//'
|
|
}
|
|
|
|
# Log functions
|
|
log_error()
|
|
{
|
|
echo -e "${RED}ERROR:${NC} $1" >&2
|
|
}
|
|
# Log a warning message
|
|
log_warn()
|
|
{
|
|
echo -e "${YELLOW}WARN:${NC} $1" >&2
|
|
}
|
|
# Log an informational message
|
|
log_info()
|
|
{
|
|
if [[ "${INFO:-0}" == "1" ]]; then
|
|
echo -e "${GREEN}INFO:${NC} $1" >&2
|
|
fi
|
|
}
|
|
# Log a debug message
|
|
log_debug()
|
|
{
|
|
if [[ "${DEBUG:-0}" == "1" ]]; then
|
|
echo -e "${BLUE}DEBUG:${NC} $1" >&2
|
|
fi
|
|
}
|
|
|
|
# Check required dependencies
|
|
check_dependencies()
|
|
{
|
|
local missing=0
|
|
|
|
if ! command -v curl &> /dev/null; then
|
|
log_error "curl is not installed. Install it with your package manager."
|
|
missing=1
|
|
fi
|
|
|
|
if ! command -v jq &> /dev/null; then
|
|
log_error "jq is not installed. Install it with your package manager:"
|
|
log_error " https://jqlang.github.io/jq/download/"
|
|
missing=1
|
|
fi
|
|
|
|
if [[ "$missing" -eq 1 ]]; then
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# Check authentication
|
|
check_auth()
|
|
{
|
|
if [[ -z "${SONAR_TOKEN:-}" ]]; then
|
|
log_error "SONAR_TOKEN environment variable is not set."
|
|
log_error "Generate a token at: https://sonarcloud.io/account/security"
|
|
log_error "Then export it: export SONAR_TOKEN=your_token_here"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# Detect project from sonar-project.properties
|
|
detect_project_from_properties()
|
|
{
|
|
local props_file="sonar-project.properties"
|
|
|
|
if [[ ! -f "$props_file" ]]; then
|
|
return 1
|
|
fi
|
|
|
|
local org key
|
|
org=$(grep -E '^sonar\.organization=' "$props_file" 2> /dev/null | cut -d'=' -f2- || echo "")
|
|
key=$(grep -E '^sonar\.projectKey=' "$props_file" 2> /dev/null | cut -d'=' -f2- || echo "")
|
|
|
|
if [[ -n "$org" && -n "$key" ]]; then
|
|
log_debug "Detected from sonar-project.properties: org=$org key=$key"
|
|
echo "$org" "$key" ""
|
|
return 0
|
|
fi
|
|
|
|
return 1
|
|
}
|
|
|
|
# Detect project from .sonarlint/connectedMode.json
|
|
detect_project_from_sonarlint()
|
|
{
|
|
local sonarlint_file=".sonarlint/connectedMode.json"
|
|
|
|
if [[ ! -f "$sonarlint_file" ]]; then
|
|
return 1
|
|
fi
|
|
|
|
local org key region
|
|
org=$(jq -r '.sonarCloudOrganization // empty' "$sonarlint_file" 2> /dev/null || echo "")
|
|
key=$(jq -r '.projectKey // empty' "$sonarlint_file" 2> /dev/null || echo "")
|
|
region=$(jq -r '.region // empty' "$sonarlint_file" 2> /dev/null || echo "")
|
|
|
|
if [[ -n "$org" && -n "$key" ]]; then
|
|
log_debug "Detected from .sonarlint/connectedMode.json: org=$org key=$key region=$region"
|
|
echo "$org" "$key" "$region"
|
|
return 0
|
|
fi
|
|
|
|
return 1
|
|
}
|
|
|
|
# Orchestrate project detection in priority order
|
|
detect_project()
|
|
{
|
|
local result
|
|
|
|
# 1. sonar-project.properties
|
|
if result=$(detect_project_from_properties); then
|
|
echo "$result"
|
|
return 0
|
|
fi
|
|
|
|
# 2. .sonarlint/connectedMode.json
|
|
if result=$(detect_project_from_sonarlint); then
|
|
echo "$result"
|
|
return 0
|
|
fi
|
|
|
|
# No config found
|
|
log_error "Could not auto-detect SonarCloud project configuration."
|
|
log_error "Provide one of the following:"
|
|
log_error " 1. sonar-project.properties with sonar.organization and sonar.projectKey"
|
|
log_error " 2. .sonarlint/connectedMode.json with sonarCloudOrganization and projectKey"
|
|
log_error " 3. CLI flags: --org <org> --project-key <key>"
|
|
return 1
|
|
}
|
|
|
|
# Get API base URL (currently same for all regions)
|
|
get_base_url()
|
|
{
|
|
echo "https://sonarcloud.io"
|
|
}
|
|
|
|
# Make an authenticated SonarCloud API request
|
|
sonar_api_request()
|
|
{
|
|
local url="$1"
|
|
|
|
log_debug "API request: $url"
|
|
|
|
local http_code body response
|
|
response=$(curl -s -w "\n%{http_code}" \
|
|
-H "Authorization: Bearer $SONAR_TOKEN" \
|
|
"$url" 2> /dev/null) || {
|
|
log_error "curl request failed for: $url"
|
|
return 1
|
|
}
|
|
|
|
http_code=$(echo "$response" | tail -n1)
|
|
body=$(echo "$response" | sed '$d')
|
|
|
|
case "$http_code" in
|
|
200)
|
|
echo "$body"
|
|
return 0
|
|
;;
|
|
401)
|
|
log_error "Authentication failed (HTTP 401). Check your SONAR_TOKEN."
|
|
return 1
|
|
;;
|
|
403)
|
|
log_error "Access forbidden (HTTP 403). Token may lack required permissions."
|
|
return 1
|
|
;;
|
|
404)
|
|
log_error "Not found (HTTP 404). Check organization and project key."
|
|
return 1
|
|
;;
|
|
429)
|
|
log_error "Rate limited (HTTP 429). Wait before retrying."
|
|
return 1
|
|
;;
|
|
*)
|
|
log_error "API request failed with HTTP $http_code"
|
|
log_debug "Response body: $body"
|
|
return 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
# Fetch a single page of issues
|
|
fetch_issues_page()
|
|
{
|
|
local base_url="$1"
|
|
local project_key="$2"
|
|
local page="$3"
|
|
local pr_number="${4:-}"
|
|
local branch="${5:-}"
|
|
local severities="${6:-}"
|
|
local types="${7:-}"
|
|
local statuses="${8:-}"
|
|
local resolved="${9:-}"
|
|
|
|
local url="${base_url}/api/issues/search?componentKeys=${project_key}"
|
|
url="${url}&p=${page}&ps=${MAX_PAGE_SIZE}"
|
|
|
|
if [[ -n "$pr_number" ]]; then
|
|
url="${url}&pullRequest=${pr_number}"
|
|
fi
|
|
|
|
if [[ -n "$branch" ]]; then
|
|
url="${url}&branch=${branch}"
|
|
fi
|
|
|
|
if [[ -n "$severities" ]]; then
|
|
url="${url}&severities=${severities}"
|
|
fi
|
|
|
|
if [[ -n "$types" ]]; then
|
|
url="${url}&types=${types}"
|
|
fi
|
|
|
|
if [[ -n "$statuses" ]]; then
|
|
url="${url}&statuses=${statuses}"
|
|
fi
|
|
|
|
if [[ -n "$resolved" ]]; then
|
|
url="${url}&resolved=${resolved}"
|
|
fi
|
|
|
|
sonar_api_request "$url"
|
|
}
|
|
|
|
# Fetch all issues with pagination
|
|
fetch_all_issues()
|
|
{
|
|
local base_url="$1"
|
|
local project_key="$2"
|
|
local pr_number="${3:-}"
|
|
local branch="${4:-}"
|
|
local severities="${5:-}"
|
|
local types="${6:-}"
|
|
local statuses="${7:-}"
|
|
local resolved="${8:-}"
|
|
|
|
local page=1
|
|
local all_issues="[]"
|
|
local total=0
|
|
|
|
while true; do
|
|
log_info "Fetching issues page $page..."
|
|
|
|
local response
|
|
response=$(fetch_issues_page "$base_url" "$project_key" "$page" \
|
|
"$pr_number" "$branch" "$severities" "$types" "$statuses" "$resolved") || return 1
|
|
|
|
local page_issues page_total
|
|
page_issues=$(echo "$response" | jq '.issues // []' 2> /dev/null || echo "[]")
|
|
page_total=$(echo "$response" | jq '.total // 0' 2> /dev/null || echo "0")
|
|
|
|
local page_count
|
|
page_count=$(echo "$page_issues" | jq 'length' 2> /dev/null || echo "0")
|
|
|
|
log_debug "Page $page: $page_count issues (total available: $page_total)"
|
|
|
|
# Merge into accumulated results
|
|
all_issues=$(echo "$all_issues" "$page_issues" | jq -s '.[0] + .[1]' 2> /dev/null || echo "$all_issues")
|
|
total=$(echo "$all_issues" | jq 'length' 2> /dev/null || echo "0")
|
|
|
|
# Check if we have all issues or hit the cap
|
|
if [[ "$page_count" -lt "$MAX_PAGE_SIZE" ]]; then
|
|
break
|
|
fi
|
|
|
|
if [[ "$total" -ge "$MAX_TOTAL_ISSUES" ]]; then
|
|
log_warn "Reached maximum of $MAX_TOTAL_ISSUES issues. Results may be incomplete."
|
|
break
|
|
fi
|
|
|
|
page=$((page + 1))
|
|
done
|
|
|
|
log_info "Fetched $total issues total"
|
|
echo "$all_issues"
|
|
}
|
|
|
|
# Format issues grouped by severity then by file
|
|
format_issues_by_severity()
|
|
{
|
|
local issues="$1"
|
|
local base_url="$2"
|
|
local org="$3"
|
|
local project_key="$4"
|
|
|
|
echo "$issues" | jq -r --arg base_url "$base_url" --arg org "$org" --arg key "$project_key" '
|
|
group_by(.severity) | sort_by(-(
|
|
if .[0].severity == "BLOCKER" then 5
|
|
elif .[0].severity == "CRITICAL" then 4
|
|
elif .[0].severity == "MAJOR" then 3
|
|
elif .[0].severity == "MINOR" then 2
|
|
elif .[0].severity == "INFO" then 1
|
|
else 0 end
|
|
)) | .[] |
|
|
"### Severity: \(.[0].severity)\n" +
|
|
(
|
|
group_by(.component) | .[] |
|
|
"#### File: \(.[0].component | split(":") | if length > 1 then .[1:] | join(":") else .[0] end)\n" +
|
|
(
|
|
[.[] |
|
|
"##### Issue: \(.message)\n" +
|
|
"- **Rule:** \(.rule)\n" +
|
|
"- **Type:** \(.type)\n" +
|
|
"- **Severity:** \(.severity)\n" +
|
|
"- **Status:** \(.status)\n" +
|
|
"- **Line:** \(.line // "N/A")\n" +
|
|
"- **Effort:** \(.effort // "N/A")\n" +
|
|
"- **Created:** \(.creationDate // "N/A")\n" +
|
|
"- **URL:** \($base_url)/project/issues?open=\(.key)&id=\($key)\n"
|
|
] | join("\n")
|
|
)
|
|
)
|
|
' 2> /dev/null || echo "Error formatting issues."
|
|
}
|
|
|
|
# Format summary counts
|
|
format_summary()
|
|
{
|
|
local issues="$1"
|
|
|
|
echo "### By Severity"
|
|
echo ""
|
|
echo "$issues" | jq -r '
|
|
group_by(.severity) | .[] |
|
|
"- **\(.[0].severity):** \(length)"
|
|
' 2> /dev/null || echo "- Error computing severity counts"
|
|
|
|
echo ""
|
|
echo "### By Type"
|
|
echo ""
|
|
echo "$issues" | jq -r '
|
|
group_by(.type) | .[] |
|
|
"- **\(.[0].type):** \(length)"
|
|
' 2> /dev/null || echo "- Error computing type counts"
|
|
|
|
echo ""
|
|
echo "### Total"
|
|
echo ""
|
|
local count
|
|
count=$(echo "$issues" | jq 'length' 2> /dev/null || echo "0")
|
|
echo "- **Total issues:** $count"
|
|
}
|
|
|
|
# Format the full markdown output
|
|
format_output()
|
|
{
|
|
local org="$1"
|
|
local project_key="$2"
|
|
local mode="$3"
|
|
local mode_value="$4"
|
|
local base_url="$5"
|
|
local issues="$6"
|
|
|
|
local issue_count
|
|
issue_count=$(echo "$issues" | jq 'length' 2> /dev/null || echo "0")
|
|
|
|
# Header and LLM instructions
|
|
cat << 'EOF'
|
|
# SonarCloud Issues Analysis Report
|
|
|
|
## LLM Processing Instructions
|
|
|
|
You are analyzing code quality issues from SonarCloud for this project.
|
|
|
|
**Your tasks:**
|
|
1. **Triage**: Review each issue and assess its real impact on the codebase
|
|
2. **Priority Assessment**: Rank issues by severity and likelihood of causing problems
|
|
3. **Code Verification**: Check the actual source code to confirm each issue is valid
|
|
4. **Root Cause Analysis**: Identify why the issue exists and what pattern caused it
|
|
5. **Implementation Plan**: Create actionable fix tasks grouped by file for efficiency
|
|
6. **False Positive Detection**: Flag issues that appear to be false positives with reasoning
|
|
|
|
**Tools to use:**
|
|
- `find`, `cat`, `rg` commands and available tools to examine current codebase
|
|
- `git log` and `git blame` to understand code history and authorship
|
|
- File system tools to verify mentioned files exist and check current state
|
|
EOF
|
|
|
|
# Project information
|
|
cat << EOF
|
|
|
|
## Project Information
|
|
|
|
- **Organization:** $org
|
|
- **Project Key:** $project_key
|
|
EOF
|
|
|
|
case "$mode" in
|
|
pr)
|
|
echo "- **Mode:** Pull Request #$mode_value"
|
|
echo "- **URL:** ${base_url}/project/issues?pullRequest=${mode_value}&id=${project_key}"
|
|
;;
|
|
branch)
|
|
echo "- **Mode:** Branch \`$mode_value\`"
|
|
echo "- **URL:** ${base_url}/project/issues?branch=${mode_value}&id=${project_key}"
|
|
;;
|
|
*)
|
|
echo "- **Mode:** Project (all open issues)"
|
|
echo "- **URL:** ${base_url}/project/issues?id=${project_key}"
|
|
;;
|
|
esac
|
|
|
|
echo "- **Dashboard:** ${base_url}/project/overview?id=${project_key}"
|
|
|
|
# Issues section
|
|
echo ""
|
|
echo "## Issues ($issue_count total)"
|
|
echo ""
|
|
|
|
if [[ "$issue_count" -eq 0 ]]; then
|
|
echo "No issues found matching the specified filters."
|
|
else
|
|
format_issues_by_severity "$issues" "$base_url" "$org" "$project_key"
|
|
|
|
echo ""
|
|
echo "## Summary"
|
|
echo ""
|
|
format_summary "$issues"
|
|
fi
|
|
|
|
# Footer
|
|
cat << 'EOF'
|
|
|
|
## Next Steps for LLM Analysis
|
|
|
|
1. **Validate against current code:**
|
|
- Check if mentioned files and lines still match the reported issues
|
|
- Verify issues are not already fixed in the current branch
|
|
- Identify false positives and explain why they are false positives
|
|
|
|
2. **Prioritize fixes:**
|
|
- Address BLOCKER and CRITICAL severity issues first
|
|
- Group fixes by file to minimize context switching
|
|
- Consider effort estimates when planning the fix order
|
|
|
|
3. **Group by file for implementation:**
|
|
- Batch changes to the same file together
|
|
- Consider dependencies between fixes
|
|
- Plan atomic commits per logical change group
|
|
|
|
4. **Track progress:**
|
|
- Use todo lists and memory tools to track which issues are addressed
|
|
- Mark false positives with clear reasoning
|
|
- Verify fixes do not introduce new issues
|
|
EOF
|
|
}
|
|
|
|
# Main pipeline: fetch and display issues
|
|
fetch_and_display_issues()
|
|
{
|
|
local org="$1"
|
|
local project_key="$2"
|
|
local mode="$3"
|
|
local mode_value="$4"
|
|
local severities="${5:-}"
|
|
local types="${6:-}"
|
|
local statuses="${7:-}"
|
|
local resolved="${8:-}"
|
|
|
|
local base_url
|
|
base_url=$(get_base_url)
|
|
|
|
local pr_number=""
|
|
local branch=""
|
|
|
|
case "$mode" in
|
|
pr)
|
|
pr_number="$mode_value"
|
|
;;
|
|
branch)
|
|
branch="$mode_value"
|
|
;;
|
|
esac
|
|
|
|
log_info "Fetching SonarCloud issues for $project_key (mode: $mode)..."
|
|
|
|
local issues
|
|
issues=$(fetch_all_issues "$base_url" "$project_key" \
|
|
"$pr_number" "$branch" "$severities" "$types" "$statuses" "$resolved") || {
|
|
log_error "Failed to fetch issues"
|
|
return 1
|
|
}
|
|
|
|
format_output "$org" "$project_key" "$mode" "$mode_value" "$base_url" "$issues"
|
|
}
|
|
|
|
# Main function
|
|
main()
|
|
{
|
|
local org=""
|
|
local project_key=""
|
|
local mode="project"
|
|
local mode_value=""
|
|
local severities=""
|
|
local types=""
|
|
local statuses="OPEN,CONFIRMED,REOPENED"
|
|
local resolved="false"
|
|
|
|
# Parse arguments
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
-h | --help)
|
|
show_usage
|
|
exit 0
|
|
;;
|
|
--pr)
|
|
mode="pr"
|
|
mode_value="${2:?Missing PR number after --pr}"
|
|
shift 2
|
|
;;
|
|
--branch)
|
|
mode="branch"
|
|
mode_value="${2:?Missing branch name after --branch}"
|
|
shift 2
|
|
;;
|
|
--org)
|
|
org="${2:?Missing organization after --org}"
|
|
shift 2
|
|
;;
|
|
--project-key)
|
|
project_key="${2:?Missing project key after --project-key}"
|
|
shift 2
|
|
;;
|
|
--severities)
|
|
severities="${2:?Missing severities after --severities}"
|
|
shift 2
|
|
;;
|
|
--types)
|
|
types="${2:?Missing types after --types}"
|
|
shift 2
|
|
;;
|
|
--statuses)
|
|
statuses="${2:?Missing statuses after --statuses}"
|
|
shift 2
|
|
;;
|
|
--resolved)
|
|
resolved="true"
|
|
statuses=""
|
|
shift
|
|
;;
|
|
*)
|
|
log_error "Unknown argument: $1"
|
|
show_usage
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
check_dependencies
|
|
check_auth
|
|
|
|
# Auto-detect project if not specified via CLI
|
|
if [[ -z "$org" || -z "$project_key" ]]; then
|
|
local detected
|
|
detected=$(detect_project) || exit 1
|
|
# shellcheck disable=SC2034 # region reserved for future per-region base URLs
|
|
read -r detected_org detected_key detected_region <<< "$detected"
|
|
|
|
if [[ -z "$org" ]]; then
|
|
org="$detected_org"
|
|
fi
|
|
if [[ -z "$project_key" ]]; then
|
|
project_key="$detected_key"
|
|
fi
|
|
fi
|
|
|
|
log_debug "Organization: $org"
|
|
log_debug "Project Key: $project_key"
|
|
log_debug "Mode: $mode"
|
|
log_debug "Severities: ${severities:-all}"
|
|
log_debug "Types: ${types:-all}"
|
|
log_debug "Statuses: ${statuses:-all}"
|
|
log_debug "Resolved: $resolved"
|
|
|
|
fetch_and_display_issues "$org" "$project_key" "$mode" "$mode_value" \
|
|
"$severities" "$types" "$statuses" "$resolved"
|
|
}
|
|
|
|
# Run main function with all arguments
|
|
main "$@"
|