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
54 changes: 46 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ jobs:
sudo apt-get install -y clang-21 lld-21 llvm-21-dev \
cmake make gcc g++ \
autoconf automake libtool \
libzstd-dev zlib1g-dev libsqlite3-dev libcurl4-openssl-dev
libzstd-dev zlib1g-dev libsqlite3-dev libcurl4-openssl-dev libpq-dev
sudo ln -sf /usr/bin/clang-21 /usr/bin/clang
sudo ln -sf /usr/bin/llvm-config-21 /usr/bin/llvm-config
- run: npm install
Expand Down Expand Up @@ -166,6 +166,24 @@ jobs:
build-linux-glibc:
runs-on: ubuntu-22.04

services:
postgres:
image: postgres:16
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: test
POSTGRES_DB: chadtest
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5

env:
PG_TESTS_ENABLED: 1

steps:
- uses: actions/checkout@v4

Expand All @@ -182,7 +200,7 @@ jobs:
sudo apt-get install -y clang-21 lld-21 llvm-21-dev \
cmake make gcc g++ \
autoconf automake libtool \
libzstd-dev zlib1g-dev libsqlite3-dev libcurl4-openssl-dev
libzstd-dev zlib1g-dev libsqlite3-dev libcurl4-openssl-dev libpq-dev
sudo ln -sf /usr/bin/clang-21 /usr/bin/clang
sudo ln -sf /usr/bin/llvm-config-21 /usr/bin/llvm-config
clang --version 2>&1 | head -2
Expand All @@ -207,7 +225,7 @@ jobs:
- name: Verify vendor libraries
run: |
fail=0
for lib in vendor/bdwgc/libgc.a vendor/yyjson/libyyjson.a vendor/libuv/build/libuv.a vendor/picohttpparser/picohttpparser.o c_bridges/lws-bridge.o c_bridges/multipart-bridge.o c_bridges/regex-bridge.o c_bridges/child-process-bridge.o c_bridges/child-process-spawn.o c_bridges/os-bridge.o c_bridges/strlen-cache.o c_bridges/time-bridge.o c_bridges/base64-bridge.o c_bridges/url-bridge.o c_bridges/uri-bridge.o c_bridges/dotenv-bridge.o c_bridges/watch-bridge.o c_bridges/arena-bridge.o c_bridges/curl-bridge.o c_bridges/compress-bridge.o c_bridges/yaml-bridge.o c_bridges/string-ops-bridge.o c_bridges/llvm-bridge.o c_bridges/llvm-builder-bridge.o c_bridges/lld-bridge.o; do
for lib in vendor/bdwgc/libgc.a vendor/yyjson/libyyjson.a vendor/libuv/build/libuv.a vendor/picohttpparser/picohttpparser.o c_bridges/lws-bridge.o c_bridges/multipart-bridge.o c_bridges/regex-bridge.o c_bridges/child-process-bridge.o c_bridges/child-process-spawn.o c_bridges/os-bridge.o c_bridges/strlen-cache.o c_bridges/time-bridge.o c_bridges/base64-bridge.o c_bridges/url-bridge.o c_bridges/uri-bridge.o c_bridges/dotenv-bridge.o c_bridges/watch-bridge.o c_bridges/arena-bridge.o c_bridges/curl-bridge.o c_bridges/pg-bridge.o c_bridges/compress-bridge.o c_bridges/yaml-bridge.o c_bridges/string-ops-bridge.o c_bridges/llvm-bridge.o c_bridges/llvm-builder-bridge.o c_bridges/lld-bridge.o; do
if [ ! -f "$lib" ]; then
echo "MISSING: $lib"
fail=1
Expand Down Expand Up @@ -276,7 +294,7 @@ jobs:
cp c_bridges/os-bridge.o c_bridges/strlen-cache.o release/lib/
cp c_bridges/time-bridge.o c_bridges/base64-bridge.o c_bridges/url-bridge.o c_bridges/uri-bridge.o release/lib/
cp c_bridges/dotenv-bridge.o release/lib/
cp c_bridges/watch-bridge.o c_bridges/arena-bridge.o c_bridges/curl-bridge.o c_bridges/compress-bridge.o c_bridges/yaml-bridge.o c_bridges/string-ops-bridge.o c_bridges/llvm-bridge.o c_bridges/llvm-builder-bridge.o c_bridges/lld-bridge.o release/lib/
cp c_bridges/watch-bridge.o c_bridges/arena-bridge.o c_bridges/curl-bridge.o c_bridges/pg-bridge.o c_bridges/compress-bridge.o c_bridges/yaml-bridge.o c_bridges/string-ops-bridge.o c_bridges/llvm-bridge.o c_bridges/llvm-builder-bridge.o c_bridges/lld-bridge.o release/lib/
tar -czf chadscript-linux-x64.tar.gz -C release chad lib

