postgresai 0.14.0-beta.7 → 0.14.0-beta.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/postgres-ai.ts +92 -20
- package/bunfig.toml +3 -2
- package/dist/bin/postgres-ai.js +71 -16
- package/lib/metrics-embedded.ts +1 -1
- package/package.json +1 -1
- package/test/checkup.integration.test.ts +7 -5
- package/test/init.integration.test.ts +4 -2
package/bin/postgres-ai.ts
CHANGED
|
@@ -1155,8 +1155,14 @@ async function runCompose(args: string[], grafanaPassword?: string): Promise<num
|
|
|
1155
1155
|
}
|
|
1156
1156
|
}
|
|
1157
1157
|
|
|
1158
|
+
// On macOS, node-exporter can't mount host root filesystem - skip it
|
|
1159
|
+
const finalArgs = [...args];
|
|
1160
|
+
if (process.platform === "darwin" && args.includes("up")) {
|
|
1161
|
+
finalArgs.push("--scale", "node-exporter=0");
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1158
1164
|
return new Promise<number>((resolve) => {
|
|
1159
|
-
const child = spawn(cmd[0], [...cmd.slice(1), "-f", composeFile, ...
|
|
1165
|
+
const child = spawn(cmd[0], [...cmd.slice(1), "-f", composeFile, ...finalArgs], {
|
|
1160
1166
|
stdio: "inherit",
|
|
1161
1167
|
env: env,
|
|
1162
1168
|
cwd: projectDir
|
|
@@ -1215,8 +1221,8 @@ mon
|
|
|
1215
1221
|
if (pwdMatch) existingPassword = pwdMatch[1].trim();
|
|
1216
1222
|
}
|
|
1217
1223
|
|
|
1218
|
-
// Priority: CLI --tag flag > existing .env > package version
|
|
1219
|
-
const imageTag = opts.tag || existingTag || pkg.version;
|
|
1224
|
+
// Priority: CLI --tag flag > PGAI_TAG env var > existing .env > package version
|
|
1225
|
+
const imageTag = opts.tag || process.env.PGAI_TAG || existingTag || pkg.version;
|
|
1220
1226
|
|
|
1221
1227
|
const envLines: string[] = [`PGAI_TAG=${imageTag}`];
|
|
1222
1228
|
if (existingRegistry) {
|
|
@@ -1527,6 +1533,34 @@ mon
|
|
|
1527
1533
|
if (code !== 0) process.exitCode = code;
|
|
1528
1534
|
});
|
|
1529
1535
|
|
|
1536
|
+
// Known container names for cleanup
|
|
1537
|
+
const MONITORING_CONTAINERS = [
|
|
1538
|
+
"postgres-ai-config-init",
|
|
1539
|
+
"node-exporter",
|
|
1540
|
+
"cadvisor",
|
|
1541
|
+
"grafana-with-datasources",
|
|
1542
|
+
"sink-postgres",
|
|
1543
|
+
"sink-prometheus",
|
|
1544
|
+
"target-db",
|
|
1545
|
+
"pgwatch-postgres",
|
|
1546
|
+
"pgwatch-prometheus",
|
|
1547
|
+
"postgres-exporter-sink",
|
|
1548
|
+
"flask-pgss-api",
|
|
1549
|
+
"sources-generator",
|
|
1550
|
+
"postgres-reports",
|
|
1551
|
+
];
|
|
1552
|
+
|
|
1553
|
+
/** Remove orphaned containers that docker compose down might miss */
|
|
1554
|
+
async function removeOrphanedContainers(): Promise<void> {
|
|
1555
|
+
for (const container of MONITORING_CONTAINERS) {
|
|
1556
|
+
try {
|
|
1557
|
+
await execFilePromise("docker", ["rm", "-f", container]);
|
|
1558
|
+
} catch {
|
|
1559
|
+
// Container doesn't exist, ignore
|
|
1560
|
+
}
|
|
1561
|
+
}
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1530
1564
|
mon
|
|
1531
1565
|
.command("stop")
|
|
1532
1566
|
.description("stop monitoring services")
|
|
@@ -1765,34 +1799,72 @@ mon
|
|
|
1765
1799
|
});
|
|
1766
1800
|
mon
|
|
1767
1801
|
.command("clean")
|
|
1768
|
-
.description("cleanup monitoring services artifacts")
|
|
1769
|
-
.
|
|
1770
|
-
|
|
1802
|
+
.description("cleanup monitoring services artifacts (stops services and removes volumes)")
|
|
1803
|
+
.option("--keep-volumes", "keep data volumes (only stop and remove containers)")
|
|
1804
|
+
.action(async (options: { keepVolumes?: boolean }) => {
|
|
1805
|
+
console.log("Cleaning up monitoring services...\n");
|
|
1771
1806
|
|
|
1772
1807
|
try {
|
|
1773
|
-
//
|
|
1774
|
-
const
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1808
|
+
// First, use docker-compose down to properly stop and remove containers/volumes
|
|
1809
|
+
const downArgs = options.keepVolumes ? ["down"] : ["down", "-v"];
|
|
1810
|
+
console.log(options.keepVolumes
|
|
1811
|
+
? "Stopping and removing containers (keeping volumes)..."
|
|
1812
|
+
: "Stopping and removing containers and volumes...");
|
|
1813
|
+
|
|
1814
|
+
const downCode = await runCompose(downArgs);
|
|
1815
|
+
if (downCode === 0) {
|
|
1816
|
+
console.log("✓ Monitoring services stopped and removed");
|
|
1779
1817
|
} else {
|
|
1780
|
-
console.log("
|
|
1818
|
+
console.log("⚠ Could not stop services (may not be running)");
|
|
1819
|
+
}
|
|
1820
|
+
|
|
1821
|
+
// Remove any orphaned containers that docker compose down missed
|
|
1822
|
+
await removeOrphanedContainers();
|
|
1823
|
+
console.log("✓ Removed orphaned containers");
|
|
1824
|
+
|
|
1825
|
+
// Remove orphaned volumes from previous installs with different project names
|
|
1826
|
+
if (!options.keepVolumes) {
|
|
1827
|
+
const volumePatterns = [
|
|
1828
|
+
"monitoring_grafana_data",
|
|
1829
|
+
"monitoring_postgres_ai_configs",
|
|
1830
|
+
"monitoring_sink_postgres_data",
|
|
1831
|
+
"monitoring_target_db_data",
|
|
1832
|
+
"monitoring_victoria_metrics_data",
|
|
1833
|
+
"postgres_ai_configs_grafana_data",
|
|
1834
|
+
"postgres_ai_configs_sink_postgres_data",
|
|
1835
|
+
"postgres_ai_configs_target_db_data",
|
|
1836
|
+
"postgres_ai_configs_victoria_metrics_data",
|
|
1837
|
+
"postgres_ai_configs_postgres_ai_configs",
|
|
1838
|
+
];
|
|
1839
|
+
|
|
1840
|
+
const { stdout: existingVolumes } = await execFilePromise("docker", ["volume", "ls", "-q"]);
|
|
1841
|
+
const volumeList = existingVolumes.trim().split('\n').filter(Boolean);
|
|
1842
|
+
const orphanedVolumes = volumeList.filter(v => volumePatterns.includes(v));
|
|
1843
|
+
|
|
1844
|
+
if (orphanedVolumes.length > 0) {
|
|
1845
|
+
let removedCount = 0;
|
|
1846
|
+
for (const vol of orphanedVolumes) {
|
|
1847
|
+
try {
|
|
1848
|
+
await execFilePromise("docker", ["volume", "rm", vol]);
|
|
1849
|
+
removedCount++;
|
|
1850
|
+
} catch {
|
|
1851
|
+
// Volume might be in use, skip silently
|
|
1852
|
+
}
|
|
1853
|
+
}
|
|
1854
|
+
if (removedCount > 0) {
|
|
1855
|
+
console.log(`✓ Removed ${removedCount} orphaned volume(s) from previous installs`);
|
|
1856
|
+
}
|
|
1857
|
+
}
|
|
1781
1858
|
}
|
|
1782
1859
|
|
|
1783
|
-
// Remove
|
|
1784
|
-
await execFilePromise("docker", ["volume", "prune", "-f"]);
|
|
1785
|
-
console.log("✓ Removed unused volumes");
|
|
1786
|
-
|
|
1787
|
-
// Remove unused networks
|
|
1860
|
+
// Remove any dangling resources
|
|
1788
1861
|
await execFilePromise("docker", ["network", "prune", "-f"]);
|
|
1789
1862
|
console.log("✓ Removed unused networks");
|
|
1790
1863
|
|
|
1791
|
-
// Remove dangling images
|
|
1792
1864
|
await execFilePromise("docker", ["image", "prune", "-f"]);
|
|
1793
1865
|
console.log("✓ Removed dangling images");
|
|
1794
1866
|
|
|
1795
|
-
console.log("\
|
|
1867
|
+
console.log("\n✓ Cleanup completed - ready for fresh install");
|
|
1796
1868
|
} catch (error) {
|
|
1797
1869
|
const message = error instanceof Error ? error.message : String(error);
|
|
1798
1870
|
console.error(`Error during cleanup: ${message}`);
|
package/bunfig.toml
CHANGED
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
# https://bun.sh/docs/runtime/bunfig
|
|
3
3
|
|
|
4
4
|
[test]
|
|
5
|
-
# Default timeout for all tests (
|
|
5
|
+
# Default timeout for all tests (60 seconds)
|
|
6
6
|
# Integration tests that connect to databases need longer timeouts
|
|
7
|
-
|
|
7
|
+
# PostgreSQL startup can take up to 30s in slow CI environments
|
|
8
|
+
timeout = 60000
|
|
8
9
|
|
|
9
10
|
# Coverage settings - enabled by default for test runs
|
|
10
11
|
coverage = true
|
package/dist/bin/postgres-ai.js
CHANGED
|
@@ -13064,7 +13064,7 @@ var {
|
|
|
13064
13064
|
// package.json
|
|
13065
13065
|
var package_default = {
|
|
13066
13066
|
name: "postgresai",
|
|
13067
|
-
version: "0.14.0-beta.
|
|
13067
|
+
version: "0.14.0-beta.9",
|
|
13068
13068
|
description: "postgres_ai CLI",
|
|
13069
13069
|
license: "Apache-2.0",
|
|
13070
13070
|
private: false,
|
|
@@ -15887,7 +15887,7 @@ var Result = import_lib.default.Result;
|
|
|
15887
15887
|
var TypeOverrides = import_lib.default.TypeOverrides;
|
|
15888
15888
|
var defaults = import_lib.default.defaults;
|
|
15889
15889
|
// package.json
|
|
15890
|
-
var version = "0.14.0-beta.
|
|
15890
|
+
var version = "0.14.0-beta.9";
|
|
15891
15891
|
var package_default2 = {
|
|
15892
15892
|
name: "postgresai",
|
|
15893
15893
|
version,
|
|
@@ -27182,8 +27182,12 @@ async function runCompose(args, grafanaPassword) {
|
|
|
27182
27182
|
}
|
|
27183
27183
|
}
|
|
27184
27184
|
}
|
|
27185
|
+
const finalArgs = [...args];
|
|
27186
|
+
if (process.platform === "darwin" && args.includes("up")) {
|
|
27187
|
+
finalArgs.push("--scale", "node-exporter=0");
|
|
27188
|
+
}
|
|
27185
27189
|
return new Promise((resolve6) => {
|
|
27186
|
-
const child = spawn2(cmd[0], [...cmd.slice(1), "-f", composeFile, ...
|
|
27190
|
+
const child = spawn2(cmd[0], [...cmd.slice(1), "-f", composeFile, ...finalArgs], {
|
|
27187
27191
|
stdio: "inherit",
|
|
27188
27192
|
env,
|
|
27189
27193
|
cwd: projectDir
|
|
@@ -27224,7 +27228,7 @@ mon.command("local-install").description("install local monitoring stack (genera
|
|
|
27224
27228
|
if (pwdMatch)
|
|
27225
27229
|
existingPassword = pwdMatch[1].trim();
|
|
27226
27230
|
}
|
|
27227
|
-
const imageTag = opts.tag || existingTag || package_default.version;
|
|
27231
|
+
const imageTag = opts.tag || process.env.PGAI_TAG || existingTag || package_default.version;
|
|
27228
27232
|
const envLines = [`PGAI_TAG=${imageTag}`];
|
|
27229
27233
|
if (existingRegistry) {
|
|
27230
27234
|
envLines.push(`PGAI_REGISTRY=${existingRegistry}`);
|
|
@@ -27546,6 +27550,28 @@ mon.command("start").description("start monitoring services").action(async () =>
|
|
|
27546
27550
|
if (code !== 0)
|
|
27547
27551
|
process.exitCode = code;
|
|
27548
27552
|
});
|
|
27553
|
+
var MONITORING_CONTAINERS = [
|
|
27554
|
+
"postgres-ai-config-init",
|
|
27555
|
+
"node-exporter",
|
|
27556
|
+
"cadvisor",
|
|
27557
|
+
"grafana-with-datasources",
|
|
27558
|
+
"sink-postgres",
|
|
27559
|
+
"sink-prometheus",
|
|
27560
|
+
"target-db",
|
|
27561
|
+
"pgwatch-postgres",
|
|
27562
|
+
"pgwatch-prometheus",
|
|
27563
|
+
"postgres-exporter-sink",
|
|
27564
|
+
"flask-pgss-api",
|
|
27565
|
+
"sources-generator",
|
|
27566
|
+
"postgres-reports"
|
|
27567
|
+
];
|
|
27568
|
+
async function removeOrphanedContainers() {
|
|
27569
|
+
for (const container of MONITORING_CONTAINERS) {
|
|
27570
|
+
try {
|
|
27571
|
+
await execFilePromise("docker", ["rm", "-f", container]);
|
|
27572
|
+
} catch {}
|
|
27573
|
+
}
|
|
27574
|
+
}
|
|
27549
27575
|
mon.command("stop").description("stop monitoring services").action(async () => {
|
|
27550
27576
|
const code = await runCompose(["down"]);
|
|
27551
27577
|
if (code !== 0)
|
|
@@ -27748,27 +27774,56 @@ Stopping services and removing data...`);
|
|
|
27748
27774
|
process.exitCode = 1;
|
|
27749
27775
|
}
|
|
27750
27776
|
});
|
|
27751
|
-
mon.command("clean").description("cleanup monitoring services artifacts").action(async () => {
|
|
27752
|
-
console.log(`Cleaning up
|
|
27777
|
+
mon.command("clean").description("cleanup monitoring services artifacts (stops services and removes volumes)").option("--keep-volumes", "keep data volumes (only stop and remove containers)").action(async (options) => {
|
|
27778
|
+
console.log(`Cleaning up monitoring services...
|
|
27753
27779
|
`);
|
|
27754
27780
|
try {
|
|
27755
|
-
const
|
|
27756
|
-
|
|
27757
|
-
|
|
27758
|
-
|
|
27759
|
-
|
|
27760
|
-
console.log("\u2713 Removed stopped containers");
|
|
27781
|
+
const downArgs = options.keepVolumes ? ["down"] : ["down", "-v"];
|
|
27782
|
+
console.log(options.keepVolumes ? "Stopping and removing containers (keeping volumes)..." : "Stopping and removing containers and volumes...");
|
|
27783
|
+
const downCode = await runCompose(downArgs);
|
|
27784
|
+
if (downCode === 0) {
|
|
27785
|
+
console.log("\u2713 Monitoring services stopped and removed");
|
|
27761
27786
|
} else {
|
|
27762
|
-
console.log("\
|
|
27787
|
+
console.log("\u26A0 Could not stop services (may not be running)");
|
|
27788
|
+
}
|
|
27789
|
+
await removeOrphanedContainers();
|
|
27790
|
+
console.log("\u2713 Removed orphaned containers");
|
|
27791
|
+
if (!options.keepVolumes) {
|
|
27792
|
+
const volumePatterns = [
|
|
27793
|
+
"monitoring_grafana_data",
|
|
27794
|
+
"monitoring_postgres_ai_configs",
|
|
27795
|
+
"monitoring_sink_postgres_data",
|
|
27796
|
+
"monitoring_target_db_data",
|
|
27797
|
+
"monitoring_victoria_metrics_data",
|
|
27798
|
+
"postgres_ai_configs_grafana_data",
|
|
27799
|
+
"postgres_ai_configs_sink_postgres_data",
|
|
27800
|
+
"postgres_ai_configs_target_db_data",
|
|
27801
|
+
"postgres_ai_configs_victoria_metrics_data",
|
|
27802
|
+
"postgres_ai_configs_postgres_ai_configs"
|
|
27803
|
+
];
|
|
27804
|
+
const { stdout: existingVolumes } = await execFilePromise("docker", ["volume", "ls", "-q"]);
|
|
27805
|
+
const volumeList = existingVolumes.trim().split(`
|
|
27806
|
+
`).filter(Boolean);
|
|
27807
|
+
const orphanedVolumes = volumeList.filter((v) => volumePatterns.includes(v));
|
|
27808
|
+
if (orphanedVolumes.length > 0) {
|
|
27809
|
+
let removedCount = 0;
|
|
27810
|
+
for (const vol of orphanedVolumes) {
|
|
27811
|
+
try {
|
|
27812
|
+
await execFilePromise("docker", ["volume", "rm", vol]);
|
|
27813
|
+
removedCount++;
|
|
27814
|
+
} catch {}
|
|
27815
|
+
}
|
|
27816
|
+
if (removedCount > 0) {
|
|
27817
|
+
console.log(`\u2713 Removed ${removedCount} orphaned volume(s) from previous installs`);
|
|
27818
|
+
}
|
|
27819
|
+
}
|
|
27763
27820
|
}
|
|
27764
|
-
await execFilePromise("docker", ["volume", "prune", "-f"]);
|
|
27765
|
-
console.log("\u2713 Removed unused volumes");
|
|
27766
27821
|
await execFilePromise("docker", ["network", "prune", "-f"]);
|
|
27767
27822
|
console.log("\u2713 Removed unused networks");
|
|
27768
27823
|
await execFilePromise("docker", ["image", "prune", "-f"]);
|
|
27769
27824
|
console.log("\u2713 Removed dangling images");
|
|
27770
27825
|
console.log(`
|
|
27771
|
-
Cleanup completed`);
|
|
27826
|
+
\u2713 Cleanup completed - ready for fresh install`);
|
|
27772
27827
|
} catch (error2) {
|
|
27773
27828
|
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
27774
27829
|
console.error(`Error during cleanup: ${message}`);
|
package/lib/metrics-embedded.ts
CHANGED
package/package.json
CHANGED
|
@@ -112,11 +112,12 @@ async function createTempPostgres(): Promise<TempPostgres> {
|
|
|
112
112
|
const cleanup = async () => {
|
|
113
113
|
postgresProc.kill("SIGTERM");
|
|
114
114
|
try {
|
|
115
|
+
// 30s timeout to handle slower CI environments gracefully
|
|
115
116
|
await waitFor(
|
|
116
117
|
async () => {
|
|
117
118
|
if (postgresProc.exitCode === null) throw new Error("still running");
|
|
118
119
|
},
|
|
119
|
-
{ timeoutMs:
|
|
120
|
+
{ timeoutMs: 30000, intervalMs: 100 }
|
|
120
121
|
);
|
|
121
122
|
} catch {
|
|
122
123
|
postgresProc.kill("SIGKILL");
|
|
@@ -130,11 +131,11 @@ async function createTempPostgres(): Promise<TempPostgres> {
|
|
|
130
131
|
return c;
|
|
131
132
|
};
|
|
132
133
|
|
|
133
|
-
// Wait for Postgres to start
|
|
134
|
+
// Wait for Postgres to start (30s timeout for slower CI environments)
|
|
134
135
|
await waitFor(async () => {
|
|
135
136
|
const c = await connect();
|
|
136
137
|
await c.end();
|
|
137
|
-
});
|
|
138
|
+
}, { timeoutMs: 30000, intervalMs: 100 });
|
|
138
139
|
|
|
139
140
|
return { port, socketDir, cleanup, connect };
|
|
140
141
|
}
|
|
@@ -171,15 +172,16 @@ describe.skipIf(!!skipReason)("checkup integration: express mode schema compatib
|
|
|
171
172
|
let pg: TempPostgres;
|
|
172
173
|
let client: Client;
|
|
173
174
|
|
|
175
|
+
// 60s timeout for hooks - PostgreSQL startup can take 30s+ in slow CI
|
|
174
176
|
beforeAll(async () => {
|
|
175
177
|
pg = await createTempPostgres();
|
|
176
178
|
client = await pg.connect();
|
|
177
|
-
});
|
|
179
|
+
}, { timeout: 60000 });
|
|
178
180
|
|
|
179
181
|
afterAll(async () => {
|
|
180
182
|
if (client) await client.end();
|
|
181
183
|
if (pg) await pg.cleanup();
|
|
182
|
-
});
|
|
184
|
+
}, { timeout: 60000 });
|
|
183
185
|
|
|
184
186
|
// Test all checks supported by express mode
|
|
185
187
|
const expressChecks = Object.keys(checkup.CHECK_INFO);
|
|
@@ -118,11 +118,12 @@ async function createTempPostgres(): Promise<TempPostgres> {
|
|
|
118
118
|
const cleanup = async () => {
|
|
119
119
|
postgresProc.kill("SIGTERM");
|
|
120
120
|
try {
|
|
121
|
+
// 30s timeout to handle slower CI environments gracefully
|
|
121
122
|
await waitFor(
|
|
122
123
|
async () => {
|
|
123
124
|
if (postgresProc.exitCode === null) throw new Error("still running");
|
|
124
125
|
},
|
|
125
|
-
{ timeoutMs:
|
|
126
|
+
{ timeoutMs: 30000, intervalMs: 100 }
|
|
126
127
|
);
|
|
127
128
|
} catch {
|
|
128
129
|
postgresProc.kill("SIGKILL");
|
|
@@ -136,10 +137,11 @@ async function createTempPostgres(): Promise<TempPostgres> {
|
|
|
136
137
|
return c;
|
|
137
138
|
};
|
|
138
139
|
|
|
140
|
+
// Wait for Postgres to start (30s timeout for slower CI environments)
|
|
139
141
|
await waitFor(async () => {
|
|
140
142
|
const c = await connectLocal();
|
|
141
143
|
await c.end();
|
|
142
|
-
});
|
|
144
|
+
}, { timeoutMs: 30000, intervalMs: 100 });
|
|
143
145
|
|
|
144
146
|
const postgresPassword = "postgrespw";
|
|
145
147
|
{
|