Skip to content

Commit e9fd559

Browse files
committed
Merge branch 'fix/cli-init-ssl-support' into 'main'
fix: CLI sslmode=prefer behavior, CI publish improvements, and schema fixes See merge request postgres-ai/postgres_ai!108
2 parents 7dbe2ed + 0231628 commit e9fd559

File tree

7 files changed

+458
-119
lines changed

7 files changed

+458
-119
lines changed

.cursor

Submodule .cursor updated from 735d2bf to f20a54c

.gitlab-ci.yml

Lines changed: 105 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,62 @@ cli:node:tests:
133133
rules:
134134
- if: '$CI_COMMIT_BRANCH'
135135

136+
# Shared script for version parsing (used by both publish jobs)
137+
.cli_npm_version_script: &cli_npm_version_script |
138+
set -euo pipefail
139+
: "${NPM_TOKEN:?NPM_TOKEN is required to publish}"
140+
141+
# Supported tag formats (examples):
142+
# - v0.14.0-dev.1
143+
# - 0.14.0-dev.1
144+
# - 0.14-dev.1 (normalized to 0.14.0-dev.1)
145+
# - 0.14.0-rc.1
146+
# - v0.14.0 (stable, published to latest)
147+
RAW_TAG="${CI_COMMIT_TAG:-${CI_COMMIT_REF_NAME:-}}"
148+
if [ -z "$RAW_TAG" ]; then
149+
echo "CI_COMMIT_TAG is empty"
150+
exit 1
151+
fi
152+
153+
TAG_VERSION="$RAW_TAG"
154+
TAG_VERSION="${TAG_VERSION#v}"
155+
156+
# Normalize 0.14-dev.1 -> 0.14.0-dev.1 (also for beta/rc)
157+
if printf '%s' "$TAG_VERSION" | grep -Eq '^[0-9]+\.[0-9]+-(dev|beta|rc)\.[0-9]+$'; then
158+
TAG_VERSION="$(printf '%s' "$TAG_VERSION" | sed -E 's/^([0-9]+\.[0-9]+)-((dev|beta|rc)\.[0-9]+)$/\1.0-\2/')"
159+
fi
160+
161+
DIST_TAG=""
162+
if printf '%s' "$TAG_VERSION" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+-(dev|beta|rc)\.[0-9]+$'; then
163+
DIST_TAG="$(printf '%s' "$TAG_VERSION" | sed -E 's/^.*-(dev|beta|rc)\.[0-9]+$/\1/')"
164+
elif printf '%s' "$TAG_VERSION" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+$'; then
165+
DIST_TAG="latest"
166+
fi
167+
168+
if [ "$DIST_TAG" != "dev" ] && [ "$DIST_TAG" != "beta" ] && [ "$DIST_TAG" != "rc" ] && [ "$DIST_TAG" != "latest" ]; then
169+
echo "Unsupported tag/version: $TAG_VERSION (expected X.Y.Z, X.Y.Z-dev.N, X.Y.Z-beta.N, or X.Y.Z-rc.N)"
170+
exit 1
171+
fi
172+
173+
# Configure npm auth without committing credentials
174+
printf "//registry.npmjs.org/:_authToken=%s\n" "$NPM_TOKEN" > ~/.npmrc
175+
176+
# Helper: check if package version exists on npm
177+
pkg_exists() {
178+
npm view "$1@$2" version >/dev/null 2>&1
179+
}
180+
181+
.cli_npm_publish_rules:
182+
rules:
183+
# prereleases (also allow patchless X.Y-dev.N, normalized in script)
184+
- if: '$CI_COMMIT_TAG =~ /^v?\d+\.\d+(\.\d+)?-(dev|beta|rc)\.\d+$/'
185+
# stable releases
186+
- if: '$CI_COMMIT_TAG =~ /^v?\d+\.\d+\.\d+$/'
187+
# GitLab "Run pipeline" UI ("web" pipelines) often doesn't populate tag vars consistently.
188+
# Keep this job available manually so the pipeline is never empty; the script validates the ref name.
189+
- if: '$CI_PIPELINE_SOURCE == "web"'
190+
when: manual
191+
136192
cli:npm:publish:
137193
stage: publish
138194
image: node:20-bullseye
@@ -144,72 +200,67 @@ cli:npm:publish:
144200
- corepack enable || true
145201
- node -v && npm -v
146202
script:
203+
- *cli_npm_version_script
147204
- |
148-
set -euo pipefail
149-
: "${NPM_TOKEN:?NPM_TOKEN is required to publish}"
150-
151-
# Supported tag formats (examples):
152-
# - v0.14.0-dev.1
153-
# - 0.14.0-dev.1
154-
# - 0.14-dev.1 (normalized to 0.14.0-dev.1)
155-
# - 0.14.0-rc.1
156-
# - v0.14.0 (stable, published to latest)
157-
RAW_TAG="${CI_COMMIT_TAG:-${CI_COMMIT_REF_NAME:-}}"
158-
if [ -z "$RAW_TAG" ]; then
159-
echo "CI_COMMIT_TAG is empty"
160-
exit 1
161-
fi
162-
163-
TAG_VERSION="$RAW_TAG"
164-
TAG_VERSION="${TAG_VERSION#v}"
165-
166-
# Normalize 0.14-dev.1 -> 0.14.0-dev.1 (also for beta/rc)
167-
if printf '%s' "$TAG_VERSION" | grep -Eq '^[0-9]+\.[0-9]+-(dev|beta|rc)\.[0-9]+$'; then
168-
TAG_VERSION="$(printf '%s' "$TAG_VERSION" | sed -E 's/^([0-9]+\.[0-9]+)-((dev|beta|rc)\.[0-9]+)$/\1.0-\2/')"
169-
fi
170-
171-
DIST_TAG=""
172-
if printf '%s' "$TAG_VERSION" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+-(dev|beta|rc)\.[0-9]+$'; then
173-
DIST_TAG="$(printf '%s' "$TAG_VERSION" | sed -E 's/^.*-(dev|beta|rc)\.[0-9]+$/\1/')"
174-
elif printf '%s' "$TAG_VERSION" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+$'; then
175-
DIST_TAG="latest"
176-
fi
177-
178-
if [ "$DIST_TAG" != "dev" ] && [ "$DIST_TAG" != "beta" ] && [ "$DIST_TAG" != "rc" ] && [ "$DIST_TAG" != "latest" ]; then
179-
echo "Unsupported tag/version: $TAG_VERSION (expected X.Y.Z, X.Y.Z-dev.N, X.Y.Z-beta.N, or X.Y.Z-rc.N)"
180-
exit 1
181-
fi
182-
183-
# Configure npm auth without committing credentials
184-
printf "//registry.npmjs.org/:_authToken=%s\n" "$NPM_TOKEN" > ~/.npmrc
185-
186205
echo "Publishing postgresai@${TAG_VERSION} to dist-tag ${DIST_TAG}"
187206
cd cli
188207
npm ci --no-audit --no-fund
189-
# Make the git tag the source of truth without committing version bumps.
190208
npm version --no-git-tag-version "$TAG_VERSION"
191209
npm run build
192-
npm publish --tag "$DIST_TAG" --access public
210+
if pkg_exists "postgresai" "$TAG_VERSION"; then
211+
echo "postgresai@${TAG_VERSION} already published, skipping"
212+
else
213+
npm publish --tag "$DIST_TAG" --access public
214+
fi
215+
after_script:
216+
- rm -f ~/.npmrc
217+
extends: .cli_npm_publish_rules
218+
219+
cli:npm:publish-wrapper:
220+
stage: publish
221+
image: node:20-bullseye
222+
needs: [cli:npm:publish]
223+
variables:
224+
GIT_STRATEGY: fetch
225+
NPM_CONFIG_AUDIT: "false"
226+
NPM_CONFIG_FUND: "false"
227+
before_script:
228+
- corepack enable || true
229+
- node -v && npm -v
230+
script:
231+
- *cli_npm_version_script
232+
- |
233+
# Wait for postgresai to be available on npm (max 60s)
234+
wait_for_pkg() {
235+
local pkg="$1" ver="$2" i=0
236+
while [ $i -lt 12 ]; do
237+
if pkg_exists "$pkg" "$ver"; then
238+
echo "✓ $pkg@$ver is available on npm"
239+
return 0
240+
fi
241+
echo "Waiting for $pkg@$ver to be available on npm..."
242+
sleep 5
243+
i=$((i + 1))
244+
done
245+
echo "✗ Timeout waiting for $pkg@$ver"
246+
return 1
247+
}
248+
249+
wait_for_pkg "postgresai" "$TAG_VERSION"
193250
194251
echo "Publishing pgai@${TAG_VERSION} (wrapper) to dist-tag ${DIST_TAG}"
195-
cd ../pgai
196-
# Update version + dependency so `npx pgai@<tag>` pulls the matching postgresai version.
252+
cd pgai
197253
npm pkg set "dependencies.postgresai=$TAG_VERSION"
198254
npm version --no-git-tag-version "$TAG_VERSION"
199-
# No lockfile: use npm install here.
200-
npm install --no-audit --no-fund
201-
npm publish --tag "$DIST_TAG" --access public
255+
if pkg_exists "pgai" "$TAG_VERSION"; then
256+
echo "pgai@${TAG_VERSION} already published, skipping"
257+
else
258+
npm install --no-audit --no-fund
259+
npm publish --tag "$DIST_TAG" --access public
260+
fi
202261
after_script:
203262
- rm -f ~/.npmrc
204-
rules:
205-
# prereleases (also allow patchless X.Y-dev.N, normalized in script)
206-
- if: '$CI_COMMIT_TAG =~ /^v?\d+\.\d+(\.\d+)?-(dev|beta|rc)\.\d+$/'
207-
# stable releases
208-
- if: '$CI_COMMIT_TAG =~ /^v?\d+\.\d+\.\d+$/'
209-
# GitLab "Run pipeline" UI ("web" pipelines) often doesn't populate tag vars consistently.
210-
# Keep this job available manually so the pipeline is never empty; the script validates the ref name.
211-
- if: '$CI_PIPELINE_SOURCE == "web"'
212-
when: manual
263+
extends: .cli_npm_publish_rules
213264