- name: Upload artifact
Expand All @@ -294,6 +312,9 @@ jobs:
build-macos:
runs-on: macos-latest

env:
PG_TESTS_ENABLED: 1

steps:
- uses: actions/checkout@v4

Expand All @@ -304,10 +325,27 @@ jobs:

- name: Install system dependencies
run: |
brew install llvm lld cmake autoconf automake libtool zstd
brew install llvm lld cmake autoconf automake libtool zstd libpq postgresql@16
echo "/opt/homebrew/opt/llvm/bin" >> $GITHUB_PATH
echo "/opt/homebrew/opt/postgresql@16/bin" >> $GITHUB_PATH
echo "/usr/local/opt/llvm/bin" >> $GITHUB_PATH

- name: Start and provision postgres
run: |
brew services start postgresql@16
export PATH="/opt/homebrew/opt/postgresql@16/bin:$PATH"
for i in $(seq 1 30); do
if pg_isready -h localhost -p 5432 2>/dev/null; then
echo "postgres ready"
break
fi
sleep 1
done
psql postgres -c "CREATE USER postgres SUPERUSER PASSWORD 'test';" || echo "postgres user already exists"
psql postgres -c "ALTER USER postgres WITH PASSWORD 'test';"
psql postgres -c "CREATE DATABASE chadtest OWNER postgres;" || echo "chadtest db already exists"
psql -h localhost -p 5432 -U postgres -d chadtest -c "SELECT 1;" postgresql://postgres:test@localhost:5432/chadtest

- name: Install npm dependencies
run: npm install

Expand All @@ -328,7 +366,7 @@ jobs:
- name: Verify vendor libraries
run: |
fail=0
for lib in vendor/bdwgc/libgc.a vendor/yyjson/libyyjson.a vendor/libuv/build/libuv.a vendor/picohttpparser/picohttpparser.o c_bridges/lws-bridge.o c_bridges/multipart-bridge.o c_bridges/regex-bridge.o c_bridges/child-process-bridge.o c_bridges/child-process-spawn.o c_bridges/os-bridge.o c_bridges/strlen-cache.o c_bridges/time-bridge.o c_bridges/base64-bridge.o c_bridges/url-bridge.o c_bridges/uri-bridge.o c_bridges/dotenv-bridge.o c_bridges/watch-bridge.o c_bridges/arena-bridge.o c_bridges/curl-bridge.o c_bridges/compress-bridge.o c_bridges/yaml-bridge.o c_bridges/string-ops-bridge.o c_bridges/llvm-bridge.o c_bridges/llvm-builder-bridge.o c_bridges/lld-bridge.o; do
for lib in vendor/bdwgc/libgc.a vendor/yyjson/libyyjson.a vendor/libuv/build/libuv.a vendor/picohttpparser/picohttpparser.o c_bridges/lws-bridge.o c_bridges/multipart-bridge.o c_bridges/regex-bridge.o c_bridges/child-process-bridge.o c_bridges/child-process-spawn.o c_bridges/os-bridge.o c_bridges/strlen-cache.o c_bridges/time-bridge.o c_bridges/base64-bridge.o c_bridges/url-bridge.o c_bridges/uri-bridge.o c_bridges/dotenv-bridge.o c_bridges/watch-bridge.o c_bridges/arena-bridge.o c_bridges/curl-bridge.o c_bridges/pg-bridge.o c_bridges/compress-bridge.o c_bridges/yaml-bridge.o c_bridges/string-ops-bridge.o c_bridges/llvm-bridge.o c_bridges/llvm-builder-bridge.o c_bridges/lld-bridge.o; do
if [ ! -f "$lib" ]; then
echo "MISSING: $lib"
fail=1
Expand Down Expand Up @@ -401,7 +439,7 @@ jobs:
cp c_bridges/os-bridge.o c_bridges/strlen-cache.o release/lib/
cp c_bridges/time-bridge.o c_bridges/base64-bridge.o c_bridges/url-bridge.o c_bridges/uri-bridge.o release/lib/
cp c_bridges/dotenv-bridge.o release/lib/
cp c_bridges/watch-bridge.o c_bridges/arena-bridge.o c_bridges/curl-bridge.o c_bridges/compress-bridge.o c_bridges/yaml-bridge.o c_bridges/string-ops-bridge.o c_bridges/llvm-bridge.o c_bridges/llvm-builder-bridge.o c_bridges/lld-bridge.o release/lib/
cp c_bridges/watch-bridge.o c_bridges/arena-bridge.o c_bridges/curl-bridge.o c_bridges/pg-bridge.o c_bridges/compress-bridge.o c_bridges/yaml-bridge.o c_bridges/string-ops-bridge.o c_bridges/llvm-bridge.o c_bridges/llvm-builder-bridge.o c_bridges/lld-bridge.o release/lib/
tar -czf chadscript-macos-arm64.tar.gz -C release chad lib

