Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/mongo-authsource-and-atlas-errors.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@asksql/mongodb": patch
---

Default the separate user/password `authSource` to `admin` (fixes authentication for root/Atlas users, who don't live in the query database; overridable via a new `authSource` option), and give clearer connection errors - an Atlas IP allow-list hint on a TLS/timeout failure, and a note about the `<password>` placeholder brackets on an auth failure.
6 changes: 3 additions & 3 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ name: API Docs

on:
push:
# develop is the default branch (github-pages env allows it); it's fast-forwarded to main
# after every release, so the docs track the released code.
branches: [develop, main]
# Deploy only from develop: it's the default branch the github-pages env allows, and it's
# fast-forwarded to main after every release, so the docs still track the released code.
branches: [develop]
paths:
- 'packages/*/src/**'
- 'typedoc.json'
Expand Down
131 changes: 131 additions & 0 deletions .github/workflows/jetbrains-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
name: JetBrains Plugin CI

on:
push:
branches: [main, develop]
paths:
- 'packages/jetbrains/**'
- '.github/workflows/jetbrains-ci.yml'
- 'pnpm-workspace.yaml'
- '.changeset/config.json'
pull_request:
paths:
- 'packages/jetbrains/**'
- '.github/workflows/jetbrains-ci.yml'
- 'pnpm-workspace.yaml'
- '.changeset/config.json'

jobs:
isolation-guard:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- name: Verify packages/jetbrains stays outside the pnpm workspace
run: |
set -e
if [ -f packages/jetbrains/package.json ]; then
echo "::error::packages/jetbrains/package.json must not exist — it would join the pnpm workspace glob."
exit 1
fi
# pnpm-workspace.yaml's 'packages/*' glob is single-level, so a
# nested manifest like packages/jetbrains/tools/parity/package.json
# is correctly outside it — a two-level (or deeper) pattern would
# pull that in. This checks the invariant directly instead of just
# trusting the current glob depth.
if grep -qE '^\s*-\s*.packages/\*\*' pnpm-workspace.yaml; then
echo "::error::pnpm-workspace.yaml's packages glob went recursive (packages/**) — this would pull packages/jetbrains/tools/parity's own package.json into the workspace."
exit 1
fi
# This check is scoped to what jetbrains isolation actually needs
# (no npm package of ITS OWN ever gets changesets-ignored as a
# workaround for not being a workspace member) — NOT a blanket
# "ignore must be empty forever" rule. That broader rule used to
# live here but coupled jetbrains CI's health to unrelated
# vscode-side changeset config, which is a legitimate, separate
# package's own business.
if jq -e '.ignore // [] | any(test("jetbrains"; "i"))' .changeset/config.json > /dev/null; then
echo "::error::.changeset/config.json 'ignore' references a jetbrains package — packages/jetbrains must stay invisible to changesets via having no package.json, never via an ignore entry."
exit 1
fi
echo "Isolation checks passed."

build-and-test:
needs: isolation-guard
runs-on: ubuntu-latest
defaults:
run:
working-directory: packages/jetbrains
steps:
- uses: actions/checkout@v5
- uses: actions/setup-java@v5
with:
distribution: temurin
java-version: 21
- uses: gradle/actions/setup-gradle@v6
- run: ./gradlew test buildPlugin verifyPlugin --stacktrace
- name: Upload plugin zip
uses: actions/upload-artifact@v4
with:
name: asksql-jetbrains-plugin
path: packages/jetbrains/build/distributions/*.zip
if-no-files-found: error

integration-test:
needs: isolation-guard
runs-on: ubuntu-latest
defaults:
run:
working-directory: packages/jetbrains
steps:
- uses: actions/checkout@v5
- uses: actions/setup-java@v5
with:
distribution: temurin
java-version: 21
- uses: gradle/actions/setup-gradle@v6
- run: ./gradlew test -PintegrationTests=true --stacktrace
env:
# Rootful Docker on the runner; skip the rootless strategy, whose JNA collides with the IntelliJ Platform's bundled jnidispatch.
TESTCONTAINERS_DOCKER_CLIENT_STRATEGY: org.testcontainers.dockerclient.UnixSocketClientProviderStrategy

parity:
needs: isolation-guard
runs-on: ubuntu-latest
defaults:
run:
working-directory: packages/jetbrains/tools/parity
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v5
with:
node-version: 22
# setup-node@v5 defaults package-manager-cache:true, which detects the repo-root pnpm and runs it; this isolated npm-only dir has no pnpm installed.
package-manager-cache: false
- run: npm install --no-audit --no-fund
- run: node export-vectors.mjs
- name: Fail if generated vectors differ from committed ones
run: |
git diff --exit-code -- vectors/ || {
echo "::error::Golden parity vectors changed — a new @asksql/core release altered guard/prompt behavior. Port the change, regenerate, and commit the new vectors."
exit 1
}

root-monorepo-unaffected:
needs: isolation-guard
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: pnpm/action-setup@v5
- uses: actions/setup-node@v5
with:
node-version: 22
cache: pnpm
- name: pnpm install must be byte-identical to before this change
run: |
pnpm install --frozen-lockfile
git diff --exit-code -- pnpm-lock.yaml || {
echo "::error::pnpm-lock.yaml changed — packages/jetbrains must be invisible to the pnpm workspace."
exit 1
}
- run: pnpm build
- run: pnpm test
68 changes: 68 additions & 0 deletions .github/workflows/jetbrains-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
name: JetBrains Plugin Release

on:
push:
tags:
- 'jetbrains-v*'
workflow_dispatch:

jobs:
publish:
runs-on: ubuntu-latest
environment: jetbrains-publish
permissions:
contents: write
defaults:
run:
working-directory: packages/jetbrains
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0

- name: Verify release tag is on main and matches the plugin version
env:
REF_NAME: ${{ github.ref_name }}
REF_TYPE: ${{ github.ref_type }}
run: |
if [ "$REF_TYPE" != "tag" ]; then
echo "::error::This workflow must be dispatched against a jetbrains-v* TAG (use the 'Use workflow from' tag picker), not a branch — otherwise there is no tag to attach the signed zip to after Marketplace publish."
exit 1
fi
git fetch origin main --quiet
if ! git merge-base --is-ancestor "${{ github.sha }}" origin/main; then
echo "::error::Tag $REF_NAME (${{ github.sha }}) is not on main; refusing to publish."
exit 1
fi
tag_version="${REF_NAME#jetbrains-v}"
plugin_version="$(grep -m1 '^pluginVersion' gradle.properties | cut -d= -f2 | tr -d '[:space:]')"
if [ "$tag_version" != "$plugin_version" ]; then
echo "::error::Tag $REF_NAME (version $tag_version) does not match gradle.properties pluginVersion ($plugin_version); refusing to publish a mismatched version."
exit 1
fi
echo "Release tag $REF_NAME is on main; proceeding."

- uses: actions/setup-java@v5
with:
distribution: temurin
java-version: 21
- uses: gradle/actions/setup-gradle@v6

- name: Build, sign, and verify
run: ./gradlew buildPlugin signPlugin verifyPlugin --stacktrace
env:
ASKSQL_VERIFY_FULL: "true" # verify the full cross-IDE matrix at release (per-push CI verifies IC floor+latest only)
CERTIFICATE_CHAIN: ${{ secrets.JETBRAINS_CERTIFICATE_CHAIN }}
PRIVATE_KEY: ${{ secrets.JETBRAINS_PRIVATE_KEY }}
PRIVATE_KEY_PASSWORD: ${{ secrets.JETBRAINS_PRIVATE_KEY_PASSWORD }}

- name: Publish to JetBrains Marketplace
run: ./gradlew publishPlugin --stacktrace
env:
PUBLISH_TOKEN: ${{ secrets.JETBRAINS_PUBLISH_TOKEN }}

- name: Attach signed zip to the GitHub Release
uses: softprops/action-gh-release@v2
with:
files: packages/jetbrains/build/distributions/*-signed.zip
fail_on_unmatched_files: true
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
node_modules/
dist/
coverage/
docs/api/
*.log
*.tgz
.env
Expand All @@ -20,3 +21,4 @@ internal/

# Claude Code local settings (personal, never commit)
.claude/settings.local.json
.claude/settings.json
12 changes: 12 additions & 0 deletions packages/core/test/mongo-engine.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,18 @@ describe('mongo engine happy path', () => {
expect(out).toEqual(RESULT);
expect(conn.aggregateCalls[0]!.collection).toBe('orders');
});

it('execute re-guards a hand-edited pipeline and blocks a write stage', async () => {
const conn = new FakeMongo();
const engine = createMongoAskSql({ connector: conn, model: model(['']) });
await expect(engine.execute('[{"$out": "evil"}]', 'orders')).rejects.toMatchObject({ code: 'GUARD_BLOCKED' });
expect(conn.aggregateCalls).toHaveLength(0);
});

it('execute rejects an unknown collection', async () => {
const engine = createMongoAskSql({ connector: new FakeMongo(), model: model(['']) });
await expect(engine.execute('[{"$match": {}}]', 'does_not_exist')).rejects.toMatchObject({ code: 'DB_QUERY_ERROR' });
});
});

describe('mongo engine floors and repair', () => {
Expand Down
15 changes: 15 additions & 0 deletions packages/jetbrains/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
build/
.gradle/
.intellijPlatform/
.kotlin/
out/
*.iml
.idea/

# Generated at build time by the buildSidecar-equivalent (none for this
# in-process plugin) and by the license-report task.
THIRD-PARTY-NOTICES.txt

# Parity tooling is plain Node - its own node_modules never joins the pnpm
# workspace (no package.json above tools/parity/).
tools/parity/node_modules/
20 changes: 20 additions & 0 deletions packages/jetbrains/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Changelog

All notable changes to the AskSQL JetBrains plugin are documented here.
Format loosely follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

## [Unreleased]

## [0.1.0] - 2026-07-18

First release. Chat and Schema tool windows, pure Kotlin/JVM engine (JSqlParser guard, JDBC
connectivity for Postgres/MySQL/SQLite/DuckDB/Oracle, MongoDB via a separate `MongoEnginePipeline`,
OpenAI-compatible/Anthropic/Gemini streaming clients incl. NVIDIA/Groq/local-model presets),
PasswordSafe-backed secrets, sample-database and DuckDB file-upload onboarding, connection editor
with per-engine validation and a Test Connection button, and "Explain"/"Suggest a fix" actions.

### Highlights
- Read-only by construction: an AST guard plus an enforced read-only DB session on every query
(allowlist-based `MongoGuard` for MongoDB, which has no server-enforced equivalent).
- Zero telemetry; secrets only ever live in the OS keychain.
- CI parity-tested against the published `@asksql/core` guard/prompt behavior.
Loading
Loading