-
Notifications
You must be signed in to change notification settings - Fork 8
CLI-800 Introduce sonar update, deprecate sonar self-update #558
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -137,7 +137,8 @@ Error subclasses extend the abstract `CliError` and carry their own `exitCode`, | |
| - `sonar auth logout` relies on state: if there is no active connection or `isAuthenticated` is false, it only reports that you are already logged out (no keychain changes). | ||
| - When `sonar auth login` runs the browser-based OAuth flow, the server-generated token name returned in the callback POST body is captured and persisted on the connection as `tokenName` (see `AuthConnection` in `src/lib/state.ts`). The wire field is `name` (matching `/api/user_tokens/revoke?name=`); we keep it as `tokenName` in-memory to disambiguate from other "name" fields. | ||
| - On `sonar auth logout`, the CLI best-effort revokes the server-side token via `SonarQubeClient.revokeUserToken(...)` (a one-line wrapper over the generic `postForm(endpoint, params)` helper) before clearing the keychain entry. Failures (network error, non-2xx response) are reported via a warning on stderr; local cleanup still proceeds. When the connection has no `tokenName` (e.g. upgraded from an older CLI version), the CLI emits a manual-revocation hint on stderr instead. | ||
| - `sonar system status` displays a diagnostic overview: authentication (token verified live via `/api/authentication/validate`, four states: Not Set / Active / Invalid / Set Unverified), installed binaries with update availability, configured integrations with paths, and MCP Server configured/running status. Supports `--json` for machine consumption. Implementation in `src/cli/commands/system/status.ts`. Token verification uses `checkTokenStatus` from `_common/token.ts`; CLI update availability comes from `Distribution/sonarqube-cli/stable.version`, while `sonar self-update` downloads the platform installer script from the GitHub `user-scripts/` directory when it actually performs the update. MCP config validation is JSON-based for Claude/Copilot (checks `mcpServers.sonarqube` structure) and trusts file existence for Codex TOML configs. | ||
| - `sonar system status` displays a diagnostic overview: authentication (token verified live via `/api/authentication/validate`, four states: Not Set / Active / Invalid / Set Unverified), installed binaries with update availability, configured integrations with paths, and MCP Server configured/running status. Supports `--json` for machine consumption. Implementation in `src/cli/commands/system/status.ts`. Token verification uses `checkTokenStatus` from `_common/token.ts`; CLI update availability comes from `Distribution/sonarqube-cli/stable.version`, while `sonar update` downloads the platform installer script from the GitHub `user-scripts/` directory when it actually performs the update. MCP config validation is JSON-based for Claude/Copilot (checks `mcpServers.sonarqube` structure) and trusts file existence for Codex TOML configs. | ||
| - `sonar self-update` is a deprecated, hidden alias for `sonar update` (standalone distribution only, see `IS_STANDALONE_DISTRIBUTION`): calling it prints a deprecation warning pointing to `sonar update`, then runs the same `updateVersion()` logic. Both are registered in `src/cli/command-tree.ts`. | ||
|
|
||
| ## Tests | ||
|
|
||
|
|
@@ -176,3 +177,35 @@ When adding, removing, or changing commands, scripts, or project structure, upda | |
|
|
||
| The docs site is generated from the CLI source — do not edit `commands.json`, `llms.txt`, or `sitemap.xml` by hand. This is done by automation post-release. The docs generator also stamps the current package version into the nav badge in `index.html` and `commands.html`, plus the JSON-LD `softwareVersion` and `meta-version` in `index.html`. | ||
| Keep a fixed `<base href="/sonarqube/cli/">` in the docs HTML so production resolves relative assets and internal links under the public mount point. This is intentionally production-oriented: Netlify deploy previews served from `/` will not work with that fixed base. | ||
|
|
||
| <!-- sonar:begin:sonarqube-agentic-analysis-protocol --> | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This looks unrelated to the ticket. Just making sure that it's not accidental. |
||
| # SonarQube Agentic Analysis protocol | ||
|
|
||
| SonarQube Agentic Analysis is the final confirmation layer at the end of every turn in which you wrote to one or more files in the workspace (create, edit, patch, format — any tool call that changed file contents on disk). | ||
|
|
||
| **Trigger:** the moment before you send your final reply for the turn. | ||
|
|
||
| When the trigger fires, you **MUST** run SonarQube Agentic Analysis on the full set of files you modified this turn — **one command, not a per-file loop**. | ||
|
|
||
| **Preferred:** pass every path you modified this turn in a single invocation (repeat `--file` for each path). Use paths **relative to the project root** (e.g. `src/foo.ts`). For end-of-turn analysis, **always** pass `--depth DEEP` — even when you changed only one file — so cross-file findings are included. | ||
|
|
||
| ```bash | ||
| sonar analyze agentic --project SonarSource_sonarqube-cli --depth DEEP --file <path/to/file1> --file <path/to/file2> | ||
| ``` | ||
|
|
||
| **Fallback:** when you cannot reliably list every modified path (e.g. you did not track them, or git state is the only source of truth), use the git change set with DEEP analysis: | ||
|
|
||
| ```bash | ||
| sonar analyze agentic --project SonarSource_sonarqube-cli --depth DEEP | ||
| ``` | ||
|
|
||
| Per-edit hooks run faster STANDARD analysis. End-of-turn analysis must always use `--depth DEEP` (including a single `--file`). Use `--depth STANDARD` only for optional faster passes — never for the mandatory end-of-turn run. | ||
|
|
||
| Non-negotiable rules: | ||
|
|
||
| 1. Mid-turn edits do **not** need SonarQube Agentic Analysis — only the final on-disk state matters. Skipping SonarQube Agentic Analysis at end-of-turn is a protocol violation. | ||
| 2. Run SonarQube Agentic Analysis **before** sending your final reply, marking the task done, or handing control back to the user. | ||
| 3. If SonarQube Agentic Analysis reports issues on lines you touched in this turn, fix them, then re-run SonarQube Agentic Analysis on the same scope (change set or explicit file list). Repeat until clean (or only pre-existing findings on lines you did not touch remain). Pre-existing findings on untouched lines are out of scope — do not "fix" them unless the user asked. | ||
| 4. If SonarQube Agentic Analysis is skipped (no SonarQube Cloud connection, or no project configured), state the skip reason to the user once and continue — do not retry. | ||
| 5. Do not suppress, summarize away, or omit SonarQube Agentic Analysis findings from your reply. Surface them verbatim. | ||
| <!-- sonar:end:sonarqube-agentic-analysis-protocol --> | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -27,7 +27,7 @@ export interface SelfUpdateOptions { | |
| force?: boolean; | ||
| } | ||
|
|
||
| async function selfUpdateStatus(): Promise<void> { | ||
| async function updateVersionStatus(): Promise<void> { | ||
| info('Checking for updates...'); | ||
|
|
||
| const { currentVersion, latest, upToDate } = await checkForUpdate(); | ||
|
|
@@ -40,13 +40,13 @@ async function selfUpdateStatus(): Promise<void> { | |
| success('Already up to date'); | ||
| } else { | ||
| warn(`Update available: v${latest.version.noBuild.text}`); | ||
| text(' Run: sonar self-update'); | ||
| text(' Run: sonar update'); | ||
| } | ||
| } | ||
|
|
||
| export async function selfUpdate(options: SelfUpdateOptions = {}): Promise<void> { | ||
| export async function updateVersion(options: SelfUpdateOptions = {}): Promise<void> { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| if (options.status) { | ||
| await selfUpdateStatus(); | ||
| await updateVersionStatus(); | ||
| return; | ||
| } | ||
|
|
||
|
|
@@ -78,7 +78,7 @@ export async function selfUpdate(options: SelfUpdateOptions = {}): Promise<void> | |
| : `Update script exited with code ${String(installResult.scriptExitStatus ?? 'unknown')}`; | ||
| throw new CommandFailedError(message, { | ||
| remediationHint: | ||
| "Rerun 'sonar self-update --force' or update manually using the installer script.", | ||
| "Rerun 'sonar update --force' or update manually using the installer script.", | ||
| }); | ||
| } | ||
| case 'installed': | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -29,7 +29,7 @@ import { Version } from '../cli/commands/_common/version.js'; | |
| import { | ||
| BACKGROUND_UPDATE_CHECK_TIMEOUT_MS, | ||
| fetchLatestVersion, | ||
| } from '../cli/commands/self-update/update-check.js'; | ||
| } from '../cli/commands/update/update-check.js'; | ||
| import { TELEMETRY_FLUSH_MODE_ENV } from '../telemetry/index.js'; | ||
| import { cyan } from '../ui/colors.js'; | ||
| import { isFormattedOutputMode, text } from '../ui/index.js'; | ||
|
|
@@ -141,7 +141,7 @@ function renderUpdateNotification(currentNoBuild: string, latestNoBuild: string) | |
| undefined, | ||
| 'stderr', | ||
| ); | ||
| text(' → Run `sonar self-update` to upgrade.', undefined, 'stderr'); | ||
| text(' → Run `sonar update` to get the latest version', undefined, 'stderr'); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Very minor nitpick, this message could be aligned with the status.ts message |
||
| } | ||
|
|
||
| /** | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| /* | ||
| * SonarQube CLI | ||
| * Copyright (C) SonarSource Sàrl | ||
| * mailto:info AT sonarsource DOT com | ||
| * | ||
| * This program is free software; you can redistribute it and/or | ||
| * modify it under the terms of the GNU Lesser General Public | ||
| * License as published by the Free Software Foundation; either | ||
| * version 3 of the License, or (at your option) any later version. | ||
| * | ||
| * This program is distributed in the hope that it will be useful, | ||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| * Lesser General Public License for more details. | ||
| * | ||
| * You should have received a copy of the GNU Lesser General Public License | ||
| * along with this program; if not, write to the Free Software Foundation, | ||
| * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
| */ | ||
|
|
||
| // Integration tests for the `sonar update` command and the deprecated `sonar self-update` alias | ||
|
|
||
| import { afterEach, beforeEach, describe, expect, it } from 'bun:test'; | ||
|
|
||
| import { TestHarness } from '../../harness'; | ||
|
|
||
| describe('update command', () => { | ||
| let harness: TestHarness; | ||
|
|
||
| beforeEach(async () => { | ||
| harness = await TestHarness.create(); | ||
| }); | ||
|
|
||
| afterEach(async () => { | ||
| await harness.dispose(); | ||
| }); | ||
|
|
||
| it( | ||
| 'sonar update --status reports an available update without a deprecation warning', | ||
| async () => { | ||
| const newerVersion = '99.0.0'; | ||
| await harness.newFakeBinariesServer().withStableVersion(newerVersion).start(); | ||
|
|
||
| const result = await harness.run('update --status'); | ||
|
|
||
| expect(result.exitCode).toBe(0); | ||
| expect(result.stdout + result.stderr).toContain(`Update available: v${newerVersion}`); | ||
| expect(result.stderr).not.toContain('deprecated'); | ||
| }, | ||
| { timeout: 15000 }, | ||
| ); | ||
|
|
||
| it( | ||
| 'sonar self-update --status still works but warns that it is deprecated', | ||
| async () => { | ||
| const newerVersion = '99.0.0'; | ||
| await harness.newFakeBinariesServer().withStableVersion(newerVersion).start(); | ||
|
|
||
| const result = await harness.run('self-update --status'); | ||
|
|
||
| expect(result.exitCode).toBe(0); | ||
| expect(result.stdout + result.stderr).toContain(`Update available: v${newerVersion}`); | ||
| expect(result.stderr).toContain('deprecated'); | ||
| expect(result.stderr).toContain('sonar update'); | ||
| }, | ||
| { timeout: 15000 }, | ||
| ); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AGENTS.mdshould be in sync with theCLAUDE.mdAt some point probably we can change the
CLAUDE.mdto contain only a reference to@AGENTS.md, like in intellij.