214265
cli:node:e2e:dind:
215266
stage: test

cli/bin/postgres-ai.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import { Client } from "pg";
1616
import { startMcpServer } from "../lib/mcp-server";
1717
import { fetchIssues, fetchIssueComments, createIssueComment, fetchIssue } from "../lib/issues";
1818
import { resolveBaseUrls } from "../lib/util";
19-
import { applyInitPlan, buildInitPlan, DEFAULT_MONITORING_USER, redactPasswordsInSql, resolveAdminConnection, resolveMonitoringPassword, verifyInitSetup } from "../lib/init";
19+
import { applyInitPlan, buildInitPlan, connectWithSslFallback, DEFAULT_MONITORING_USER, redactPasswordsInSql, resolveAdminConnection, resolveMonitoringPassword, verifyInitSetup } from "../lib/init";
2020

2121
const execPromise = promisify(exec);
2222
const execFilePromise = promisify(execFile);
@@ -151,9 +151,15 @@ program
151151
" If auto-generated, it is printed only on TTY by default.",
152152
" To print it in non-interactive mode: --print-password",
153153
"",
154+
"SSL connection (sslmode=prefer behavior):",
155+
" Tries SSL first, falls back to non-SSL if server doesn't support it.",
156+
" To force SSL: PGSSLMODE=require or ?sslmode=require in URL",
157+
" To disable SSL: PGSSLMODE=disable or ?sslmode=disable in URL",
158+
"",
154159
"Environment variables (libpq standard):",
155160
" PGHOST, PGPORT, PGUSER, PGDATABASE — connection defaults",
156161
" PGPASSWORD — admin password",
162+
" PGSSLMODE — SSL mode (disable, require, verify-full)",
157163
" PGAI_MON_PASSWORD — monitoring password",
158164
"",
159165
"Inspect SQL without applying changes:",
@@ -265,8 +271,8 @@ program
265271
// Use native pg client instead of requiring psql to be installed
266272
let client: Client | undefined;
267273
try {
268-
client = new Client(adminConn.clientConfig);
269-
await client.connect();
274+
const connResult = await connectWithSslFallback(Client, adminConn);
275+
client = connResult.client;
270276

271277
const dbRes = await client.query("select current_database() as db");
272278
const database = dbRes.rows?.[0]?.db;

0 commit comments

Comments
 (0)