@@ -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+
136192cli: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
214265cli:node:e2e:dind :
215266 stage : test
0 commit comments