- name: Upload artifact
Expand Down Expand Up @@ -436,7 +474,7 @@ jobs:
wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
echo "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-21 main" | sudo tee /etc/apt/sources.list.d/llvm-21.list
sudo apt-get update
sudo apt-get install -y clang-21 libsqlite3-dev libzstd-dev zlib1g-dev
sudo apt-get install -y clang-21 libsqlite3-dev libzstd-dev zlib1g-dev libpq-dev
sudo ln -sf /usr/bin/clang-21 /usr/bin/clang

- name: Install system dependencies (macOS)
Expand Down
4 changes: 3 additions & 1 deletion docs/stdlib/postgres.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ PostgreSQL client via `libpq`. Connect to a Postgres database, run queries, and
import { Pool } from "chadscript/postgres";
```

`libpq` is required at build time. On macOS: `brew install libpq`. On Debian/Ubuntu: `apt install libpq-dev`.
`libpq` is required at build time. On Debian/Ubuntu: `apt install libpq-dev`. On macOS: `brew install libpq` then add `pg_config` to your `PATH` (libpq is keg-only) — `export PATH="/opt/homebrew/opt/libpq/bin:$PATH"`. Or `brew install postgresql@16` which puts `pg_config` on `PATH` automatically.

The build script discovers `libpq` via `pg_config --includedir`, so as long as `pg_config` is on `PATH` the bridge compiles cleanly. If `pg_config` is missing, `pg-bridge.o` is silently skipped and `import { Pool } from "chadscript/postgres"` fails to link.

## `new Pool(conninfo)`

Expand Down
2 changes: 1 addition & 1 deletion scripts/build-target-sdk.sh
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ fi

# Copy C bridge object files
echo " Copying bridge objects..."
for bridge in child-process-bridge.o os-bridge.o strlen-cache.o time-bridge.o base64-bridge.o url-bridge.o uri-bridge.o regex-bridge.o dotenv-bridge.o watch-bridge.o lws-bridge.o multipart-bridge.o child-process-spawn.o arena-bridge.o curl-bridge.o compress-bridge.o yaml-bridge.o string-ops-bridge.o llvm-bridge.o llvm-builder-bridge.o lld-bridge.o; do
for bridge in child-process-bridge.o os-bridge.o strlen-cache.o time-bridge.o base64-bridge.o url-bridge.o uri-bridge.o regex-bridge.o dotenv-bridge.o watch-bridge.o lws-bridge.o multipart-bridge.o child-process-spawn.o arena-bridge.o curl-bridge.o pg-bridge.o compress-bridge.o yaml-bridge.o string-ops-bridge.o llvm-bridge.o llvm-builder-bridge.o lld-bridge.o; do
if [ -f "$C_BRIDGES_DIR/$bridge" ]; then
cp "$C_BRIDGES_DIR/$bridge" "$SDK_DIR/bridges/"
fi
Expand Down
15 changes: 9 additions & 6 deletions scripts/build-vendor.sh
Original file line number Diff line number Diff line change
Expand Up @@ -265,14 +265,17 @@ PG_BRIDGE_OBJ="$C_BRIDGES_DIR/pg-bridge.o"
if [ ! -f "$PG_BRIDGE_OBJ" ] || [ "$PG_BRIDGE_SRC" -nt "$PG_BRIDGE_OBJ" ]; then
PG_CFLAGS=""
PG_FOUND=0
if [ "$(uname)" = "Darwin" ]; then
BREW_PREFIX=$(brew --prefix 2>/dev/null || echo "/opt/homebrew")
LIBPQ_PREFIX=$(brew --prefix libpq 2>/dev/null || echo "$BREW_PREFIX/opt/libpq")
if [ -f "$LIBPQ_PREFIX/include/libpq-fe.h" ]; then
PG_CFLAGS="-I$LIBPQ_PREFIX/include"
# Prefer pg_config — ships with libpq (libpq-dev on debian/ubuntu, libpq on
# brew). Knows the right -I regardless of platform-specific install layout.
if command -v pg_config >/dev/null 2>&1; then
PG_INCDIR=$(pg_config --includedir 2>/dev/null || echo "")
if [ -n "$PG_INCDIR" ] && [ -f "$PG_INCDIR/libpq-fe.h" ]; then
PG_CFLAGS="-I$PG_INCDIR"
PG_FOUND=1
fi
fi
# Fallback: default cc include path (covers cases where libpq-fe.h is in
# /usr/include directly or the user has set CPATH/CPLUS_INCLUDE_PATH).
if [ "$PG_FOUND" = "0" ]; then
if echo '#include <libpq-fe.h>' | cc -xc -fsyntax-only - 2>/dev/null; then
PG_FOUND=1
Expand All @@ -283,7 +286,7 @@ if [ ! -f "$PG_BRIDGE_OBJ" ] || [ "$PG_BRIDGE_SRC" -nt "$PG_BRIDGE_OBJ" ]; then
cc -c -O2 -fPIC $PG_CFLAGS "$PG_BRIDGE_SRC" -o "$PG_BRIDGE_OBJ"
echo " -> $PG_BRIDGE_OBJ"
else
echo "==> pg-bridge skipped (no libpq headers found)"
echo "==> pg-bridge skipped (no libpq headers found — install libpq-dev / libpq)"
fi
else
echo "==> pg-bridge already built, skipping"
Expand Down
1 change: 1 addition & 0 deletions src/chad-native.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ registerStdlib("colors.ts", ChadScript.embedFile("../lib/colors.ts"));
registerStdlib("events.ts", ChadScript.embedFile("../lib/events.ts"));
registerStdlib("glob.ts", ChadScript.embedFile("../lib/glob.ts"));
registerStdlib("compress.ts", ChadScript.embedFile("../lib/compress.ts"));
registerStdlib("postgres.ts", ChadScript.embedFile("../lib/postgres.ts"));
const skillContent = ChadScript.embedFile("../lib/skill.md");
import { ArgumentParser } from "chadscript/argparse";

Expand Down
2 changes: 1 addition & 1 deletion tests/fixtures/stdlib/postgres-connect.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// @test-skip
// @test-requires-env: PG_TESTS_ENABLED
import { Client } from "chadscript/postgres";

const c = new Client("host=127.0.0.1 port=5432 user=postgres password=test dbname=chadtest");
Expand Down
2 changes: 1 addition & 1 deletion tests/fixtures/stdlib/postgres-params-types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// @test-skip
// @test-requires-env: PG_TESTS_ENABLED
import { Pool } from "chadscript/postgres";

const pool = new Pool("host=127.0.0.1 port=5432 user=postgres password=test dbname=chadtest");
Expand Down
2 changes: 1 addition & 1 deletion tests/fixtures/stdlib/postgres-pool.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// @test-skip
// @test-requires-env: PG_TESTS_ENABLED
import { Pool } from "chadscript/postgres";

const pool = new Pool("host=127.0.0.1 port=5432 user=postgres password=test dbname=chadtest");
Expand Down
2 changes: 1 addition & 1 deletion tests/fixtures/stdlib/postgres-query-rowcount.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// @test-skip
// @test-requires-env: PG_TESTS_ENABLED
import { Client } from "chadscript/postgres";

const c = new Client("host=127.0.0.1 port=5432 user=postgres password=test dbname=chadtest");
Expand Down
2 changes: 1 addition & 1 deletion tests/fixtures/stdlib/postgres-query-select.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// @test-skip
// @test-requires-env: PG_TESTS_ENABLED
import { Client } from "chadscript/postgres";

const c = new Client("host=127.0.0.1 port=5432 user=postgres password=test dbname=chadtest");
Expand Down
11 changes: 11 additions & 0 deletions tests/test-discovery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
// // @test-description: ... — custom test description
// // @test-native-only — skip when running with node compiler
// // @test-skip — exclude from auto-discovery
// // @test-requires-env: VAR — skip unless process.env.VAR is set and non-empty
//
// Defaults (no annotation needed):
// expectTestPassed: true — asserts stdout contains TEST_PASSED and exit code 0
Expand All @@ -34,6 +35,7 @@ interface ParsedAnnotations {
description?: string;
skip: boolean;
nativeOnly: boolean;
requiresEnv?: string;
}

function parseAnnotations(filePath: string): ParsedAnnotations {
Expand Down Expand Up @@ -72,6 +74,11 @@ function parseAnnotations(filePath: string): ParsedAnnotations {
if (descMatch) {
result.description = descMatch[1].trim();
}

const requiresEnvMatch = trimmed.match(/^\/\/\s*@test-requires-env:\s*(\S+)/);
if (requiresEnvMatch) {
result.requiresEnv = requiresEnvMatch[1].trim();
}
}

return result;
Expand Down Expand Up @@ -111,6 +118,10 @@ export function discoverTests(fixturesDir: string = "tests/fixtures"): TestCase[
const annotations = parseAnnotations(absPath);

if (annotations.skip) continue;
if (annotations.requiresEnv) {
const val = process.env[annotations.requiresEnv];
if (!val || val.length === 0) continue;
}

// Name from relative path without extension: "arrays/array-filter"
const relToFixtures = path.relative(fixturesDir, relPath);
Expand Down
Loading