Skip to content

Commit 7c6a3a6

Browse files
committed
Merge branch 'fix/checkup-no-upload-markdown' into 'main'
fix(cli): reject markdown with no-upload Closes #284 See merge request postgres-ai/postgresai!359
2 parents 61d1252 + d8010fd commit 7c6a3a6

5 files changed

Lines changed: 61 additions & 36 deletions

File tree

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ Open [console.postgres.ai](https://console.postgres.ai) to see:
6464
- Issues workflow to track remediation
6565
- Historical data across all your projects
6666

67-
> **Offline mode:** Add `--no-upload` to run locally without an account.
67+
> **Offline mode:** Add `--no-upload` to run locally without an account or sending report data to the PostgresAI API.
6868
6969
<details open>
7070
<summary>See demo</summary>
@@ -85,6 +85,10 @@ npx postgresai checkup --check-id H002 postgresql://...
8585
npx postgresai checkup --no-upload --check-id H002 postgresql://...
8686
```
8787

88+
Markdown conversion is performed by the PostgresAI API and transmits the full
89+
report JSON. Therefore, `--markdown` cannot be combined with `--no-upload`. For
90+
local-only output, use `--json` or `--output` instead.
91+
8892
> **Tips:** `npx pgai checkup` also works. `bunx postgresai` if you prefer Bun.
8993
9094
## Full monitoring stack

cli/CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Changelog
2+
3+
## Unreleased
4+
5+
### Fixed
6+
7+
- `checkup --markdown` previously performed server-side conversion and sent the
8+
full report JSON to the PostgresAI API even when `--no-upload` was set. The
9+
flags are now mutually exclusive, and `--no-upload` prevents report data from
10+
being sent to the PostgresAI API. Use `--json` or `--output` for local-only
11+
output.

cli/bin/postgres-ai.ts

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -335,12 +335,18 @@ function prepareUploadConfig(
335335
console.error("Tip: run 'postgresai auth' or pass --api-key / set PGAI_API_KEY");
336336
return null; // Signal to exit
337337
}
338-
// No credentials and upload not explicitly requested: fall back to
339-
// local-only mode, but say so prominently — skipping the upload silently
340-
// hides the fact that results never reach the Console.
341-
console.error("Notice: no API key configured — results will NOT be uploaded to PostgresAI.");
342-
console.error(" To upload: run 'postgresai auth login' or pass --api-key / set PGAI_API_KEY.");
343-
console.error(" To run locally without this notice, pass --no-upload.");
338+
if (opts.markdown) {
339+
console.error("Notice: no API key configured — regular report upload is disabled.");
340+
console.error(" The full report JSON will still be sent to the PostgresAI API for markdown conversion.");
341+
console.error(" To avoid sending report data, replace --markdown with --no-upload and --json or --output.");
342+
} else {
343+
// No credentials and upload not explicitly requested: fall back to
344+
// local-only mode, but say so prominently — skipping the upload silently
345+
// hides the fact that results never reach the Console.
346+
console.error("Notice: no API key configured — results will NOT be uploaded to PostgresAI.");
347+
console.error(" To upload: run 'postgresai auth login' or pass --api-key / set PGAI_API_KEY.");
348+
console.error(" To run locally without this notice, pass --no-upload.");
349+
}
344350
return undefined; // Skip upload, run checks locally
345351
}
346352

@@ -1996,7 +2002,7 @@ program
19962002
"project name or ID for remote upload (used with --upload; defaults to config defaultProject; auto-generated on first run)"
19972003
)
19982004
.option("--json", "output JSON to stdout")
1999-
.option("--markdown", "output markdown to stdout")
2005+
.option("--markdown", "output markdown via PostgresAI API (transmits the full report JSON)")
20002006
.addHelpText(
20012007
"after",
20022008
[
@@ -2010,7 +2016,7 @@ program
20102016
" postgresai checkup postgresql://user:pass@host:5432/db --check-id H002",
20112017
" postgresai checkup postgresql://user:pass@host:5432/db --output ./reports",
20122018
" postgresai checkup postgresql://user:pass@host:5432/db --no-upload --json",
2013-
" postgresai checkup postgresql://user:pass@host:5432/db --no-upload --markdown",
2019+
" postgresai checkup postgresql://user:pass@host:5432/db --markdown",
20142020
].join("\n")
20152021
)
20162022
.action(async (checkIdOrConn: string | undefined, connArg: string | undefined, opts: CheckupOptions, cmd: Command) => {
@@ -2060,9 +2066,14 @@ program
20602066
process.exitCode = 1;
20612067
return;
20622068
}
2063-
// Note: --json, --markdown and --upload/--no-upload are independent flags.
2064-
// Use --no-upload to explicitly disable upload when using --json or --markdown.
20652069
const uploadExplicitlyDisabled = opts.upload === false;
2070+
if (uploadExplicitlyDisabled && shouldConvertMarkdown) {
2071+
console.error("Error: --no-upload and --markdown are mutually exclusive");
2072+
console.error("Markdown conversion is performed by the PostgresAI API and transmits the full report JSON.");
2073+
console.error("Drop --no-upload to allow transmission, or use --json or --output for local-only output.");
2074+
process.exitCode = 1;
2075+
return;
2076+
}
20662077
let shouldUpload = !uploadExplicitlyDisabled;
20672078

20682079
// Preflight: validate/create output directory BEFORE connecting / running checks.
@@ -2308,7 +2319,9 @@ program
23082319

23092320
console.log('\nFor details:');
23102321
console.log(' --json Output JSON');
2311-
console.log(' --markdown Output markdown');
2322+
if (!uploadExplicitlyDisabled) {
2323+
console.log(' --markdown Output markdown via PostgresAI API');
2324+
}
23122325
console.log(' --output <dir> Save to directory');
23132326
}
23142327
} catch (error) {

cli/test/checkup.integration.test.ts

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -410,25 +410,4 @@ describe.skipIf(!!skipReason)("checkup integration: express mode schema compatib
410410
}
411411
});
412412

413-
test("CLI --markdown flag works without API key", async () => {
414-
// Test that --markdown works even without an API key
415-
const connString = `postgresql://postgres@${pg.socketDir}:${pg.port}/postgres`;
416-
const cliPath = path.resolve(import.meta.dir, "..", "bin", "postgres-ai.ts");
417-
const bunBin = typeof process.execPath === "string" && process.execPath.length > 0 ? process.execPath : "bun";
418-
419-
const result = Bun.spawnSync(
420-
[bunBin, cliPath, "checkup", connString, "--check-id", "H002", "--markdown", "--no-upload"],
421-
{
422-
env: {
423-
...process.env,
424-
XDG_CONFIG_HOME: "/tmp/postgresai-test-empty-config",
425-
},
426-
}
427-
);
428-
429-
const stderr = new TextDecoder().decode(result.stderr);
430-
431-
// Should not complain about missing API key
432-
expect(stderr).not.toMatch(/API key is required/i);
433-
});
434413
});

