diff --git a/README.md b/README.md index 6e63e1b..6eaee26 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,20 @@ -# sonacli +

+ sonacli logo +

-`sonacli` is a CLI for consuming SonarQube analysis reports. +

sonacli

+ +

A CLI for consuming SonarQube analysis reports.

+ +

+ Release + CI + License +

+ +

+ sonacli demo — auth, project list, issue list with jq, severity filter, and SonarQube URL parsing +

> SonarQube is an open-source platform for continuous inspection of code quality and security. It ships in several editions: **Community Build** (free, open-source, single-node), **Server** (commercial), **Data Center** (high-availability), and **Cloud** (SaaS, hosted by Sonar). `sonacli` targets the Community Build only. diff --git a/docs/_demo-source/sonar-project.properties b/docs/_demo-source/sonar-project.properties new file mode 100644 index 0000000..eb99103 --- /dev/null +++ b/docs/_demo-source/sonar-project.properties @@ -0,0 +1,4 @@ +sonar.projectKey=payments-api +sonar.projectName=Payments API +sonar.sources=src +sonar.sourceEncoding=UTF-8 diff --git a/docs/_demo-source/src/auth.go b/docs/_demo-source/src/auth.go new file mode 100644 index 0000000..d8c1d02 --- /dev/null +++ b/docs/_demo-source/src/auth.go @@ -0,0 +1,26 @@ +// Deliberately problematic demo fixture used to regenerate docs/demo.gif. +// Not compiled by the sonacli module — the parent directory is Go-ignored. +package payments + +import ( + "fmt" + "net/http" +) + +const adminPassword = "replace-me-in-real-deployments" + +const stripeSecretKey = "replace-me-in-real-deployments" + +func Authenticate(user, pass string) bool { + if user == "admin" && pass == adminPassword { + return true + } + + return false +} + +func CallStripe() { + req, _ := http.NewRequest("GET", "https://api.stripe.com/v1/charges", nil) + req.Header.Set("Authorization", "Bearer "+stripeSecretKey) + fmt.Println(req) +} diff --git a/docs/_demo-source/src/invoice.go b/docs/_demo-source/src/invoice.go new file mode 100644 index 0000000..6c49b11 --- /dev/null +++ b/docs/_demo-source/src/invoice.go @@ -0,0 +1,25 @@ +// Deliberately problematic demo fixture used to regenerate docs/demo.gif. +// Not compiled by the sonacli module — the parent directory is Go-ignored. +package payments + +import "fmt" + +// TODO: migrate to the new billing engine before Q3 +// FIXME: handle partial refunds +// TODO: add idempotency keys once the gateway supports them + +func SendInvoice(id string) { + total := 0 + total = 100 + total = 200 + + fmt.Println("invoice", id, total) +} + +func duplicate() { + fmt.Println("dup") +} + +func duplicate2() { + fmt.Println("dup") +} diff --git a/docs/_demo-source/src/pricing.go b/docs/_demo-source/src/pricing.go new file mode 100644 index 0000000..553e336 --- /dev/null +++ b/docs/_demo-source/src/pricing.go @@ -0,0 +1,59 @@ +// Deliberately problematic demo fixture used to regenerate docs/demo.gif. +// Not compiled by the sonacli module — the parent directory is Go-ignored. +package payments + +import "fmt" + +func ComputeDiscount(tier string, quantity int, seasonal bool, vip bool, region string) float64 { + discount := 0.0 + + if tier == "gold" { + if quantity > 100 { + if seasonal { + if vip { + discount = 0.35 + } else { + discount = 0.25 + } + } else { + if region == "EU" { + discount = 0.20 + } else { + discount = 0.15 + } + } + } else { + if vip { + discount = 0.15 + } else { + discount = 0.10 + } + } + } else if tier == "silver" { + if quantity > 50 { + if seasonal { + discount = 0.15 + } else { + discount = 0.10 + } + } else { + if vip { + discount = 0.08 + } else { + discount = 0.05 + } + } + } else { + if quantity > 20 { + discount = 0.05 + } else { + discount = 0.0 + } + } + + return discount +} + +func LegacyPricing() { + fmt.Println("legacy path") +} diff --git a/docs/demo.gif b/docs/demo.gif new file mode 100644 index 0000000..c1ff825 Binary files /dev/null and b/docs/demo.gif differ diff --git a/docs/demo.tape b/docs/demo.tape new file mode 100644 index 0000000..ab8977e --- /dev/null +++ b/docs/demo.tape @@ -0,0 +1,80 @@ +# sonacli demo tape — regenerates docs/demo.gif +# +# Prerequisites: make, Go 1.26+, podman, vhs, jq, curl, a cached +# SonarScanner CLI (see scripts/seed-sample-project.sh for one). +# +# Regenerate: +# 1. make build +# 2. ./scripts/local-sonarqube.sh start +# 3. Create and analyze the demo project (once per fresh SonarQube): +# curl -fsS -u admin:SonacliAdmin1@ -X POST \ +# "http://127.0.0.1:9000/api/projects/create" \ +# --data-urlencode "project=payments-api" \ +# --data-urlencode "name=Payments API" +# SCANNER=tests/bin/sample-project-tools/sonar-scanner-*/bin/sonar-scanner +# SCAN_TOKEN=$(curl -fsS -u admin:SonacliAdmin1@ -X POST \ +# "http://127.0.0.1:9000/api/user_tokens/generate" \ +# --data-urlencode "name=demo-scan-$(date +%s)" | jq -r .token) +# ( cd docs/_demo-source && \ +# SONAR_HOST_URL=http://127.0.0.1:9000 SONAR_TOKEN=$SCAN_TOKEN \ +# "$PWD/../../$SCANNER" ) +# 4. export TOKEN=$(curl -fsS -u admin:SonacliAdmin1@ -X POST \ +# "http://127.0.0.1:9000/api/user_tokens/generate" \ +# --data-urlencode "name=demo-$(date +%s)" | jq -r .token) +# 5. export ISSUE_KEY=$(curl -fsS -u admin:SonacliAdmin1@ \ +# "http://127.0.0.1:9000/api/issues/search?componentKeys=payments-api&impactSeverities=HIGH" \ +# | jq -r '.issues[0].key') +# 6. vhs docs/demo.tape + +Output docs/demo.gif + +Set Shell "bash" +Set FontSize 14 +Set Width 1200 +Set Height 760 +Set Theme "Dracula" +Set TypingSpeed 30ms +Set PlaybackSpeed 1.0 +Set Padding 25 +Set WindowBar Colorful + +Hide +Type 'export PATH="$PWD:$PATH" HOME=/tmp/sonacli-demo-home PS1="$ "' +Enter +Type 'mkdir -p $HOME && rm -rf $HOME/.sonacli && cd /tmp && clear' +Enter +Show + +Sleep 600ms + +# Scene 1 — Connect sonacli to a SonarQube instance. +Type "sonacli auth setup -s http://127.0.0.1:9000 -t $TOKEN" +Sleep 300ms +Enter +Sleep 1500ms + +# Scene 2 — Browse projects as pretty JSON. +Type "sonacli project list --pretty" +Sleep 300ms +Enter +Sleep 1800ms + +# Scene 3 — sonacli speaks JSON: compose with jq to reshape issues. +Type "sonacli issue list payments-api | jq '.issues[] | {rule, sev: .impacts[0].severity, file: .component, line, message}'" +Sleep 300ms +Enter +Sleep 2200ms + +# Scene 4 — Filter by impact severity. +Type "sonacli issue list payments-api --severity HIGH | jq '.issues[] | {rule, file: .component, line, message}'" +Sleep 300ms +Enter +Sleep 2200ms + +# Scene 5 — Paste a SonarQube web URL; sonacli extracts the issue key for you. +Type@6ms 'sonacli issue show "http://127.0.0.1:9000/project/issues?id=payments-api&issues=$ISSUE_KEY" | jq "{rule, file: .component, line, impact: .impacts[0], message}"' +Sleep 300ms +Enter +Sleep 2500ms + +Sleep 500ms diff --git a/sonacli-logo.png b/sonacli-logo.png new file mode 100644 index 0000000..053b7c3 Binary files /dev/null and b/sonacli-logo.png differ