cli/test/checkup.test.ts

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1539,12 +1539,13 @@ describe("CLI tests", () => {
15391539
const r = runCli(["checkup", "--help"]);
15401540
expect(r.status).toBe(0);
15411541
expect(r.stdout).toMatch(/--markdown/);
1542-
expect(r.stdout).toMatch(/output markdown to stdout/i);
1542+
expect(r.stdout).toMatch(/PostgresAI API/i);
1543+
expect(r.stdout).toMatch(/transmits the full\s+report JSON/i);
15431544
});
15441545

15451546
test("checkup --markdown is recognized as valid option", () => {
15461547
// Should not produce "unknown option" error for --markdown
1547-
const r = runCli(["checkup", "postgresql://test:test@localhost:5432/test", "--markdown", "--no-upload"]);
1548+
const r = runCli(["checkup", "postgresql://test:test@localhost:5432/test", "--markdown"]);
15481549
// Connection will fail, but option parsing should succeed
15491550
expect(r.stderr).not.toMatch(/unknown option/i);
15501551
expect(r.stderr).not.toMatch(/did you mean/i);
@@ -1554,11 +1555,13 @@ describe("CLI tests", () => {
15541555
// Use empty config dir to ensure no API key is configured
15551556
const env = { XDG_CONFIG_HOME: "/tmp/postgresai-test-empty-config" };
15561557
// --markdown should work even without API key
1557-
const r = runCli(["checkup", "postgresql://test:test@localhost:5432/test", "--markdown", "--no-upload"], env);
1558+
const r = runCli(["checkup", "postgresql://test:test@localhost:5432/test", "--markdown"], env);
15581559
// Connection will fail, but --markdown flag should be recognized
15591560
expect(r.status).not.toBe(0);
15601561
expect(r.stderr).not.toMatch(/unknown option/i);
15611562
expect(r.stderr).not.toMatch(/API key is required/i);
1563+
expect(r.stderr).toMatch(/full report JSON will still be sent/i);
1564+
expect(r.stderr).toMatch(/markdown conversion/i);
15621565
});
15631566

15641567
test("checkup with --no-upload and no output flags shows summary", () => {
@@ -1663,6 +1666,21 @@ describe("checkup auth pre-flight (CLI)", () => {
16631666
// failure mentions this address.
16641667
const DEAD_DB = "postgresql://test:test@127.0.0.1:2/test";
16651668

1669+
test("--no-upload rejects --markdown before database or API work", () => {
1670+
const env = {
1671+
XDG_CONFIG_HOME: `/tmp/postgresai-test-no-upload-markdown-${process.pid}`,
1672+
PGAI_API_KEY: "configured-token",
1673+
PGAI_API_BASE_URL: "http://127.0.0.1:1",
1674+
};
1675+
const r = runCli(["checkup", DEAD_DB, "--no-upload", "--markdown"], env);
1676+
1677+
expect(r.status).not.toBe(0);
1678+
expect(r.stderr).toMatch(/--no-upload and --markdown are mutually exclusive/i);
1679+
expect(r.stderr).toMatch(/markdown conversion is performed by the PostgresAI API/i);
1680+
expect(r.stderr).toMatch(/use --json or --output/i);
1681+
expect(r.stderr).not.toMatch(/127\.0\.0\.1:2|ECONNREFUSED/);
1682+
});
1683+
16661684
test("no API key: prominent notice, run continues locally", () => {
16671685
const env = {
16681686
XDG_CONFIG_HOME: `/tmp/postgresai-test-preflight-nokey-${process.pid}`,

0 commit comments

Comments
 (0)