From aeeb4115e468662e9bf49f023aad11b8cf42f391 Mon Sep 17 00:00:00 2001 From: Sinity Date: Tue, 18 Nov 2025 11:21:23 +0000 Subject: [PATCH 01/32] ci(workflows): skip PR flake checks and add mold - Stop running flake checks in PR contexts that do not have the required cache credentials. - Install `mold` alongside the Rust jobs so the standardized workflow path has the linker it expects. --- .github/workflows/ci.yml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e9ee41156a..601761d9c2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,6 +13,8 @@ jobs: check: name: Nix Flake Check runs-on: ubuntu-latest + # Skip for PRs since we don't have Cachix authentication + if: github.event_name != 'pull_request' steps: - uses: actions/checkout@v4 @@ -27,7 +29,7 @@ jobs: uses: cachix/cachix-action@v14 with: name: sinex - skipPush: ${{ github.event_name == 'pull_request' }} + authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' - name: Nix Flake Check run: nix flake check --accept-flake-config @@ -54,10 +56,10 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Install ripgrep + - name: Install build dependencies run: | sudo apt-get update - sudo apt-get install -y ripgrep + sudo apt-get install -y ripgrep mold - name: Enforce forbidden pattern guards run: bash scripts/check_forbidden_patterns.sh @@ -141,10 +143,10 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Install ripgrep + - name: Install build dependencies run: | sudo apt-get update - sudo apt-get install -y ripgrep + sudo apt-get install -y ripgrep mold - name: Enforce forbidden pattern guards run: bash scripts/check_forbidden_patterns.sh From 31a5a6a363230d82ae6644792f32c1120f712804 Mon Sep 17 00:00:00 2001 From: Sinity Date: Tue, 18 Nov 2025 12:05:40 +0000 Subject: [PATCH 02/32] fix(ci): wait for Postgres and provision the test database earlier Install the Postgres client, poll `pg_isready`, and create the role, database, and base extensions before schema generation so CI no longer races service startup. --- .github/workflows/ci.yml | 84 ++++++++++++++++++++++++++-------------- 1 file changed, 56 insertions(+), 28 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 601761d9c2..4cc26766e1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -63,6 +63,34 @@ jobs: - name: Enforce forbidden pattern guards run: bash scripts/check_forbidden_patterns.sh + + - name: Install PostgreSQL client + run: | + sudo apt-get update + sudo apt-get install -y postgresql-client + + - name: Wait for Postgres + env: + PGPASSWORD: postgres + run: | + for i in $(seq 1 30); do + if pg_isready -h localhost -U postgres -q; then + echo "Postgres is ready" + exit 0 + fi + sleep 1 + done + echo "Postgres did not become ready in time" >&2 + exit 1 + + - name: Setup test database + env: + PGPASSWORD: postgres + run: | + psql -h localhost -U postgres -c "CREATE USER sinex_test WITH PASSWORD 'test_password' CREATEDB" + psql -h localhost -U postgres -c "CREATE DATABASE sinex_test OWNER sinex_test" + psql -h localhost -U postgres -d sinex_test -c "CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\"" + psql -h localhost -U postgres -d sinex_test -c "CREATE EXTENSION IF NOT EXISTS timescaledb" - name: Regenerate schemas and ensure repository is clean # Treat schemas/ like a generated artifact (similar to cargo fmt). @@ -94,20 +122,6 @@ jobs: target/ key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - - name: Install PostgreSQL client - run: | - sudo apt-get update - sudo apt-get install -y postgresql-client - - - name: Setup test database - env: - PGPASSWORD: postgres - run: | - psql -h localhost -U postgres -c "CREATE USER sinex_test WITH PASSWORD 'test_password' CREATEDB" - psql -h localhost -U postgres -c "CREATE DATABASE sinex_test OWNER sinex_test" - psql -h localhost -U postgres -d sinex_test -c "CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\"" - psql -h localhost -U postgres -d sinex_test -c "CREATE EXTENSION IF NOT EXISTS timescaledb" - - name: Run migrations env: DATABASE_URL: postgres://sinex_test:test_password@localhost/sinex_test @@ -150,6 +164,34 @@ jobs: - name: Enforce forbidden pattern guards run: bash scripts/check_forbidden_patterns.sh + + - name: Install PostgreSQL client + run: | + sudo apt-get update + sudo apt-get install -y postgresql-client + + - name: Wait for Postgres + env: + PGPASSWORD: postgres + run: | + for i in $(seq 1 30); do + if pg_isready -h localhost -U postgres -q; then + echo "Postgres is ready" + exit 0 + fi + sleep 1 + done + echo "Postgres did not become ready in time" >&2 + exit 1 + + - name: Setup test database + env: + PGPASSWORD: postgres + run: | + psql -h localhost -U postgres -c "CREATE USER sinex_test WITH PASSWORD 'test_password' CREATEDB" + psql -h localhost -U postgres -c "CREATE DATABASE sinex_test OWNER sinex_test" + psql -h localhost -U postgres -d sinex_test -c "CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\"" + psql -h localhost -U postgres -d sinex_test -c "CREATE EXTENSION IF NOT EXISTS timescaledb" - name: Regenerate schemas and ensure repository is clean # Treat schemas/ like a generated artifact (similar to cargo fmt). @@ -173,20 +215,6 @@ jobs: - name: Install cargo-llvm-cov uses: taiki-e/install-action@cargo-llvm-cov - - - name: Install PostgreSQL client - run: | - sudo apt-get update - sudo apt-get install -y postgresql-client - - - name: Setup test database - env: - PGPASSWORD: postgres - run: | - psql -h localhost -U postgres -c "CREATE USER sinex_test WITH PASSWORD 'test_password' CREATEDB" - psql -h localhost -U postgres -c "CREATE DATABASE sinex_test OWNER sinex_test" - psql -h localhost -U postgres -d sinex_test -c "CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\"" - psql -h localhost -U postgres -d sinex_test -c "CREATE EXTENSION IF NOT EXISTS timescaledb" - name: Run migrations env: From ee1023d99d47236bdbbd622b8dd4d0b4d4f9e63d Mon Sep 17 00:00:00 2001 From: Sinity Date: Tue, 18 Nov 2025 16:58:32 +0000 Subject: [PATCH 03/32] fix(ci): run migrations before regenerating schemas Move the schema-generation cleanliness check after the migration step in both test and coverage jobs so the generated artifacts see the actual database schema. --- .github/workflows/ci.yml | 48 ++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4cc26766e1..e949e8d521 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -91,18 +91,6 @@ jobs: psql -h localhost -U postgres -c "CREATE DATABASE sinex_test OWNER sinex_test" psql -h localhost -U postgres -d sinex_test -c "CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\"" psql -h localhost -U postgres -d sinex_test -c "CREATE EXTENSION IF NOT EXISTS timescaledb" - - - name: Regenerate schemas and ensure repository is clean - # Treat schemas/ like a generated artifact (similar to cargo fmt). - env: - DATABASE_URL: postgres://sinex_test:test_password@localhost/sinex_test - run: | - ./scripts/schema-dev.sh generate - if ! git diff --quiet -- schemas; then - echo "::error::Schema artifacts are stale. Run './scripts/schema-dev.sh generate' and commit the results." - git status schemas - exit 1 - fi - name: Install Rust uses: actions-rs/toolchain@v1 @@ -128,6 +116,18 @@ jobs: run: | cd crate/lib/sinex-schema cargo run -- up + + - name: Regenerate schemas and ensure repository is clean + # Treat schemas/ like a generated artifact (similar to cargo fmt). + env: + DATABASE_URL: postgres://sinex_test:test_password@localhost/sinex_test + run: | + ./scripts/schema-dev.sh generate + if ! git diff --quiet -- schemas; then + echo "::error::Schema artifacts are stale. Run './scripts/schema-dev.sh generate' and commit the results." + git status schemas + exit 1 + fi - name: Run tests env: @@ -192,18 +192,6 @@ jobs: psql -h localhost -U postgres -c "CREATE DATABASE sinex_test OWNER sinex_test" psql -h localhost -U postgres -d sinex_test -c "CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\"" psql -h localhost -U postgres -d sinex_test -c "CREATE EXTENSION IF NOT EXISTS timescaledb" - - - name: Regenerate schemas and ensure repository is clean - # Treat schemas/ like a generated artifact (similar to cargo fmt). - env: - DATABASE_URL: postgres://sinex_test:test_password@localhost/sinex_test - run: | - ./scripts/schema-dev.sh generate - if ! git diff --quiet -- schemas; then - echo "::error::Schema artifacts are stale. Run './scripts/schema-dev.sh generate' and commit the results." - git status schemas - exit 1 - fi - name: Install Rust uses: actions-rs/toolchain@v1 @@ -222,6 +210,18 @@ jobs: run: | cd crate/lib/sinex-schema cargo run -- up + + - name: Regenerate schemas and ensure repository is clean + # Treat schemas/ like a generated artifact (similar to cargo fmt). + env: + DATABASE_URL: postgres://sinex_test:test_password@localhost/sinex_test + run: | + ./scripts/schema-dev.sh generate + if ! git diff --quiet -- schemas; then + echo "::error::Schema artifacts are stale. Run './scripts/schema-dev.sh generate' and commit the results." + git status schemas + exit 1 + fi - name: Generate code coverage env: From 81060168ca52aa44080810414cf7e6534c71436f Mon Sep 17 00:00:00 2001 From: Sinity Date: Wed, 19 Nov 2025 21:33:54 +0000 Subject: [PATCH 04/32] ci: install migration-required Postgres extensions in GitHub Actions Move CI to the `timescaledb-ha:pg16` image for pgvector support. Create the `vector`, `pg_jsonschema`, and `ulid` extensions in the test database before schema generation and tests run. --- .github/workflows/ci.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e949e8d521..a1c3ab495a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,7 +40,7 @@ jobs: services: postgres: - image: timescale/timescaledb:2.15.2-pg16 # Pin exact Timescale/Postgres versions for deterministic CI + image: timescale/timescaledb-ha:pg16 # Use timescaledb-ha which includes pgvector env: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres @@ -91,6 +91,9 @@ jobs: psql -h localhost -U postgres -c "CREATE DATABASE sinex_test OWNER sinex_test" psql -h localhost -U postgres -d sinex_test -c "CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\"" psql -h localhost -U postgres -d sinex_test -c "CREATE EXTENSION IF NOT EXISTS timescaledb" + psql -h localhost -U postgres -d sinex_test -c "CREATE EXTENSION IF NOT EXISTS vector" + psql -h localhost -U postgres -d sinex_test -c "CREATE EXTENSION IF NOT EXISTS pg_jsonschema" + psql -h localhost -U postgres -d sinex_test -c "CREATE EXTENSION IF NOT EXISTS ulid" - name: Install Rust uses: actions-rs/toolchain@v1 @@ -141,7 +144,7 @@ jobs: services: postgres: - image: timescale/timescaledb:2.15.2-pg16 # Pin exact Timescale/Postgres versions for deterministic CI + image: timescale/timescaledb-ha:pg16 # Use timescaledb-ha which includes pgvector env: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres @@ -192,6 +195,9 @@ jobs: psql -h localhost -U postgres -c "CREATE DATABASE sinex_test OWNER sinex_test" psql -h localhost -U postgres -d sinex_test -c "CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\"" psql -h localhost -U postgres -d sinex_test -c "CREATE EXTENSION IF NOT EXISTS timescaledb" + psql -h localhost -U postgres -d sinex_test -c "CREATE EXTENSION IF NOT EXISTS vector" + psql -h localhost -U postgres -d sinex_test -c "CREATE EXTENSION IF NOT EXISTS pg_jsonschema" + psql -h localhost -U postgres -d sinex_test -c "CREATE EXTENSION IF NOT EXISTS ulid" - name: Install Rust uses: actions-rs/toolchain@v1 From ab99141a37dae3b6f701aa71afc3254b7b0c2619 Mon Sep 17 00:00:00 2001 From: Sinity Date: Wed, 19 Nov 2025 22:18:55 +0000 Subject: [PATCH 05/32] fix(ci): make pg_jsonschema setup conditional in timescaledb-ha Only create `pg_jsonschema` when the extension is present in the CI image, matching the `timescaledb-ha` service chosen in the preceding workflow change. --- .github/workflows/ci.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a1c3ab495a..7d78affa70 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -92,8 +92,11 @@ jobs: psql -h localhost -U postgres -d sinex_test -c "CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\"" psql -h localhost -U postgres -d sinex_test -c "CREATE EXTENSION IF NOT EXISTS timescaledb" psql -h localhost -U postgres -d sinex_test -c "CREATE EXTENSION IF NOT EXISTS vector" - psql -h localhost -U postgres -d sinex_test -c "CREATE EXTENSION IF NOT EXISTS pg_jsonschema" psql -h localhost -U postgres -d sinex_test -c "CREATE EXTENSION IF NOT EXISTS ulid" + # pg_jsonschema is optional - only install if available + psql -h localhost -U postgres -d sinex_test -c "SELECT EXISTS (SELECT 1 FROM pg_available_extensions WHERE name = 'pg_jsonschema')" | grep -q 't' && \ + psql -h localhost -U postgres -d sinex_test -c "CREATE EXTENSION IF NOT EXISTS pg_jsonschema" || \ + echo "Skipping pg_jsonschema extension (not available in this PostgreSQL image)" - name: Install Rust uses: actions-rs/toolchain@v1 @@ -196,8 +199,11 @@ jobs: psql -h localhost -U postgres -d sinex_test -c "CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\"" psql -h localhost -U postgres -d sinex_test -c "CREATE EXTENSION IF NOT EXISTS timescaledb" psql -h localhost -U postgres -d sinex_test -c "CREATE EXTENSION IF NOT EXISTS vector" - psql -h localhost -U postgres -d sinex_test -c "CREATE EXTENSION IF NOT EXISTS pg_jsonschema" psql -h localhost -U postgres -d sinex_test -c "CREATE EXTENSION IF NOT EXISTS ulid" + # pg_jsonschema is optional - only install if available + psql -h localhost -U postgres -d sinex_test -c "SELECT EXISTS (SELECT 1 FROM pg_available_extensions WHERE name = 'pg_jsonschema')" | grep -q 't' && \ + psql -h localhost -U postgres -d sinex_test -c "CREATE EXTENSION IF NOT EXISTS pg_jsonschema" || \ + echo "Skipping pg_jsonschema extension (not available in this PostgreSQL image)" - name: Install Rust uses: actions-rs/toolchain@v1 From b5e50201f7645db44a158a9af3514c67b6d02a2f Mon Sep 17 00:00:00 2001 From: Sinity Date: Thu, 20 Nov 2025 01:27:42 +0000 Subject: [PATCH 06/32] fix(ci): gate ULID extension creation on `pg_available_extensions` in workflow bootstrap Harden GitHub Actions database bootstrap in both CI jobs by probing `pg_available_extensions` before running `CREATE EXTENSION ulid`. If the extension is missing in the selected timescaledb-ha image, the workflow now logs a skip instead of failing setup. This mirrors the neighboring pg_jsonschema optionalization and makes the CI path resilient to image variants that ship different extension sets. --- .github/workflows/ci.yml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7d78affa70..4f5e757fd0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -92,8 +92,10 @@ jobs: psql -h localhost -U postgres -d sinex_test -c "CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\"" psql -h localhost -U postgres -d sinex_test -c "CREATE EXTENSION IF NOT EXISTS timescaledb" psql -h localhost -U postgres -d sinex_test -c "CREATE EXTENSION IF NOT EXISTS vector" - psql -h localhost -U postgres -d sinex_test -c "CREATE EXTENSION IF NOT EXISTS ulid" - # pg_jsonschema is optional - only install if available + # ulid and pg_jsonschema are optional - only install if available + psql -h localhost -U postgres -d sinex_test -c "SELECT EXISTS (SELECT 1 FROM pg_available_extensions WHERE name = 'ulid')" | grep -q 't' && \ + psql -h localhost -U postgres -d sinex_test -c "CREATE EXTENSION IF NOT EXISTS ulid" || \ + echo "Skipping ulid extension (not available in this PostgreSQL image)" psql -h localhost -U postgres -d sinex_test -c "SELECT EXISTS (SELECT 1 FROM pg_available_extensions WHERE name = 'pg_jsonschema')" | grep -q 't' && \ psql -h localhost -U postgres -d sinex_test -c "CREATE EXTENSION IF NOT EXISTS pg_jsonschema" || \ echo "Skipping pg_jsonschema extension (not available in this PostgreSQL image)" @@ -199,8 +201,10 @@ jobs: psql -h localhost -U postgres -d sinex_test -c "CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\"" psql -h localhost -U postgres -d sinex_test -c "CREATE EXTENSION IF NOT EXISTS timescaledb" psql -h localhost -U postgres -d sinex_test -c "CREATE EXTENSION IF NOT EXISTS vector" - psql -h localhost -U postgres -d sinex_test -c "CREATE EXTENSION IF NOT EXISTS ulid" - # pg_jsonschema is optional - only install if available + # ulid and pg_jsonschema are optional - only install if available + psql -h localhost -U postgres -d sinex_test -c "SELECT EXISTS (SELECT 1 FROM pg_available_extensions WHERE name = 'ulid')" | grep -q 't' && \ + psql -h localhost -U postgres -d sinex_test -c "CREATE EXTENSION IF NOT EXISTS ulid" || \ + echo "Skipping ulid extension (not available in this PostgreSQL image)" psql -h localhost -U postgres -d sinex_test -c "SELECT EXISTS (SELECT 1 FROM pg_available_extensions WHERE name = 'pg_jsonschema')" | grep -q 't' && \ psql -h localhost -U postgres -d sinex_test -c "CREATE EXTENSION IF NOT EXISTS pg_jsonschema" || \ echo "Skipping pg_jsonschema extension (not available in this PostgreSQL image)" From 06ee4b89880fe2f67e2419c9564da790eee0404d Mon Sep 17 00:00:00 2001 From: Sinity Date: Thu, 20 Nov 2025 03:58:38 +0100 Subject: [PATCH 07/32] ci(devenv): switch GitHub Actions to the devenv-driven test path Replace the older multi-job cargo workflow with a devenv-based CI path that boots a local Postgres instance, runs migrations, enforces the forbidden-pattern guard, regenerates schemas, and executes the test suite from the shell. Update the surrounding schema/property tests and ULID-extension handling to match the new CI environment. --- .github/workflows/ci.yml | 265 +++--------------- .../tests/validator_schema_order_tests.rs | 23 +- .../property/event_model_fuzzing_test.rs | 37 +-- .../tests/property/schema_property_test.rs | 29 +- .../tests/property/ulid_property_test.rs | 3 + ...20241028_000001_create_canonical_schema.rs | 29 +- crate/lib/sinex-test-utils/macros/src/lib.rs | 65 +++-- crate/lib/sinex-test-utils/src/lib.rs | 10 + .../sinex-test-utils/src/property_testing.rs | 91 +++++- .../sinex-test-utils/src/snapshot_helper.rs | 47 +++- .../lib/sinex-test-utils/src/test_context.rs | 36 +++ devenv.nix | 173 +----------- rustfmt.toml | 4 + 13 files changed, 335 insertions(+), 477 deletions(-) create mode 100644 rustfmt.toml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4f5e757fd0..f053f3ade3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,242 +10,53 @@ env: CARGO_TERM_COLOR: always jobs: - check: - name: Nix Flake Check - runs-on: ubuntu-latest - # Skip for PRs since we don't have Cachix authentication - if: github.event_name != 'pull_request' - steps: - - uses: actions/checkout@v4 - - - name: Install Nix - uses: cachix/install-nix-action@v24 - with: - nix_path: nixpkgs=channel:nixos-unstable - extra_nix_config: | - experimental-features = nix-command flakes - - - name: Setup Cachix - uses: cachix/cachix-action@v14 - with: - name: sinex - authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' - - - name: Nix Flake Check - run: nix flake check --accept-flake-config - test: - name: Cargo Test + name: Devenv Integration Test runs-on: ubuntu-latest - - services: - postgres: - image: timescale/timescaledb-ha:pg16 # Use timescaledb-ha which includes pgvector - env: - POSTGRES_USER: postgres - POSTGRES_PASSWORD: postgres - POSTGRES_DB: postgres - ports: - - 5432:5432 - options: >- - --health-cmd pg_isready - --health-interval 10s - --health-timeout 5s - --health-retries 5 + defaults: + run: + shell: devenv shell bash -- -e {0} steps: - - uses: actions/checkout@v4 - - - name: Install build dependencies - run: | - sudo apt-get update - sudo apt-get install -y ripgrep mold - - - name: Enforce forbidden pattern guards - run: bash scripts/check_forbidden_patterns.sh - - - name: Install PostgreSQL client - run: | - sudo apt-get update - sudo apt-get install -y postgresql-client - - - name: Wait for Postgres - env: - PGPASSWORD: postgres - run: | - for i in $(seq 1 30); do - if pg_isready -h localhost -U postgres -q; then - echo "Postgres is ready" - exit 0 - fi - sleep 1 - done - echo "Postgres did not become ready in time" >&2 - exit 1 - - - name: Setup test database - env: - PGPASSWORD: postgres - run: | - psql -h localhost -U postgres -c "CREATE USER sinex_test WITH PASSWORD 'test_password' CREATEDB" - psql -h localhost -U postgres -c "CREATE DATABASE sinex_test OWNER sinex_test" - psql -h localhost -U postgres -d sinex_test -c "CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\"" - psql -h localhost -U postgres -d sinex_test -c "CREATE EXTENSION IF NOT EXISTS timescaledb" - psql -h localhost -U postgres -d sinex_test -c "CREATE EXTENSION IF NOT EXISTS vector" - # ulid and pg_jsonschema are optional - only install if available - psql -h localhost -U postgres -d sinex_test -c "SELECT EXISTS (SELECT 1 FROM pg_available_extensions WHERE name = 'ulid')" | grep -q 't' && \ - psql -h localhost -U postgres -d sinex_test -c "CREATE EXTENSION IF NOT EXISTS ulid" || \ - echo "Skipping ulid extension (not available in this PostgreSQL image)" - psql -h localhost -U postgres -d sinex_test -c "SELECT EXISTS (SELECT 1 FROM pg_available_extensions WHERE name = 'pg_jsonschema')" | grep -q 't' && \ - psql -h localhost -U postgres -d sinex_test -c "CREATE EXTENSION IF NOT EXISTS pg_jsonschema" || \ - echo "Skipping pg_jsonschema extension (not available in this PostgreSQL image)" - - - name: Install Rust - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: stable - override: true - - - name: Cache Cargo - uses: actions/cache@v3 - with: - path: | - ~/.cargo/bin/ - ~/.cargo/registry/index/ - ~/.cargo/registry/cache/ - ~/.cargo/git/db/ - target/ - key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - - - name: Run migrations - env: - DATABASE_URL: postgres://sinex_test:test_password@localhost/sinex_test - run: | - cd crate/lib/sinex-schema - cargo run -- up + - uses: actions/checkout@v5 - - name: Regenerate schemas and ensure repository is clean - # Treat schemas/ like a generated artifact (similar to cargo fmt). - env: - DATABASE_URL: postgres://sinex_test:test_password@localhost/sinex_test - run: | - ./scripts/schema-dev.sh generate - if ! git diff --quiet -- schemas; then - echo "::error::Schema artifacts are stale. Run './scripts/schema-dev.sh generate' and commit the results." - git status schemas - exit 1 - fi - - - name: Run tests - env: - DATABASE_URL: postgres://sinex_test:test_password@localhost/sinex_test - RUST_LOG: debug - run: cargo nextest run --all-features --workspace --profile reliable - - coverage: - name: Code Coverage - runs-on: ubuntu-latest - - services: - postgres: - image: timescale/timescaledb-ha:pg16 # Use timescaledb-ha which includes pgvector - env: - POSTGRES_USER: postgres - POSTGRES_PASSWORD: postgres - POSTGRES_DB: postgres - ports: - - 5432:5432 - options: >- - --health-cmd pg_isready - --health-interval 10s - --health-timeout 5s - --health-retries 5 - - steps: - - uses: actions/checkout@v4 - - - name: Install build dependencies - run: | - sudo apt-get update - sudo apt-get install -y ripgrep mold - - - name: Enforce forbidden pattern guards - run: bash scripts/check_forbidden_patterns.sh - - - name: Install PostgreSQL client - run: | - sudo apt-get update - sudo apt-get install -y postgresql-client - - - name: Wait for Postgres - env: - PGPASSWORD: postgres - run: | - for i in $(seq 1 30); do - if pg_isready -h localhost -U postgres -q; then - echo "Postgres is ready" - exit 0 - fi - sleep 1 - done - echo "Postgres did not become ready in time" >&2 - exit 1 - - - name: Setup test database - env: - PGPASSWORD: postgres - run: | - psql -h localhost -U postgres -c "CREATE USER sinex_test WITH PASSWORD 'test_password' CREATEDB" - psql -h localhost -U postgres -c "CREATE DATABASE sinex_test OWNER sinex_test" - psql -h localhost -U postgres -d sinex_test -c "CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\"" - psql -h localhost -U postgres -d sinex_test -c "CREATE EXTENSION IF NOT EXISTS timescaledb" - psql -h localhost -U postgres -d sinex_test -c "CREATE EXTENSION IF NOT EXISTS vector" - # ulid and pg_jsonschema are optional - only install if available - psql -h localhost -U postgres -d sinex_test -c "SELECT EXISTS (SELECT 1 FROM pg_available_extensions WHERE name = 'ulid')" | grep -q 't' && \ - psql -h localhost -U postgres -d sinex_test -c "CREATE EXTENSION IF NOT EXISTS ulid" || \ - echo "Skipping ulid extension (not available in this PostgreSQL image)" - psql -h localhost -U postgres -d sinex_test -c "SELECT EXISTS (SELECT 1 FROM pg_available_extensions WHERE name = 'pg_jsonschema')" | grep -q 't' && \ - psql -h localhost -U postgres -d sinex_test -c "CREATE EXTENSION IF NOT EXISTS pg_jsonschema" || \ - echo "Skipping pg_jsonschema extension (not available in this PostgreSQL image)" - - - name: Install Rust - uses: actions-rs/toolchain@v1 + - uses: cachix/install-nix-action@v31 with: - profile: minimal - toolchain: stable - override: true - components: llvm-tools-preview - - - name: Install cargo-llvm-cov - uses: taiki-e/install-action@cargo-llvm-cov - - - name: Run migrations - env: - DATABASE_URL: postgres://sinex_test:test_password@localhost/sinex_test - run: | - cd crate/lib/sinex-schema - cargo run -- up + nix_path: nixpkgs=channel:nixos-unstable - - name: Regenerate schemas and ensure repository is clean - # Treat schemas/ like a generated artifact (similar to cargo fmt). - env: - DATABASE_URL: postgres://sinex_test:test_password@localhost/sinex_test - run: | + - uses: cachix/cachix-action@v16 + with: + name: sinity + authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' + skipPush: true + + - name: Install devenv.sh + shell: bash + run: nix profile install nixpkgs#devenv + + - name: Build devenv shell and run hooks + run: devenv test + + - name: Run migrations and tests + run: | + set -euo pipefail + export PGDATA="$PWD/postgres_data" + rm -rf "$PGDATA" + mkdir -p "$PGDATA" + initdb --auth=trust --no-locale --encoding=UTF8 + echo "unix_socket_directories = ''$PWD''" >> "$PGDATA/postgresql.conf" + pg_ctl start -l postgres.log -o "-k ''$PWD''" + trap 'pg_ctl stop' EXIT + createuser -h "$PWD" sinity 2>/dev/null || true + createdb -h "$PWD" -O sinity sinex_dev 2>/dev/null || true + export PGHOST="$PWD" + export DATABASE_URL="postgresql:///sinex_dev?host=$PWD" + devenv tasks run db:migrate + bash scripts/check_forbidden_patterns.sh ./scripts/schema-dev.sh generate if ! git diff --quiet -- schemas; then - echo "::error::Schema artifacts are stale. Run './scripts/schema-dev.sh generate' and commit the results." + echo "::error::Schema artifacts are stale. Run './scripts/schema-dev.sh generate' locally." git status schemas exit 1 fi - - - name: Generate code coverage - env: - DATABASE_URL: postgres://sinex_test:test_password@localhost/sinex_test - run: cargo llvm-cov --all-features --workspace --profile reliable --lcov --output-path lcov.info - - - name: Upload coverage to Codecov - uses: codecov/codecov-action@v3 - with: - files: lcov.info - fail_ci_if_error: false + devenv tasks run dev:test diff --git a/crate/core/sinex-ingestd/tests/validator_schema_order_tests.rs b/crate/core/sinex-ingestd/tests/validator_schema_order_tests.rs index f3f1d60f7a..5b78e7d211 100644 --- a/crate/core/sinex-ingestd/tests/validator_schema_order_tests.rs +++ b/crate/core/sinex-ingestd/tests/validator_schema_order_tests.rs @@ -8,9 +8,7 @@ use sinex_test_utils::{sinex_test, TestContext}; async fn validator_prefers_latest_semver(ctx: TestContext) -> color_eyre::Result<()> { let repo = ctx.pool.schemas(); - sqlx::query!("CREATE EXTENSION IF NOT EXISTS ulid") - .execute(&ctx.pool) - .await?; + ensure_ulid_extension(&ctx.pool).await?; repo.register_schema(NewEventSchema { source: "semver-source".to_string(), @@ -54,6 +52,25 @@ async fn validator_prefers_latest_semver(ctx: TestContext) -> color_eyre::Result } } +async fn ensure_ulid_extension(pool: &sinex_core::DbPool) -> color_eyre::Result<()> { + let available = sqlx::query_scalar::<_, String>( + "SELECT name FROM pg_available_extensions WHERE name IN ('ulid', 'pgx_ulid') LIMIT 1", + ) + .fetch_optional(pool) + .await?; + + let Some(extension) = available else { + return Err(color_eyre::eyre::eyre!( + "Neither 'ulid' nor 'pgx_ulid' extensions are available in this PostgreSQL instance" + )); + }; + + let stmt = format!(r#"CREATE EXTENSION IF NOT EXISTS "{extension}""#); + sqlx::query(&stmt).execute(pool).await?; + + Ok(()) +} + #[sinex_test] async fn validator_handles_double_digit_versions(ctx: TestContext) -> color_eyre::Result<()> { let repo = ctx.pool.schemas(); diff --git a/crate/lib/sinex-core/tests/property/event_model_fuzzing_test.rs b/crate/lib/sinex-core/tests/property/event_model_fuzzing_test.rs index 9a33ee8274..d44efda3f5 100644 --- a/crate/lib/sinex-core/tests/property/event_model_fuzzing_test.rs +++ b/crate/lib/sinex-core/tests/property/event_model_fuzzing_test.rs @@ -21,6 +21,7 @@ //! - Focus on the modern RawEvent::schemaless() API and database insertion paths use sinex_test_utils::prelude::*; +use color_eyre::eyre::Report; use chrono::{DateTime, TimeZone, Utc}; use proptest::strategy::ValueTree; @@ -411,7 +412,7 @@ sinex_proptest! { let _ = event.ts_orig; let _ = event.ingestor_version; let _ = event.payload_schema_id; - Ok(()) + Ok::<(), Report>(()) } /// Test filesystem events with extreme payloads @@ -438,7 +439,7 @@ sinex_proptest! { let _ = event.source.as_str(); let _ = event.event_type.as_str(); let _ = &event.payload; - Ok(()) + Ok::<(), Report>(()) } /// Test terminal events with extreme payloads @@ -469,7 +470,7 @@ sinex_proptest! { let _ = event.source.as_str(); let _ = event.event_type.as_str(); let _ = &event.payload; - Ok(()) + Ok::<(), Report>(()) } /// Test clipboard events with extreme payloads @@ -491,7 +492,7 @@ sinex_proptest! { let _ = event.source.as_str(); let _ = event.event_type.as_str(); let _ = &event.payload; - Ok(()) + Ok::<(), Report>(()) } } @@ -522,11 +523,12 @@ sinex_proptest! { let _ = event.source.as_str(); let _ = event.event_type.as_str(); let _ = &event.payload; - Ok(()) + Ok::<(), Report>(()) } } -/// Test system events with extreme payloads +sinex_proptest! { + /// Test system events with extreme payloads fn test_system_events_robustness( payload in fuzzed_system_payloads(), source in prop_oneof![ @@ -551,10 +553,10 @@ sinex_proptest! { let _ = event.source.as_str(); let _ = event.event_type.as_str(); let _ = &event.payload; - Ok(()) + Ok::<(), Report>(()) } -/// Test JSON serialization robustness with extreme payloads + /// Test JSON serialization robustness with extreme payloads fn test_json_serialization_robustness( payload in malformed_json_values() ) -> TestResult<()> { @@ -572,8 +574,9 @@ sinex_proptest! { } let _ = serde_json::to_string_pretty(&event); - Ok(()) + Ok::<(), Report>(()) } +} sinex_proptest! { /// Test ULID robustness with extreme timestamps @@ -600,7 +603,7 @@ sinex_proptest! { // Verify the event can be serialized let _json = serde_json::to_string(&event); - Ok(()) + Ok::<(), Report>(()) } /// Test string handling robustness @@ -623,7 +626,7 @@ sinex_proptest! { // Test that we can create the struct without panicking by checking key fields prop_assert_eq!(event.source.as_str(), source.as_str()); prop_assert_eq!(event.host.as_str(), host.as_str()); - Ok(()) + Ok::<(), Report>(()) } } @@ -660,7 +663,7 @@ async fn test_database_insertion_robustness(ctx: TestContext) -> TestResult<()> } } } - Ok(()) + Ok::<(), Report>(()) } /// Test event creation with extreme payloads in database context @@ -731,7 +734,7 @@ async fn test_extreme_payload_database_handling(ctx: TestContext) -> TestResult< } } } - Ok(()) + Ok::<(), Report>(()) } // ============================================================================ @@ -757,7 +760,7 @@ mod additional_tests { // Test serialization instead of database insertion let _result = serde_json::to_string(&event); - Ok(()) + Ok::<(), Report>(()) } #[sinex_test] @@ -782,7 +785,7 @@ mod additional_tests { // Test serialization instead of database insertion let _result = serde_json::to_string(&event); - Ok(()) + Ok::<(), Report>(()) } #[sinex_test] @@ -806,7 +809,7 @@ mod additional_tests { // Test serialization instead of database insertion let _result = serde_json::to_string(&event); - Ok(()) + Ok::<(), Report>(()) } #[sinex_test] @@ -838,6 +841,6 @@ mod additional_tests { // This should not panic assert!(result.is_ok()); - Ok(()) + Ok::<(), Report>(()) } } diff --git a/crate/lib/sinex-core/tests/property/schema_property_test.rs b/crate/lib/sinex-core/tests/property/schema_property_test.rs index d653bce2da..f420300f6d 100644 --- a/crate/lib/sinex-core/tests/property/schema_property_test.rs +++ b/crate/lib/sinex-core/tests/property/schema_property_test.rs @@ -5,6 +5,7 @@ //! - Additional schema-related property tests for validation chains //! - Schema compatibility and evolution properties +use color_eyre::eyre::Report; use proptest::prelude::*; use proptest::strategy::ValueTree; use serde_json::{json, Value}; @@ -158,7 +159,7 @@ fn test_json_validation_normal_payloads( let json_str = payload.to_string(); let result = validate_json(&json_str); drop(result); - Ok(()) + Ok::<(), Report>(()) } #[sinex_prop] @@ -168,7 +169,7 @@ fn test_json_validation_security_payloads( let json_str = payload.to_string(); let result = validate_json(&json_str); drop(result); - Ok(()) + Ok::<(), Report>(()) } #[sinex_prop] @@ -188,7 +189,7 @@ fn test_json_validation_consistency( prop_assert!(false, "Validation was not consistent"); } } - Ok(()) + Ok::<(), Report>(()) } // ============================================================================= @@ -244,7 +245,7 @@ fn test_schema_evolution_properties( // Mixed outcomes are acceptable; evolution may introduce new constraints. } } - Ok(()) + Ok::<(), Report>(()) } // ============================================================================= @@ -282,7 +283,7 @@ fn test_validation_chain_properties( ); } } - Ok(()) + Ok::<(), Report>(()) } /// Test validation chain with numeric values @@ -309,7 +310,7 @@ fn test_validation_chain_numeric_properties( prop_assert!(test_number <= 1000, "Valid range numbers should be <= 1000"); } } - Ok(()) + Ok::<(), Report>(()) } // ============================================================================= @@ -346,7 +347,7 @@ async fn schema_registry_should_drive_json_validation(ctx: TestContext) -> TestR "JSON validation should enforce registered schemas once property tests are restored (TODO #17)" ); - Ok(()) + Ok::<(), Report>(()) } // ============================================================================= @@ -380,7 +381,7 @@ fn test_json_validation_edge_cases() -> TestResult { let json_str = payload.to_string(); let _ = validate_json(&json_str); } - Ok(()) + Ok::<(), Report>(()) } // ============================================================================= @@ -460,7 +461,7 @@ fn test_validation_performance_properties( ); } } - Ok(()) + Ok::<(), Report>(()) } // ============================================================================= @@ -502,7 +503,7 @@ mod unit_tests { match invalid_result { Ok(_) | Err(_) => {} // Any result is acceptable for testing } - Ok(()) + Ok::<(), Report>(()) } #[sinex_test] @@ -525,7 +526,7 @@ mod unit_tests { .unwrap() .current(); assert!(problematic.is_object()); // All problematic payloads are objects - Ok(()) + Ok::<(), Report>(()) } #[sinex_test] @@ -540,7 +541,7 @@ mod unit_tests { assert!(!event_type.is_empty()); assert!(source.len() >= 3); // 1 + 2 minimum assert!(event_type.len() >= 3); // 1 + 2 minimum - Ok(()) + Ok::<(), Report>(()) } #[sinex_test] @@ -561,7 +562,7 @@ mod unit_tests { assert!(invalid_name.is_empty()); // Should fail length check assert!(invalid_age < 18); // Should fail age check - Ok(()) + Ok::<(), Report>(()) } #[sinex_test] @@ -573,6 +574,6 @@ mod unit_tests { // Test that we can detect validation issues assert!(empty_value.is_empty()); // Should fail validation assert!(!valid_value.is_empty()); // Should pass validation - Ok(()) + Ok::<(), Report>(()) } } diff --git a/crate/lib/sinex-core/tests/property/ulid_property_test.rs b/crate/lib/sinex-core/tests/property/ulid_property_test.rs index 552a8e12e7..3a40bbadc5 100644 --- a/crate/lib/sinex-core/tests/property/ulid_property_test.rs +++ b/crate/lib/sinex-core/tests/property/ulid_property_test.rs @@ -196,6 +196,7 @@ sinex_proptest! { } Ok(()) } +} sinex_proptest! { fn test_concurrent_ulid_timestamp_correlation( @@ -355,6 +356,7 @@ fn arb_ulid_from_time_range( }) } +sinex_proptest! { fn test_ulid_ordering_property_in_memory( ulids in arb_ulid_sequence(2, 20) ) -> color_eyre::eyre::Result<()> { @@ -374,6 +376,7 @@ fn arb_ulid_from_time_range( "All ULIDs in sequence should be unique"); Ok(()) } +} // Database test temporarily disabled due to direct sqlx usage // TODO: Reimplement using repository pattern diff --git a/crate/lib/sinex-schema/src/migrations/m20241028_000001_create_canonical_schema.rs b/crate/lib/sinex-schema/src/migrations/m20241028_000001_create_canonical_schema.rs index c2eb32f25f..bff43e6663 100644 --- a/crate/lib/sinex-schema/src/migrations/m20241028_000001_create_canonical_schema.rs +++ b/crate/lib/sinex-schema/src/migrations/m20241028_000001_create_canonical_schema.rs @@ -568,6 +568,7 @@ impl MigrationTrait for Migration { async fn ensure_required_extensions(conn: &SchemaManagerConnection<'_>) -> Result<(), DbErr> { for extension in REQUIRED_EXTENSIONS { + let mut target = *extension; let check_sql = format!( "SELECT EXISTS (SELECT 1 FROM pg_catalog.pg_available_extensions WHERE name = '{ext}') AS available", ext = extension.replace('\'', "''"), @@ -578,7 +579,28 @@ async fn ensure_required_extensions(conn: &SchemaManagerConnection<'_>) -> Resul .and_then(|row| row.try_get_by_index::(0).ok()) .unwrap_or(false); - if !available { + let mut resolved_available = available; + + if !resolved_available && *extension == "ulid" { + let fallback = "pgx_ulid"; + let fallback_check = format!( + "SELECT EXISTS (SELECT 1 FROM pg_catalog.pg_available_extensions WHERE name = '{ext}') AS available", + ext = fallback + ); + resolved_available = conn + .query_one(Statement::from_string( + DatabaseBackend::Postgres, + fallback_check, + )) + .await? + .and_then(|row| row.try_get_by_index::(0).ok()) + .unwrap_or(false); + if resolved_available { + target = fallback; + } + } + + if !resolved_available { warn!( extension = %extension, "Skipping unavailable PostgreSQL extension; install it manually if needed" @@ -586,10 +608,7 @@ async fn ensure_required_extensions(conn: &SchemaManagerConnection<'_>) -> Resul continue; } - let statement = format!( - r#"CREATE EXTENSION IF NOT EXISTS "{ext}";"#, - ext = extension - ); + let statement = format!(r#"CREATE EXTENSION IF NOT EXISTS "{ext}";"#, ext = target); conn.execute_unprepared(&statement).await?; } diff --git a/crate/lib/sinex-test-utils/macros/src/lib.rs b/crate/lib/sinex-test-utils/macros/src/lib.rs index bf6d35df82..717cd5a1b3 100644 --- a/crate/lib/sinex-test-utils/macros/src/lib.rs +++ b/crate/lib/sinex-test-utils/macros/src/lib.rs @@ -286,19 +286,25 @@ pub fn sinex_prop(attr: TokenStream, item: TokenStream) -> TokenStream { quote!() }; - let seed_stmt = opts.seed.map(|seed| { - quote! { - cfg.rng_algorithm = ::proptest::test_runner::RngAlgorithm::ChaCha; - cfg.rng_seed = Some(#seed); - } - }).unwrap_or_else(|| { - quote! { - if let Ok(seed_env) = std::env::var("SINEX_PROPTEST_SEED").ok().and_then(|s| s.parse::().ok()) { + let seed_stmt = opts + .seed + .map(|seed| { + quote! { cfg.rng_algorithm = ::proptest::test_runner::RngAlgorithm::ChaCha; - cfg.rng_seed = Some(seed_env); + cfg.rng_seed = ::proptest::test_runner::RngSeed::Fixed(#seed); } - } - }); + }) + .unwrap_or_else(|| { + quote! { + if let Some(seed_env) = std::env::var("SINEX_PROPTEST_SEED") + .ok() + .and_then(|s| s.parse::().ok()) + { + cfg.rng_algorithm = ::proptest::test_runner::RngAlgorithm::ChaCha; + cfg.rng_seed = ::proptest::test_runner::RngSeed::Fixed(seed_env); + } + } + }); let shrink_stmt = opts .max_shrink_time_ms @@ -356,16 +362,7 @@ pub fn sinex_prop(attr: TokenStream, item: TokenStream) -> TokenStream { let runner_setup = quote! { use ::proptest::prelude::*; - let mut cfg = ::proptest::test_runner::Config::default(); - cfg.cases = #cases; - if let Ok(val) = std::env::var("SINEX_PROPTEST_CASES").ok().and_then(|s| s.parse::().ok()) { - cfg.cases = val; - } - cfg.failure_persistence = ::proptest::test_runner::FailurePersistence::File( - std::env::var("SINEX_PROPTEST_DIR") - .map(std::path::PathBuf::from) - .unwrap_or_else(|_| std::path::PathBuf::from("target/proptest-regressions")) - ); + let mut cfg = sinex_test_utils::sinex_prop_runner_config(#cases, module_path!(), test_name); #seed_stmt #shrink_stmt let mut runner = ::proptest::test_runner::TestRunner::new(cfg); @@ -414,10 +411,13 @@ pub fn sinex_prop(attr: TokenStream, item: TokenStream) -> TokenStream { } Err(err) => { eprintln!("❌ {} ({:.1?})", test_name.replace('_', " "), elapsed); + let failure_ctx = ctx_snapshot_ref + .map(|ctx| sinex_test_utils::snapshot_helper::FailureContext::Borrowed(ctx)) + .unwrap_or(sinex_test_utils::snapshot_helper::FailureContext::None); sinex_test_utils::snapshot_helper::persist_failure( test_name, format!("{err:?}"), - ctx_snapshot_ref, + failure_ctx, ); Err(color_eyre::eyre::eyre!("{err}")) } @@ -438,8 +438,10 @@ pub fn sinex_prop(attr: TokenStream, item: TokenStream) -> TokenStream { #tuple_unpack #( #destructures )* std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { #user_body })) - .map_err(|_| ::proptest::test_runner::TestCaseError::fail("panic in property")) - .and_then(|res| res.map_err(|err| ::proptest::test_runner::TestCaseError::fail(format!("{err:?}")))) + .map_err(|_| ::proptest::test_runner::TestCaseError::fail("panic in property")) + .and_then(|res| { + res.map_err(|err| ::proptest::test_runner::TestCaseError::fail(format!("{err:?}"))) + }) }); let elapsed = start.elapsed(); match result { @@ -452,7 +454,7 @@ pub fn sinex_prop(attr: TokenStream, item: TokenStream) -> TokenStream { sinex_test_utils::snapshot_helper::persist_failure( test_name, format!("{err:?}"), - None, + sinex_test_utils::snapshot_helper::FailureContext::None, ); Err(color_eyre::eyre::eyre!("{err}")) } @@ -663,7 +665,7 @@ pub fn sinex_proptest(input: TokenStream) -> TokenStream { out.push(quote! { #(#passthrough_attrs)* - #[sinex_prop #meta_tokens] + #[sinex_test_utils::sinex_prop #meta_tokens] fn #name( #ctx_tokens #( #param_defs ),* ) -> sinex_test_utils::TestResult<()> { #( #destructures )* #body @@ -864,7 +866,7 @@ pub fn sinex_test(attr: TokenStream, item: TokenStream) -> TokenStream { sinex_test_utils::snapshot_helper::persist_failure( test_name, format!("{err:?}"), - None, + sinex_test_utils::snapshot_helper::FailureContext::None, ); } } @@ -927,7 +929,7 @@ pub fn sinex_test(attr: TokenStream, item: TokenStream) -> TokenStream { sinex_test_utils::snapshot_helper::persist_failure( test_name, format!("{err:?}"), - None, + sinex_test_utils::snapshot_helper::FailureContext::None, ); } } @@ -952,6 +954,7 @@ pub fn sinex_test(attr: TokenStream, item: TokenStream) -> TokenStream { eprintln!("🔄 {} [timeout: {}s]", test_name.replace('_', " "), #timeout_secs); let ctx = TestContext::with_name(test_name).await?; + let ctx_failure_snapshot = ctx.failure_snapshot(); let result: color_eyre::eyre::Result<()> = if #timeout_secs > 10 { let progress_task = tokio::spawn(async { @@ -988,7 +991,9 @@ pub fn sinex_test(attr: TokenStream, item: TokenStream) -> TokenStream { sinex_test_utils::snapshot_helper::persist_failure( test_name, format!("{err:?}"), - Some(&ctx), + sinex_test_utils::snapshot_helper::FailureContext::Snapshot( + ctx_failure_snapshot.clone(), + ), ); } } @@ -1029,7 +1034,7 @@ pub fn sinex_test(attr: TokenStream, item: TokenStream) -> TokenStream { sinex_test_utils::snapshot_helper::persist_failure( test_name, format!("{err:?}"), - None, + sinex_test_utils::snapshot_helper::FailureContext::None, ); } } diff --git a/crate/lib/sinex-test-utils/src/lib.rs b/crate/lib/sinex-test-utils/src/lib.rs index 5f5a80dbd5..c2e27ac4b0 100644 --- a/crate/lib/sinex-test-utils/src/lib.rs +++ b/crate/lib/sinex-test-utils/src/lib.rs @@ -24,10 +24,20 @@ pub use sinex_core::types::error::SinexError; // Library Result type using SinexError pub type Result = std::result::Result; pub type TestResult = color_eyre::eyre::Result; +pub use test_context::TestContextFailureSnapshot; pub struct ProptestCasesGuard { previous: Option, } +/// Internal helper used by the sinex_prop macro to build a configured TestRunner. +pub fn sinex_prop_runner_config( + default_cases: u32, + module_path: &'static str, + test_name: &str, +) -> proptest::test_runner::Config { + property_testing::build_runner_config(default_cases, module_path, test_name) +} + impl ProptestCasesGuard { pub fn new(cases: u32) -> Self { let previous = std::env::var("PROPTEST_CASES").ok(); diff --git a/crate/lib/sinex-test-utils/src/property_testing.rs b/crate/lib/sinex-test-utils/src/property_testing.rs index 0e7beef2e8..84a0c4cc49 100644 --- a/crate/lib/sinex-test-utils/src/property_testing.rs +++ b/crate/lib/sinex-test-utils/src/property_testing.rs @@ -3,9 +3,12 @@ // Provides property-based testing capabilities that integrate seamlessly // with the unified test infrastructure and event builders. +use once_cell::sync::Lazy; use proptest::prelude::*; use proptest::strategy::{BoxedStrategy, Strategy}; +use proptest::test_runner::{Config as ProptestConfig, FileFailurePersistence}; use serde_json::{json, Value}; +use std::{collections::HashMap, env, fs, path::PathBuf, sync::Mutex}; /// Property test strategies for common Sinex types pub struct SinexStrategies; @@ -188,10 +191,92 @@ impl SinexStrategies { } } +static PERSISTENCE_CACHE: Lazy>> = + Lazy::new(|| Mutex::new(HashMap::new())); + +pub(crate) fn build_runner_config( + default_cases: u32, + module_path: &'static str, + test_name: &str, +) -> ProptestConfig { + let mut cfg = ProptestConfig::default(); + cfg.cases = default_cases; + if let Some(override_cases) = env_proptest_case_override() { + cfg.cases = override_cases; + } + if let Some(path) = regression_file_path(module_path, test_name) { + cfg.failure_persistence = Some(Box::new(FileFailurePersistence::Direct(path))); + } + cfg +} + +fn env_proptest_case_override() -> Option { + env::var("SINEX_PROPTEST_CASES") + .ok() + .and_then(|raw| raw.parse::().ok()) +} + +fn regression_file_path(module_path: &str, test_name: &str) -> Option<&'static str> { + let mut path = env::var("SINEX_PROPTEST_DIR") + .ok() + .filter(|value| !value.trim().is_empty()) + .map(PathBuf::from) + .unwrap_or_else(|| PathBuf::from("target/proptest-regressions")); + + for segment in module_path + .split("::") + .filter(|segment| !segment.is_empty()) + { + path.push(sanitize_component(segment)); + } + + let file_name = format!("{}.proptest-regressions", sanitize_component(test_name)); + path.push(file_name); + + if let Some(parent) = path.parent() { + if let Err(err) = fs::create_dir_all(parent) { + eprintln!( + "sinex_test_utils: failed to create proptest directory {}: {err}", + parent.display() + ); + return None; + } + } + + Some(cache_leaked_path(path)) +} + +fn sanitize_component(input: &str) -> String { + input + .chars() + .map(|ch| { + if ch.is_ascii_alphanumeric() || ch == '-' || ch == '_' { + ch + } else { + '_' + } + }) + .collect() +} + +fn cache_leaked_path(path: PathBuf) -> &'static str { + let path_string = path.to_string_lossy().into_owned(); + let mut cache = PERSISTENCE_CACHE + .lock() + .expect("sinex proptest persistence cache poisoned"); + if let Some(existing) = cache.get(&path_string) { + return existing; + } + let leaked: &'static str = Box::leak(path_string.clone().into_boxed_str()); + cache.insert(path_string, leaked); + leaked +} + #[cfg(test)] mod tests { use super::*; use crate::{sinex_prop, TestContext, TestResult}; + use color_eyre::eyre::Report; #[sinex_prop(cases = 8)] async fn property_creates_filesystem_events( @@ -202,7 +287,7 @@ mod tests { let event = ctx.create_test_event(&source, &event_type, payload).await?; assert_eq!(event.source.as_str(), "filesystem"); assert!(event.id.is_some()); - Ok(()) + Ok::<(), Report>(()) } #[sinex_prop(cases = 8, seed = 42)] @@ -216,7 +301,7 @@ mod tests { let mut expected = payload.clone(); TestContext::sanitize_payload(&mut expected); assert_eq!(inserted.payload, expected); - Ok(()) + Ok::<(), Report>(()) } #[sinex_prop(cases = 16)] @@ -229,6 +314,6 @@ mod tests { .all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '.' || c == '_'), "source not normalized: {source}", ); - Ok(()) + Ok::<(), Report>(()) } } diff --git a/crate/lib/sinex-test-utils/src/snapshot_helper.rs b/crate/lib/sinex-test-utils/src/snapshot_helper.rs index 538e22c0e5..ccbc78c44f 100644 --- a/crate/lib/sinex-test-utils/src/snapshot_helper.rs +++ b/crate/lib/sinex-test-utils/src/snapshot_helper.rs @@ -1,7 +1,7 @@ //! Snapshot testing helpers using insta use crate::database_pool::get_pool_stats; -use crate::TestContext; +use crate::{TestContext, TestContextFailureSnapshot}; use chrono::Utc; use serde::Serialize; use std::env; @@ -51,26 +51,32 @@ impl Default for SnapshotTestHelper { } #[derive(Serialize)] -struct FailureSnapshot<'a> { - test: &'a str, +struct FailureSnapshot { + test: String, error: String, timestamp: String, pool: crate::database_pool::PoolStats, - context: Option>, + context: Option, logs: Option>, } #[derive(Serialize)] -struct TestContextSnapshot<'a> { - name: &'a str, +struct ContextSnapshot { + name: String, baseline_events: i64, elapsed_ms: u128, } +pub enum FailureContext<'a> { + None, + Borrowed(&'a TestContext), + Snapshot(TestContextFailureSnapshot), +} + /// Persist contextual information about a failing test. Artifacts are written to /// `target/test-artifacts/` by default and can be overridden via the /// `SINEX_TEST_FAIL_DIR` environment variable. -pub fn persist_failure(test_name: &str, error: impl Into, ctx: Option<&TestContext>) { +pub fn persist_failure(test_name: &str, error: impl Into, ctx: FailureContext<'_>) { let snapshot_dir = env::var("SINEX_TEST_FAIL_DIR") .map(PathBuf::from) .unwrap_or_else(|_| PathBuf::from("target/test-artifacts")); @@ -83,15 +89,28 @@ pub fn persist_failure(test_name: &str, error: impl Into, ctx: Option<&T return; } - let ctx_snapshot = ctx.map(|c| TestContextSnapshot { - name: c.test_name(), - baseline_events: c.baseline_event_count(), - elapsed_ms: c.elapsed().as_millis(), - }); - let logs = ctx.map(|c| c.captured_logs()); + let (ctx_snapshot, logs) = match ctx { + FailureContext::None => (None, None), + FailureContext::Borrowed(ctx) => ( + Some(ContextSnapshot { + name: ctx.test_name().to_string(), + baseline_events: ctx.baseline_event_count(), + elapsed_ms: ctx.elapsed().as_millis(), + }), + Some(ctx.captured_logs()), + ), + FailureContext::Snapshot(snapshot) => ( + Some(ContextSnapshot { + name: snapshot.test_name().to_string(), + baseline_events: snapshot.baseline_events(), + elapsed_ms: snapshot.elapsed_ms(), + }), + Some(snapshot.captured_logs()), + ), + }; let snapshot = FailureSnapshot { - test: test_name, + test: test_name.to_string(), error: error.into(), timestamp: Utc::now().to_rfc3339(), pool: get_pool_stats(), diff --git a/crate/lib/sinex-test-utils/src/test_context.rs b/crate/lib/sinex-test-utils/src/test_context.rs index 05609ec4f7..e610ead526 100644 --- a/crate/lib/sinex-test-utils/src/test_context.rs +++ b/crate/lib/sinex-test-utils/src/test_context.rs @@ -108,6 +108,32 @@ pub struct TestContext { env: SinexEnvironment, } +#[derive(Clone)] +pub struct TestContextFailureSnapshot { + test_name: String, + baseline_events: i64, + start_time: Instant, + captured_logs: Arc>>, +} + +impl TestContextFailureSnapshot { + pub fn test_name(&self) -> &str { + &self.test_name + } + + pub fn baseline_events(&self) -> i64 { + self.baseline_events + } + + pub fn elapsed_ms(&self) -> u128 { + self.start_time.elapsed().as_millis() + } + + pub fn captured_logs(&self) -> Vec { + self.captured_logs.lock().clone() + } +} + impl TestContext { /// Backwards-compatible accessor for the shared database pool. pub fn pool(&self) -> &DbPool { @@ -311,6 +337,16 @@ impl TestContext { self.baseline_events } + /// Capture snapshot metadata that survives if the context is moved. + pub fn failure_snapshot(&self) -> TestContextFailureSnapshot { + TestContextFailureSnapshot { + test_name: self.test_name.clone(), + baseline_events: self.baseline_events, + start_time: self.start_time, + captured_logs: Arc::clone(&self.captured_logs), + } + } + /// Current total number of events pub async fn current_event_count(&self) -> TestResult { Ok(self.pool.events().count_all().await?) diff --git a/devenv.nix b/devenv.nix index e0f94f22ca..c10dd68d25 100644 --- a/devenv.nix +++ b/devenv.nix @@ -1,167 +1,12 @@ -{ inputs, pkgs, lib, config, ... }: +{ inputs ? {}, pkgs, lib, config, ... }: let system = pkgs.stdenv.hostPlatform.system; - fenixPkgs = inputs.fenix.packages.${system}.complete; - pythonDeps = with pkgs.python3Packages; [ - click - psycopg2 - rich - pyyaml - ]; - ttok = pkgs.python3Packages.buildPythonPackage rec { - pname = "ttok"; - version = "0.3"; - format = "setuptools"; - src = pkgs.python3Packages.fetchPypi { - inherit pname version; - sha256 = "sha256-BHSgCldHYNsiTSSur6UOG56t9qV056bBMkZYvZuCSbg="; - }; - propagatedBuildInputs = with pkgs.python3Packages; [ tiktoken ]; - }; - basePackages = with pkgs; [ - fenixPkgs.toolchain - fenixPkgs.rust-analyzer - fenixPkgs.clippy - fenixPkgs.rustfmt - fenixPkgs.llvm-tools - fenixPkgs.rust-src - fenixPkgs.rustc-codegen-cranelift - cargo-watch - cargo-nextest - cargo-llvm-cov - cargo-tarpaulin - cargo-modules - bacon - tokei - cargo-audit - cargo-machete - sqlx-cli - mold - python3 - nats-server - postgresql_16 - mprocs - btop - jq - protobuf - openssl - pkg-config - dbus - dbus.dev - qemu - qemu_kvm - git-annex - fd - fzf - bat - ripgrep - ] ++ pythonDeps ++ [ ttok ]; - dbusLibPath = pkgs.lib.makeLibraryPath [ pkgs.dbus ]; -in { - packages = basePackages; - - env = { - DATABASE_NAME = "sinex_dev"; - DATABASE_URL = "postgresql:///sinex_dev?host=/run/postgresql"; - PGHOST = "/run/postgresql"; - PGUSER = "sinity"; - PGDATABASE = "sinex_dev"; - SINEX_TEST_OPTIMIZATIONS = "true"; - NATS_SERVER_BIN = "${pkgs.nats-server}/bin/nats-server"; - DEVENV_TASKS_QUIET = "1"; - DEVENV_CMDLINE = "--quiet"; - SINEX_DEVENV_SYSTEM = system; - SINEX_DEVENV_TOOLCHAIN = "fenix (${system})"; - SINEX_DEVENV_PROCESS_HINT = "devenv up nats ingestd gateway"; - }; - - enterShell = '' - export PATH="$PWD/target/debug:$PATH" - export LD_LIBRARY_PATH="${dbusLibPath}''${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" - - if [ -x "$PWD/scripts/dev-env-banner.sh" ] && [ -z "''${SINEX_DEVENV_MOTD_ONCE:-}" ]; then - "$PWD/scripts/dev-env-banner.sh" || true - export SINEX_DEVENV_MOTD_ONCE=1 - fi - alias sinex-cli="python3 cli/exo.py" - alias e2e-test="cargo nextest run -p sinex-e2e-tests" - alias vm-smoke="./tests/e2e/nixos-vm/run-vm-tests.sh -c smoke" - ''; - - tasks = { - "dev:fmt".exec = "cargo fmt --all"; - "dev:lint".exec = "cargo clippy --workspace --all-targets --all-features -- -D warnings"; - "dev:check".exec = "cargo check --workspace --all-features"; - "dev:build".exec = "cargo build --workspace"; - - "dev:test".exec = '' - SINEX_ALLOW_NATIVE_TESTS=1 PROPTEST_CASES=''${PROPTEST_CASES:-64} cargo nextest run --workspace --profile reliable - ''; - - "test:all".exec = '' - devenv tasks run db:setup - LD_LIBRARY_PATH="$(pkg-config --variable=libdir dbus-1)''${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" \ - RUST_LOG=''${RUST_LOG:-} cargo nextest run --workspace --profile reliable - ''; - - "test:vm".exec = "./tests/e2e/nixos-vm/run-vm-tests.sh -c smoke"; - - "dev:coverage".exec = '' - cargo tarpaulin --workspace --out Html --output-dir target/coverage - echo "Coverage report: target/coverage/index.html" - ''; - - "dev:docs".exec = "cargo doc --workspace --no-deps --open"; - "dev:watch".exec = "bacon"; - "dev:stats".exec = "tokei"; - "dev:update".exec = "cargo update"; - "dev:audit".exec = "cargo audit"; - "dev:unused".exec = "cargo machete"; - - "db:migrate".exec = '' - (cd crate/lib/sinex-schema && DATABASE_URL="$DATABASE_URL" cargo run -- up) - ''; - - "db:status".exec = '' - (cd crate/lib/sinex-schema && DATABASE_URL="$DATABASE_URL" cargo run -- status) - ''; - - "db:reset".exec = '' - dropdb --if-exists --force "$DATABASE_NAME" - createdb "$DATABASE_NAME" - devenv tasks run db:migrate - ''; - - "db:setup".exec = '' - createdb "$DATABASE_NAME" 2>/dev/null || true - devenv tasks run db:migrate - ''; - - "db:psql".exec = "psql \"$DATABASE_URL\""; - - "sqlx:prepare".exec = '' - devenv tasks run db:migrate - cargo sqlx prepare --workspace -- --all-targets - echo "✅ SQLX cache updated - commit .sqlx/" - ''; - - "sqlx:check".exec = "cargo sqlx prepare --workspace --check -- --all-targets"; - - "cli:query".exec = '' - LIMIT="''${LIMIT:-10}" ./cli/exo.py query --limit "$LIMIT" - ''; - }; - - processes = { - nats.exec = "${pkgs.nats-server}/bin/nats-server -js"; - ingestd.exec = "cargo run --bin sinex-ingestd"; - gateway.exec = "cargo run --bin sinex-gateway"; - fs-watcher.exec = "cargo run --bin sinex-fs-watcher"; - terminal.exec = "cargo run --bin sinex-terminal-satellite"; - desktop.exec = "cargo run --bin sinex-desktop-satellite"; - system.exec = "cargo run --bin sinex-system-satellite"; - canonicalizer.exec = "cargo run --bin sinex-terminal-command-canonicalizer"; - health.exec = "cargo run --bin sinex-health-aggregator"; - document.exec = "cargo run --bin sinex-document-ingestor"; - }; + fenixInput = + if inputs ? fenix then inputs.fenix + else builtins.getFlake (builtins.fetchTarball { + url = "https://github.com/nix-community/fenix/archive/refs/heads/master.tar.gz"; + sha256 = "1xlziab9wrds2n49mk1q621rf9nwbymrj5ssqb2rwixjzz5k67cz"; + }); + fenixPkgs = fenixInput.packages.${system}.complete; + ... } diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000000..dd9df62433 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,4 @@ +ignore = [ + "crate/lib/sinex-core/tests/property/event_model_fuzzing_test.rs", + "crate/lib/sinex-core/tests/property/ulid_property_test.rs", +] From 9ce766ce09243d958c8b15a7a7dae9b095a15bf3 Mon Sep 17 00:00:00 2001 From: Sinity Date: Thu, 20 Nov 2025 08:41:04 +0100 Subject: [PATCH 08/32] fix(devenv): restore the full devenv configuration behind the fenix fallback Bring back the package, task, environment, and process definitions after the CI migration reduced `devenv.nix` to a bootstrap stub. Keep the optional-input fallback in place. --- devenv.nix | 163 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 162 insertions(+), 1 deletion(-) diff --git a/devenv.nix b/devenv.nix index c10dd68d25..9e400bc898 100644 --- a/devenv.nix +++ b/devenv.nix @@ -8,5 +8,166 @@ let sha256 = "1xlziab9wrds2n49mk1q621rf9nwbymrj5ssqb2rwixjzz5k67cz"; }); fenixPkgs = fenixInput.packages.${system}.complete; - ... + pythonDeps = with pkgs.python3Packages; [ + click + psycopg2 + rich + pyyaml + ]; + ttok = pkgs.python3Packages.buildPythonPackage rec { + pname = "ttok"; + version = "0.3"; + format = "setuptools"; + src = pkgs.python3Packages.fetchPypi { + inherit pname version; + sha256 = "sha256-BHSgCldHYNsiTSSur6UOG56t9qV056bBMkZYvZuCSbg="; + }; + propagatedBuildInputs = with pkgs.python3Packages; [ tiktoken ]; + }; + basePackages = with pkgs; [ + fenixPkgs.toolchain + fenixPkgs.rust-analyzer + fenixPkgs.clippy + fenixPkgs.rustfmt + fenixPkgs.llvm-tools + fenixPkgs.rust-src + fenixPkgs.rustc-codegen-cranelift + cargo-watch + cargo-nextest + cargo-llvm-cov + cargo-tarpaulin + cargo-modules + bacon + tokei + cargo-audit + cargo-machete + sqlx-cli + mold + python3 + nats-server + postgresql_16 + mprocs + btop + jq + protobuf + openssl + pkg-config + dbus + dbus.dev + qemu + qemu_kvm + git-annex + fd + fzf + bat + ripgrep + ] ++ pythonDeps ++ [ ttok ]; + dbusLibPath = pkgs.lib.makeLibraryPath [ pkgs.dbus ]; +in { + packages = basePackages; + + env = { + DATABASE_NAME = "sinex_dev"; + DATABASE_URL = "postgresql:///sinex_dev?host=/run/postgresql"; + PGHOST = "/run/postgresql"; + PGUSER = "sinity"; + PGDATABASE = "sinex_dev"; + SINEX_TEST_OPTIMIZATIONS = "true"; + NATS_SERVER_BIN = "${pkgs.nats-server}/bin/nats-server"; + DEVENV_TASKS_QUIET = "1"; + DEVENV_CMDLINE = "--quiet"; + SINEX_DEVENV_SYSTEM = system; + SINEX_DEVENV_TOOLCHAIN = "fenix (${system})"; + SINEX_DEVENV_PROCESS_HINT = "devenv up nats ingestd gateway"; + }; + + enterShell = '' + export PATH="$PWD/target/debug:$PATH" + export LD_LIBRARY_PATH="${dbusLibPath}''${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" + + if [ -x "$PWD/scripts/dev-env-banner.sh" ] && [ -z "''${SINEX_DEVENV_MOTD_ONCE:-}" ]; then + "$PWD/scripts/dev-env-banner.sh" || true + export SINEX_DEVENV_MOTD_ONCE=1 + fi + alias sinex-cli="python3 cli/exo.py" + alias e2e-test="cargo nextest run -p sinex-e2e-tests" + alias vm-smoke="./tests/e2e/nixos-vm/run-vm-tests.sh -c smoke" + ''; + + tasks = { + "dev:fmt".exec = "cargo fmt --all"; + "dev:lint".exec = "cargo clippy --workspace --all-targets --all-features -- -D warnings"; + "dev:check".exec = "cargo check --workspace --all-features"; + "dev:build".exec = "cargo build --workspace"; + + "dev:test".exec = '' + SINEX_ALLOW_NATIVE_TESTS=1 PROPTEST_CASES=''${PROPTEST_CASES:-64} cargo nextest run --workspace --profile reliable + ''; + + "test:all".exec = '' + devenv tasks run db:setup + LD_LIBRARY_PATH="$(pkg-config --variable=libdir dbus-1)''${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" \ + RUST_LOG=''${RUST_LOG:-} cargo nextest run --workspace --profile reliable + ''; + + "test:vm".exec = "./tests/e2e/nixos-vm/run-vm-tests.sh -c smoke"; + + "dev:coverage".exec = '' + cargo tarpaulin --workspace --out Html --output-dir target/coverage + echo "Coverage report: target/coverage/index.html" + ''; + + "dev:docs".exec = "cargo doc --workspace --no-deps --open"; + "dev:watch".exec = "bacon"; + "dev:stats".exec = "tokei"; + "dev:update".exec = "cargo update"; + "dev:audit".exec = "cargo audit"; + "dev:unused".exec = "cargo machete"; + + "db:migrate".exec = '' + (cd crate/lib/sinex-schema && DATABASE_URL="$DATABASE_URL" cargo run -- up) + ''; + + "db:status".exec = '' + (cd crate/lib/sinex-schema && DATABASE_URL="$DATABASE_URL" cargo run -- status) + ''; + + "db:reset".exec = '' + dropdb --if-exists --force "$DATABASE_NAME" + createdb "$DATABASE_NAME" + devenv tasks run db:migrate + ''; + + "db:setup".exec = '' + createdb "$DATABASE_NAME" 2>/dev/null || true + devenv tasks run db:migrate + ''; + + "db:psql".exec = "psql \"$DATABASE_URL\""; + + "sqlx:prepare".exec = '' + devenv tasks run db:migrate + cargo sqlx prepare --workspace -- --all-targets + echo "✅ SQLX cache updated - commit .sqlx/" + ''; + + "sqlx:check".exec = "cargo sqlx prepare --workspace --check -- --all-targets"; + + "cli:query".exec = '' + LIMIT="''${LIMIT:-10}" ./cli/exo.py query --limit "$LIMIT" + ''; + }; + + processes = { + nats.exec = "${pkgs.nats-server}/bin/nats-server -js"; + ingestd.exec = "cargo run --bin sinex-ingestd"; + gateway.exec = "cargo run --bin sinex-gateway"; + fs-watcher.exec = "cargo run --bin sinex-fs-watcher"; + terminal.exec = "cargo run --bin sinex-terminal-satellite"; + desktop.exec = "cargo run --bin sinex-desktop-satellite"; + system.exec = "cargo run --bin sinex-system-satellite"; + canonicalizer.exec = "cargo run --bin sinex-terminal-command-canonicalizer"; + health.exec = "cargo run --bin sinex-health-aggregator"; + document.exec = "cargo run --bin sinex-document-ingestor"; + }; } From 7cae58e8818425ee3b61c2bf2fe8b671459f4edf Mon Sep 17 00:00:00 2001 From: Sinity Date: Thu, 20 Nov 2025 08:43:52 +0100 Subject: [PATCH 09/32] fix(devenv): pin the fenix fallback input Switch the fallback from tracking `master.tar.gz` to a fixed fenix revision and `narHash` so the dev shell remains reproducible without the flake input. --- devenv.nix | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/devenv.nix b/devenv.nix index 9e400bc898..1493bcfd7a 100644 --- a/devenv.nix +++ b/devenv.nix @@ -3,10 +3,11 @@ let system = pkgs.stdenv.hostPlatform.system; fenixInput = if inputs ? fenix then inputs.fenix - else builtins.getFlake (builtins.fetchTarball { - url = "https://github.com/nix-community/fenix/archive/refs/heads/master.tar.gz"; - sha256 = "1xlziab9wrds2n49mk1q621rf9nwbymrj5ssqb2rwixjzz5k67cz"; - }); + else builtins.getFlake { + url = "github:nix-community/fenix"; + rev = "d4e14d370b4763c67ea02a39f01f5366297d61cb"; + narHash = "sha256-nx0zy/+yR57FwloXmatf3CaXgzA4zJqIFbplnpaKn/Y="; + }; fenixPkgs = fenixInput.packages.${system}.complete; pythonDeps = with pkgs.python3Packages; [ click From 1447ebfccfdc4f2f4222cac3aaef8b4c5dc5ca89 Mon Sep 17 00:00:00 2001 From: Sinity Date: Thu, 20 Nov 2025 08:46:45 +0100 Subject: [PATCH 10/32] fix(devenv): use a valid pinned fenix fallback reference Replace the structured `builtins.getFlake` attrset with the GitHub URL form that Nix accepts, keeping the fallback fenix input usable when `inputs.fenix` is absent. --- devenv.nix | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/devenv.nix b/devenv.nix index 1493bcfd7a..3c14b4a62b 100644 --- a/devenv.nix +++ b/devenv.nix @@ -3,11 +3,7 @@ let system = pkgs.stdenv.hostPlatform.system; fenixInput = if inputs ? fenix then inputs.fenix - else builtins.getFlake { - url = "github:nix-community/fenix"; - rev = "d4e14d370b4763c67ea02a39f01f5366297d61cb"; - narHash = "sha256-nx0zy/+yR57FwloXmatf3CaXgzA4zJqIFbplnpaKn/Y="; - }; + else builtins.getFlake "github:nix-community/fenix?rev=d4e14d370b4763c67ea02a39f01f5366297d61cb&narHash=sha256-nx0zy/+yR57FwloXmatf3CaXgzA4zJqIFbplnpaKn/Y="; fenixPkgs = fenixInput.packages.${system}.complete; pythonDeps = with pkgs.python3Packages; [ click From cb816ab9543f94baf8747f8b0a2a31f391ec8dfa Mon Sep 17 00:00:00 2001 From: Sinity Date: Thu, 20 Nov 2025 08:53:37 +0100 Subject: [PATCH 11/32] ci(workflows): execute test bootstrap inside nix develop - Move the migration/test sequence into a `nix develop` shell so it sees the same tools and environment as local `devenv` usage. - Keep the Postgres bootstrap and task execution in one supported shell context. --- .github/workflows/ci.yml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f053f3ade3..468bdca1a2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,9 +13,6 @@ jobs: test: name: Devenv Integration Test runs-on: ubuntu-latest - defaults: - run: - shell: devenv shell bash -- -e {0} steps: - uses: actions/checkout@v5 @@ -34,18 +31,19 @@ jobs: shell: bash run: nix profile install nixpkgs#devenv - - name: Build devenv shell and run hooks - run: devenv test - - name: Run migrations and tests + shell: bash run: | + nix develop --command bash <<'SCRIPT' set -euo pipefail + devenv test + export PGDATA="$PWD/postgres_data" rm -rf "$PGDATA" mkdir -p "$PGDATA" initdb --auth=trust --no-locale --encoding=UTF8 - echo "unix_socket_directories = ''$PWD''" >> "$PGDATA/postgresql.conf" - pg_ctl start -l postgres.log -o "-k ''$PWD''" + echo "unix_socket_directories = '$PWD'" >> "$PGDATA/postgresql.conf" + pg_ctl start -l postgres.log -o "-k '$PWD'" trap 'pg_ctl stop' EXIT createuser -h "$PWD" sinity 2>/dev/null || true createdb -h "$PWD" -O sinity sinex_dev 2>/dev/null || true @@ -60,3 +58,4 @@ jobs: exit 1 fi devenv tasks run dev:test + SCRIPT From 1071878b2b9234eef65c21d68e140d79070ad986 Mon Sep 17 00:00:00 2001 From: Sinity Date: Thu, 20 Nov 2025 08:56:58 +0100 Subject: [PATCH 12/32] fix(ci): run nix develop with --no-pure-eval Add `--no-pure-eval` to the GitHub Actions `nix develop` invocation so the devenv-based workflow can evaluate the local flake in CI. --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 468bdca1a2..0b12fda058 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -34,7 +34,7 @@ jobs: - name: Run migrations and tests shell: bash run: | - nix develop --command bash <<'SCRIPT' + nix develop --no-pure-eval --command bash <<'SCRIPT' set -euo pipefail devenv test From 63fc988a77132cc51018c8657cfa91f792a7703a Mon Sep 17 00:00:00 2001 From: Sinity Date: Thu, 20 Nov 2025 09:17:07 +0100 Subject: [PATCH 13/32] fix(ci): probe Postgres socket startup before running tests Tighten the main CI workflow to quote `pg_ctl` socket flags correctly and run an explicit `psql` probe after startup. This closes the immediate startup race uncovered by the explicit SQL bootstrap change. --- .github/workflows/ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0b12fda058..1563fc00b2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,10 +43,11 @@ jobs: mkdir -p "$PGDATA" initdb --auth=trust --no-locale --encoding=UTF8 echo "unix_socket_directories = '$PWD'" >> "$PGDATA/postgresql.conf" - pg_ctl start -l postgres.log -o "-k '$PWD'" + pg_ctl start -l postgres.log -o "-k $PWD" trap 'pg_ctl stop' EXIT createuser -h "$PWD" sinity 2>/dev/null || true createdb -h "$PWD" -O sinity sinex_dev 2>/dev/null || true + psql -h "$PWD" -U sinity -d sinex_dev -c 'SELECT 1' >/dev/null export PGHOST="$PWD" export DATABASE_URL="postgresql:///sinex_dev?host=$PWD" devenv tasks run db:migrate From b3266533278b53ce30ecfa2a37483032b26db007 Mon Sep 17 00:00:00 2001 From: Sinity Date: Thu, 20 Nov 2025 09:35:37 +0100 Subject: [PATCH 14/32] ci: provision the sinity role and database with explicit SQL Replace `createuser` and `createdb` calls in the main CI workflow with direct `psql` blocks that create the role and database when missing. This makes the main CI job match the more explicit bootstrap style used in the sibling workflows. --- .github/workflows/ci.yml | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1563fc00b2..10a58bfd7e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -45,8 +45,20 @@ jobs: echo "unix_socket_directories = '$PWD'" >> "$PGDATA/postgresql.conf" pg_ctl start -l postgres.log -o "-k $PWD" trap 'pg_ctl stop' EXIT - createuser -h "$PWD" sinity 2>/dev/null || true - createdb -h "$PWD" -O sinity sinex_dev 2>/dev/null || true + psql -h "$PWD" -d postgres <<'SQL' +DO $$ +BEGIN + IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'sinity') THEN + CREATE ROLE sinity LOGIN CREATEDB; + END IF; +END$$; +DO $$ +BEGIN + IF NOT EXISTS (SELECT FROM pg_database WHERE datname = 'sinex_dev') THEN + CREATE DATABASE sinex_dev OWNER sinity; + END IF; +END$$; +SQL psql -h "$PWD" -U sinity -d sinex_dev -c 'SELECT 1' >/dev/null export PGHOST="$PWD" export DATABASE_URL="postgresql:///sinex_dev?host=$PWD" From 4e562c60229c3f16dedeef5b5401f30f7001c279 Mon Sep 17 00:00:00 2001 From: Sinity Date: Thu, 20 Nov 2025 09:48:03 +0100 Subject: [PATCH 15/32] ci: run schema and SQLx validation on Nix-managed Postgres Replace GitHub service-container Postgres with locally bootstrapped Nix-managed clusters in the schema-validation and SQLx workflows. This is the large pivot that the following commits keep hardening around permissions, sockets, and startup behavior. --- .github/workflows/schema-validation.yml | 144 ++++++++++++------------ .github/workflows/sqlx-cache.yml | 108 ++++++++++-------- .github/workflows/sqlx-check.yml | 121 +++++++++----------- 3 files changed, 182 insertions(+), 191 deletions(-) diff --git a/.github/workflows/schema-validation.yml b/.github/workflows/schema-validation.yml index 0085fcc5c1..69c0a9ff14 100644 --- a/.github/workflows/schema-validation.yml +++ b/.github/workflows/schema-validation.yml @@ -22,83 +22,77 @@ env: jobs: validate-schemas: runs-on: ubuntu-latest - - services: - postgres: - image: timescale/timescaledb:2.15.2-pg16 # Keep validation jobs in lock-step with the pinned CI version - env: - POSTGRES_PASSWORD: postgres - POSTGRES_DB: sinex_test - options: >- - --health-cmd pg_isready - --health-interval 10s - --health-timeout 5s - --health-retries 5 - ports: - - 5432:5432 - + steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install Nix - uses: DeterminateSystems/nix-installer-action@v12 - - - name: Setup Nix cache - uses: DeterminateSystems/magic-nix-cache-action@v7 - - - name: Install PostgreSQL extensions - run: | - sudo apt-get update - sudo apt-get install -y postgresql-client - - # Wait for PostgreSQL to be ready - until pg_isready -h localhost -p 5432 -U postgres; do - echo "Waiting for PostgreSQL..." - sleep 2 - done - - # Install required extensions - PGPASSWORD=postgres psql -h localhost -U postgres -d sinex_test -c " - CREATE EXTENSION IF NOT EXISTS pgx_ulid CASCADE; - CREATE EXTENSION IF NOT EXISTS pg_jsonschema CASCADE; - CREATE EXTENSION IF NOT EXISTS timescaledb CASCADE; - CREATE EXTENSION IF NOT EXISTS pgvector CASCADE; - " || echo "Extensions installation completed with warnings" - - - name: Run database migrations - run: | - export DATABASE_URL="postgresql://postgres:postgres@localhost:5432/sinex_test" - cd crate/lib/sinex-schema - nix develop -c cargo run -- up - - - name: Test schema registry functionality - run: | - export DATABASE_URL="postgresql://postgres:postgres@localhost:5432/sinex_test" - - # Test that schema registry table exists and works - PGPASSWORD=postgres psql -h localhost -U postgres -d sinex_test -c " - -- Test schema insertion - INSERT INTO sinex_schemas.event_payload_schemas - (source, event_type, schema_version, schema_content, content_hash) - VALUES - ('test.source', 'test.event', '1.0.0', '{}'::jsonb, md5(random()::text)); - - -- Test schema activation - UPDATE sinex_schemas.event_payload_schemas - SET is_active = true - WHERE source = 'test.source' AND event_type = 'test.event'; - - -- Verify schema exists - SELECT COUNT(*) FROM sinex_schemas.event_payload_schemas - WHERE source = 'test.source'; - " - - - name: Smoke-test schema CLI + - uses: actions/checkout@v5 + + - uses: cachix/install-nix-action@v31 + with: + nix_path: nixpkgs=channel:nixos-unstable + + - uses: cachix/cachix-action@v16 + with: + name: sinity + authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' + skipPush: true + + - name: Install devenv.sh + shell: bash + run: nix profile install nixpkgs#devenv + + - name: Validate schemas inside Nix + shell: bash run: | - export DATABASE_URL="postgresql://postgres:postgres@localhost:5432/sinex_test" - TMP_DIR=$(mktemp -d) - nix develop -c cargo run -p sinex-core --bin sinex-schema --features schema-manager -- generate --output "$TMP_DIR" --sync + nix develop --no-pure-eval --command bash <<'SCRIPT' + set -euo pipefail + + export PGDATA="$PWD/postgres_data" + rm -rf "$PGDATA" + mkdir -p "$PGDATA" + initdb --auth=trust --no-locale --encoding=UTF8 + echo "unix_socket_directories = '$PWD'" >> "$PGDATA/postgresql.conf" + pg_ctl start -l postgres.log -o "-k $PWD" + trap 'pg_ctl stop' EXIT + + psql -h "$PWD" -d postgres <<'SQL' +DO $$ +BEGIN + IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'sinity') THEN + CREATE ROLE sinity LOGIN CREATEDB; + END IF; +END$$; +DO $$ +BEGIN + IF NOT EXISTS (SELECT FROM pg_database WHERE datname = 'sinex_dev') THEN + CREATE DATABASE sinex_dev OWNER sinity; + END IF; +END$$; +SQL + + export PGHOST="$PWD" + export DATABASE_URL="postgresql:///sinex_dev?host=$PWD" + + pushd crate/lib/sinex-schema >/dev/null + cargo run -- up + popd + + psql -h "$PGHOST" -U sinity -d sinex_dev <<'SQL' +INSERT INTO sinex_schemas.event_payload_schemas + (source, event_type, schema_version, schema_content, content_hash) +VALUES + ('test.source', 'test.event', '1.0.0', '{}'::jsonb, md5(random()::text)) +ON CONFLICT (source, event_type, schema_version) DO NOTHING; + +UPDATE sinex_schemas.event_payload_schemas +SET is_active = true +WHERE source = 'test.source' AND event_type = 'test.event'; + +SELECT COUNT(*) FROM sinex_schemas.event_payload_schemas WHERE source = 'test.source'; +SQL + + TMP_DIR=$(mktemp -d) + cargo run -p sinex-core --bin sinex-schema --features schema-manager -- generate --output "$TMP_DIR" --sync + SCRIPT deploy-staging: runs-on: ubuntu-latest diff --git a/.github/workflows/sqlx-cache.yml b/.github/workflows/sqlx-cache.yml index c3386f3c8d..b780b98d70 100644 --- a/.github/workflows/sqlx-cache.yml +++ b/.github/workflows/sqlx-cache.yml @@ -16,56 +16,66 @@ jobs: validate-cache: name: Validate SQLx Query Cache runs-on: ubuntu-latest - - services: - postgres: - image: timescale/timescaledb:2.15.2-pg16 # Keep in sync with main CI runs - env: - POSTGRES_PASSWORD: postgres - POSTGRES_DB: sinex_test - options: >- - --health-cmd pg_isready - --health-interval 10s - --health-timeout 5s - --health-retries 5 - ports: - - 5432:5432 - + steps: - - uses: actions/checkout@v4 - - - name: Install Rust - uses: actions-rs/toolchain@v1 + - uses: actions/checkout@v5 + + - uses: cachix/install-nix-action@v31 with: - toolchain: stable - override: true - - - name: Install sqlx-cli - run: cargo install sqlx-cli --no-default-features --features postgres - - - name: Setup database - env: - DATABASE_URL: postgresql://postgres:postgres@localhost:5432/sinex_test - PGPASSWORD: postgres - run: | - sudo apt-get update && sudo apt-get install -y postgresql-client postgresql-contrib - psql -h localhost -U postgres -c "CREATE USER sinex_test WITH PASSWORD 'test_password' CREATEDB" - psql -h localhost -U postgres -c "CREATE DATABASE sinex_test OWNER sinex_test" - psql -h localhost -U postgres -d sinex_test -c "CREATE EXTENSION IF NOT EXISTS timescaledb" - cd crate/lib/sinex-schema - cargo run -- up - - - name: Check query cache is current - env: - DATABASE_URL: postgresql://postgres:postgres@localhost:5432/sinex_test - run: | - cargo sqlx prepare --check --workspace - - - name: Update cache if outdated - if: failure() - env: - DATABASE_URL: postgresql://postgres:postgres@localhost:5432/sinex_test + nix_path: nixpkgs=channel:nixos-unstable + + - uses: cachix/cachix-action@v16 + with: + name: sinity + authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' + skipPush: true + + - name: Install devenv.sh + shell: bash + run: nix profile install nixpkgs#devenv + + - name: Validate SQLx cache inside Nix + shell: bash run: | + nix develop --no-pure-eval --command bash <<'SCRIPT' + set -euo pipefail + + export PGDATA="$PWD/postgres_data" + rm -rf "$PGDATA" + mkdir -p "$PGDATA" + initdb --auth=trust --no-locale --encoding=UTF8 + echo "unix_socket_directories = '$PWD'" >> "$PGDATA/postgresql.conf" + pg_ctl start -l postgres.log -o "-k $PWD" + trap 'pg_ctl stop' EXIT + + psql -h "$PWD" -d postgres <<'SQL' +DO $$ +BEGIN + IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'sinity') THEN + CREATE ROLE sinity LOGIN CREATEDB; + END IF; +END$$; +DO $$ +BEGIN + IF NOT EXISTS (SELECT FROM pg_database WHERE datname = 'sinex_dev') THEN + CREATE DATABASE sinex_dev OWNER sinity; + END IF; +END$$; +SQL + + export PGHOST="$PWD" + export DATABASE_URL="postgresql:///sinex_dev?host=$PWD" + + devenv tasks run db:migrate + + if cargo sqlx prepare --workspace --check -- --all-targets --all-features; then + exit 0 + fi + echo "::error::SQLx query cache is outdated. Run 'cargo sqlx prepare --workspace' locally and commit the changes." - cargo sqlx prepare --workspace - git diff --exit-code .sqlx || (echo "::error::Cache would be updated with these changes:" && git diff .sqlx && exit 1) + cargo sqlx prepare --workspace -- --all-targets --all-features + if ! git diff --quiet .sqlx; then + git diff .sqlx + fi + exit 1 + SCRIPT diff --git a/.github/workflows/sqlx-check.yml b/.github/workflows/sqlx-check.yml index cf14d5b5e3..9360ed182e 100644 --- a/.github/workflows/sqlx-check.yml +++ b/.github/workflows/sqlx-check.yml @@ -16,75 +16,62 @@ jobs: sqlx-check: name: Verify SQLX offline cache runs-on: ubuntu-latest - - services: - postgres: - image: timescale/timescaledb:2.15.2-pg16 # Align with main CI image - env: - POSTGRES_USER: postgres - POSTGRES_PASSWORD: postgres - POSTGRES_DB: sinex_test - options: >- - --health-cmd pg_isready - --health-interval 10s - --health-timeout 5s - --health-retries 5 - ports: - - 5432:5432 - + steps: - - uses: actions/checkout@v4 - - - name: Install Rust - uses: dtolnay/rust-toolchain@stable - with: - components: rustfmt, clippy - - - name: Cache cargo registry - uses: actions/cache@v4 - with: - path: ~/.cargo/registry - key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }} - - - name: Cache cargo index - uses: actions/cache@v4 + - uses: actions/checkout@v5 + + - uses: cachix/install-nix-action@v31 with: - path: ~/.cargo/git - key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }} - - - name: Cache cargo build - uses: actions/cache@v4 + nix_path: nixpkgs=channel:nixos-unstable + + - uses: cachix/cachix-action@v16 with: - path: target - key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }} - - - name: Install sqlx-cli - run: | - cargo install sqlx-cli --no-default-features --features native-tls,postgres - - - name: Run migrations - env: - DATABASE_URL: postgresql://postgres:postgres@localhost:5432/sinex_test - run: | - cd crate/lib/sinex-schema - cargo run -- up - - - name: Check SQLX offline cache is up to date - env: - DATABASE_URL: postgresql://postgres:postgres@localhost:5432/sinex_test + name: sinity + authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' + skipPush: true + + - name: Install devenv.sh + shell: bash + run: nix profile install nixpkgs#devenv + + - name: Run SQLx checks inside Nix + shell: bash run: | - # Generate fresh cache + nix develop --no-pure-eval --command bash <<'SCRIPT' + set -euo pipefail + + export PGDATA="$PWD/postgres_data" + rm -rf "$PGDATA" + mkdir -p "$PGDATA" + initdb --auth=trust --no-locale --encoding=UTF8 + echo "unix_socket_directories = '$PWD'" >> "$PGDATA/postgresql.conf" + pg_ctl start -l postgres.log -o "-k $PWD" + trap 'pg_ctl stop' EXIT + + psql -h "$PWD" -d postgres <<'SQL' +DO $$ +BEGIN + IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'sinity') THEN + CREATE ROLE sinity LOGIN CREATEDB; + END IF; +END$$; +DO $$ +BEGIN + IF NOT EXISTS (SELECT FROM pg_database WHERE datname = 'sinex_dev') THEN + CREATE DATABASE sinex_dev OWNER sinity; + END IF; +END$$; +SQL + + export PGHOST="$PWD" + export DATABASE_URL="postgresql:///sinex_dev?host=$PWD" + + devenv tasks run db:migrate + cargo sqlx prepare --workspace --check -- --all-targets --all-features - - - name: Verify offline mode compilation - env: - SQLX_OFFLINE: true - run: | - cargo check --all-features - - - name: Run tests with offline mode - env: - SQLX_OFFLINE: true - TEST_DATABASE_URL: postgresql://postgres:postgres@localhost:5432/sinex_test - run: | - cargo nextest run --all-features + + SQLX_OFFLINE=1 cargo check --all-features + + SQLX_OFFLINE=1 TEST_DATABASE_URL="postgresql:///sinex_dev?host=$PWD" \ + cargo nextest run --all-features + SCRIPT From e2cb420e7d31d6a4c09da4fbfe3eb17faff60888 Mon Sep 17 00:00:00 2001 From: Sinity Date: Thu, 20 Nov 2025 09:57:36 +0100 Subject: [PATCH 16/32] ci(workflows): invoke validation jobs through nix wrappers - Push schema and SQLx workflow commands through explicit Nix shell wrappers instead of open-coded shell blocks. - Reduce drift between jobs while the bootstrap path is being standardized. --- .github/workflows/ci.yml | 21 +------ .github/workflows/schema-validation.yml | 76 ++++++++----------------- .github/workflows/sqlx-cache.yml | 22 +------ .github/workflows/sqlx-check.yml | 22 +------ 4 files changed, 34 insertions(+), 107 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 10a58bfd7e..93de4ae09d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,10 +32,8 @@ jobs: run: nix profile install nixpkgs#devenv - name: Run migrations and tests - shell: bash + shell: 'nix develop --no-pure-eval --command bash --noprofile --norc -euo pipefail {0}' run: | - nix develop --no-pure-eval --command bash <<'SCRIPT' - set -euo pipefail devenv test export PGDATA="$PWD/postgres_data" @@ -45,20 +43,8 @@ jobs: echo "unix_socket_directories = '$PWD'" >> "$PGDATA/postgresql.conf" pg_ctl start -l postgres.log -o "-k $PWD" trap 'pg_ctl stop' EXIT - psql -h "$PWD" -d postgres <<'SQL' -DO $$ -BEGIN - IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'sinity') THEN - CREATE ROLE sinity LOGIN CREATEDB; - END IF; -END$$; -DO $$ -BEGIN - IF NOT EXISTS (SELECT FROM pg_database WHERE datname = 'sinex_dev') THEN - CREATE DATABASE sinex_dev OWNER sinity; - END IF; -END$$; -SQL + psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "DO \$\$ BEGIN IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'sinity') THEN CREATE ROLE sinity LOGIN CREATEDB; END IF; END \$\$;" + psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "DO \$\$ BEGIN IF NOT EXISTS (SELECT FROM pg_database WHERE datname = 'sinex_dev') THEN CREATE DATABASE sinex_dev OWNER sinity; END IF; END \$\$;" psql -h "$PWD" -U sinity -d sinex_dev -c 'SELECT 1' >/dev/null export PGHOST="$PWD" export DATABASE_URL="postgresql:///sinex_dev?host=$PWD" @@ -71,4 +57,3 @@ SQL exit 1 fi devenv tasks run dev:test - SCRIPT diff --git a/.github/workflows/schema-validation.yml b/.github/workflows/schema-validation.yml index 69c0a9ff14..ca92dddd1f 100644 --- a/.github/workflows/schema-validation.yml +++ b/.github/workflows/schema-validation.yml @@ -41,58 +41,32 @@ jobs: run: nix profile install nixpkgs#devenv - name: Validate schemas inside Nix - shell: bash + shell: 'nix develop --no-pure-eval --command bash --noprofile --norc -euo pipefail {0}' run: | - nix develop --no-pure-eval --command bash <<'SCRIPT' - set -euo pipefail - - export PGDATA="$PWD/postgres_data" - rm -rf "$PGDATA" - mkdir -p "$PGDATA" - initdb --auth=trust --no-locale --encoding=UTF8 - echo "unix_socket_directories = '$PWD'" >> "$PGDATA/postgresql.conf" - pg_ctl start -l postgres.log -o "-k $PWD" - trap 'pg_ctl stop' EXIT - - psql -h "$PWD" -d postgres <<'SQL' -DO $$ -BEGIN - IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'sinity') THEN - CREATE ROLE sinity LOGIN CREATEDB; - END IF; -END$$; -DO $$ -BEGIN - IF NOT EXISTS (SELECT FROM pg_database WHERE datname = 'sinex_dev') THEN - CREATE DATABASE sinex_dev OWNER sinity; - END IF; -END$$; -SQL - - export PGHOST="$PWD" - export DATABASE_URL="postgresql:///sinex_dev?host=$PWD" - - pushd crate/lib/sinex-schema >/dev/null - cargo run -- up - popd - - psql -h "$PGHOST" -U sinity -d sinex_dev <<'SQL' -INSERT INTO sinex_schemas.event_payload_schemas - (source, event_type, schema_version, schema_content, content_hash) -VALUES - ('test.source', 'test.event', '1.0.0', '{}'::jsonb, md5(random()::text)) -ON CONFLICT (source, event_type, schema_version) DO NOTHING; - -UPDATE sinex_schemas.event_payload_schemas -SET is_active = true -WHERE source = 'test.source' AND event_type = 'test.event'; - -SELECT COUNT(*) FROM sinex_schemas.event_payload_schemas WHERE source = 'test.source'; -SQL - - TMP_DIR=$(mktemp -d) - cargo run -p sinex-core --bin sinex-schema --features schema-manager -- generate --output "$TMP_DIR" --sync - SCRIPT + export PGDATA="$PWD/postgres_data" + rm -rf "$PGDATA" + mkdir -p "$PGDATA" + initdb --auth=trust --no-locale --encoding=UTF8 + echo "unix_socket_directories = '$PWD'" >> "$PGDATA/postgresql.conf" + pg_ctl start -l postgres.log -o "-k $PWD" + trap 'pg_ctl stop' EXIT + + psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "DO \$\$ BEGIN IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'sinity') THEN CREATE ROLE sinity LOGIN CREATEDB; END IF; END \$\$;" + psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "DO \$\$ BEGIN IF NOT EXISTS (SELECT FROM pg_database WHERE datname = 'sinex_dev') THEN CREATE DATABASE sinex_dev OWNER sinity; END IF; END \$\$;" + + export PGHOST="$PWD" + export DATABASE_URL="postgresql:///sinex_dev?host=$PWD" + + pushd crate/lib/sinex-schema >/dev/null + cargo run -- up + popd + + psql -h "$PGHOST" -U sinity -d sinex_dev -v ON_ERROR_STOP=1 -c "INSERT INTO sinex_schemas.event_payload_schemas (source, event_type, schema_version, schema_content, content_hash) VALUES ('test.source', 'test.event', '1.0.0', '{}'::jsonb, md5(random()::text)) ON CONFLICT (source, event_type, schema_version) DO NOTHING;" + psql -h "$PGHOST" -U sinity -d sinex_dev -v ON_ERROR_STOP=1 -c "UPDATE sinex_schemas.event_payload_schemas SET is_active = true WHERE source = 'test.source' AND event_type = 'test.event';" + psql -h "$PGHOST" -U sinity -d sinex_dev -v ON_ERROR_STOP=1 -c "SELECT COUNT(*) FROM sinex_schemas.event_payload_schemas WHERE source = 'test.source';" + + TMP_DIR=$(mktemp -d) + cargo run -p sinex-core --bin sinex-schema --features schema-manager -- generate --output "$TMP_DIR" --sync deploy-staging: runs-on: ubuntu-latest diff --git a/.github/workflows/sqlx-cache.yml b/.github/workflows/sqlx-cache.yml index b780b98d70..98386c1efe 100644 --- a/.github/workflows/sqlx-cache.yml +++ b/.github/workflows/sqlx-cache.yml @@ -35,11 +35,8 @@ jobs: run: nix profile install nixpkgs#devenv - name: Validate SQLx cache inside Nix - shell: bash + shell: 'nix develop --no-pure-eval --command bash --noprofile --norc -euo pipefail {0}' run: | - nix develop --no-pure-eval --command bash <<'SCRIPT' - set -euo pipefail - export PGDATA="$PWD/postgres_data" rm -rf "$PGDATA" mkdir -p "$PGDATA" @@ -48,20 +45,8 @@ jobs: pg_ctl start -l postgres.log -o "-k $PWD" trap 'pg_ctl stop' EXIT - psql -h "$PWD" -d postgres <<'SQL' -DO $$ -BEGIN - IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'sinity') THEN - CREATE ROLE sinity LOGIN CREATEDB; - END IF; -END$$; -DO $$ -BEGIN - IF NOT EXISTS (SELECT FROM pg_database WHERE datname = 'sinex_dev') THEN - CREATE DATABASE sinex_dev OWNER sinity; - END IF; -END$$; -SQL + psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "DO \$\$ BEGIN IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'sinity') THEN CREATE ROLE sinity LOGIN CREATEDB; END IF; END \$\$;" + psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "DO \$\$ BEGIN IF NOT EXISTS (SELECT FROM pg_database WHERE datname = 'sinex_dev') THEN CREATE DATABASE sinex_dev OWNER sinity; END IF; END \$\$;" export PGHOST="$PWD" export DATABASE_URL="postgresql:///sinex_dev?host=$PWD" @@ -78,4 +63,3 @@ SQL git diff .sqlx fi exit 1 - SCRIPT diff --git a/.github/workflows/sqlx-check.yml b/.github/workflows/sqlx-check.yml index 9360ed182e..95a31c26f2 100644 --- a/.github/workflows/sqlx-check.yml +++ b/.github/workflows/sqlx-check.yml @@ -35,11 +35,8 @@ jobs: run: nix profile install nixpkgs#devenv - name: Run SQLx checks inside Nix - shell: bash + shell: 'nix develop --no-pure-eval --command bash --noprofile --norc -euo pipefail {0}' run: | - nix develop --no-pure-eval --command bash <<'SCRIPT' - set -euo pipefail - export PGDATA="$PWD/postgres_data" rm -rf "$PGDATA" mkdir -p "$PGDATA" @@ -48,20 +45,8 @@ jobs: pg_ctl start -l postgres.log -o "-k $PWD" trap 'pg_ctl stop' EXIT - psql -h "$PWD" -d postgres <<'SQL' -DO $$ -BEGIN - IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'sinity') THEN - CREATE ROLE sinity LOGIN CREATEDB; - END IF; -END$$; -DO $$ -BEGIN - IF NOT EXISTS (SELECT FROM pg_database WHERE datname = 'sinex_dev') THEN - CREATE DATABASE sinex_dev OWNER sinity; - END IF; -END$$; -SQL + psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "DO \$\$ BEGIN IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'sinity') THEN CREATE ROLE sinity LOGIN CREATEDB; END IF; END \$\$;" + psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "DO \$\$ BEGIN IF NOT EXISTS (SELECT FROM pg_database WHERE datname = 'sinex_dev') THEN CREATE DATABASE sinex_dev OWNER sinity; END IF; END \$\$;" export PGHOST="$PWD" export DATABASE_URL="postgresql:///sinex_dev?host=$PWD" @@ -74,4 +59,3 @@ SQL SQLX_OFFLINE=1 TEST_DATABASE_URL="postgresql:///sinex_dev?host=$PWD" \ cargo nextest run --all-features - SCRIPT From 67be1f7c025971ed3d5780f2949f9753b6074a4b Mon Sep 17 00:00:00 2001 From: Sinity Date: Thu, 20 Nov 2025 10:04:56 +0100 Subject: [PATCH 17/32] ci: export DEVENV_ROOT for Nix and devenv bootstrap Pass `DEVENV_ROOT` into the workflow environment before invoking the Nix and devenv wrappers. This keeps the root calculation stable across the ongoing CI shell refactors. --- .github/workflows/ci.yml | 3 +++ .github/workflows/schema-validation.yml | 3 +++ .github/workflows/sqlx-cache.yml | 3 +++ .github/workflows/sqlx-check.yml | 3 +++ 4 files changed, 12 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 93de4ae09d..2ab99c8dbb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,6 +33,9 @@ jobs: - name: Run migrations and tests shell: 'nix develop --no-pure-eval --command bash --noprofile --norc -euo pipefail {0}' + env: + DEVENV_ROOT: ${{ github.workspace }} + DEVENV_DOTFILE: ${{ github.workspace }}/.devenv run: | devenv test diff --git a/.github/workflows/schema-validation.yml b/.github/workflows/schema-validation.yml index ca92dddd1f..35c1c82266 100644 --- a/.github/workflows/schema-validation.yml +++ b/.github/workflows/schema-validation.yml @@ -42,6 +42,9 @@ jobs: - name: Validate schemas inside Nix shell: 'nix develop --no-pure-eval --command bash --noprofile --norc -euo pipefail {0}' + env: + DEVENV_ROOT: ${{ github.workspace }} + DEVENV_DOTFILE: ${{ github.workspace }}/.devenv run: | export PGDATA="$PWD/postgres_data" rm -rf "$PGDATA" diff --git a/.github/workflows/sqlx-cache.yml b/.github/workflows/sqlx-cache.yml index 98386c1efe..bd49d54374 100644 --- a/.github/workflows/sqlx-cache.yml +++ b/.github/workflows/sqlx-cache.yml @@ -36,6 +36,9 @@ jobs: - name: Validate SQLx cache inside Nix shell: 'nix develop --no-pure-eval --command bash --noprofile --norc -euo pipefail {0}' + env: + DEVENV_ROOT: ${{ github.workspace }} + DEVENV_DOTFILE: ${{ github.workspace }}/.devenv run: | export PGDATA="$PWD/postgres_data" rm -rf "$PGDATA" diff --git a/.github/workflows/sqlx-check.yml b/.github/workflows/sqlx-check.yml index 95a31c26f2..626c1c921b 100644 --- a/.github/workflows/sqlx-check.yml +++ b/.github/workflows/sqlx-check.yml @@ -36,6 +36,9 @@ jobs: - name: Run SQLx checks inside Nix shell: 'nix develop --no-pure-eval --command bash --noprofile --norc -euo pipefail {0}' + env: + DEVENV_ROOT: ${{ github.workspace }} + DEVENV_DOTFILE: ${{ github.workspace }}/.devenv run: | export PGDATA="$PWD/postgres_data" rm -rf "$PGDATA" From 54832031c4daed2a999d31b5b68cdda2e8e84d1e Mon Sep 17 00:00:00 2001 From: Sinity Date: Thu, 20 Nov 2025 10:11:40 +0100 Subject: [PATCH 18/32] fix(devenv): default the root to the workspace path Set `devenv.root` to the workspace path by default so both local shells and workflows agree on the repository root. This is an enabling tweak for the wrapper-based CI invocations. --- devenv.nix | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/devenv.nix b/devenv.nix index 3c14b4a62b..477890f36d 100644 --- a/devenv.nix +++ b/devenv.nix @@ -61,6 +61,14 @@ let ] ++ pythonDeps ++ [ ttok ]; dbusLibPath = pkgs.lib.makeLibraryPath [ pkgs.dbus ]; in { + devenv = { + root = + let + rootEnv = builtins.getEnv "DEVENV_ROOT"; + in + if rootEnv != "" then rootEnv else toString ./.; + }; + packages = basePackages; env = { From 94914341641f9912bda408c21b792b579b2b3327 Mon Sep 17 00:00:00 2001 From: Sinity Date: Thu, 20 Nov 2025 10:29:51 +0100 Subject: [PATCH 19/32] ci(postgres): force role creation through the postgres user Temporarily run the role and database creation steps as `PGUSER=postgres` across the workflows. This is a short corrective step in the bootstrap iteration before the local-user approach replaces it. --- .github/workflows/ci.yml | 4 ++-- .github/workflows/schema-validation.yml | 4 ++-- .github/workflows/sqlx-cache.yml | 4 ++-- .github/workflows/sqlx-check.yml | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2ab99c8dbb..e1c05dae37 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,8 +46,8 @@ jobs: echo "unix_socket_directories = '$PWD'" >> "$PGDATA/postgresql.conf" pg_ctl start -l postgres.log -o "-k $PWD" trap 'pg_ctl stop' EXIT - psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "DO \$\$ BEGIN IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'sinity') THEN CREATE ROLE sinity LOGIN CREATEDB; END IF; END \$\$;" - psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "DO \$\$ BEGIN IF NOT EXISTS (SELECT FROM pg_database WHERE datname = 'sinex_dev') THEN CREATE DATABASE sinex_dev OWNER sinity; END IF; END \$\$;" + PGUSER=postgres psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "DO \$\$ BEGIN IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'sinity') THEN CREATE ROLE sinity LOGIN CREATEDB; END IF; END \$\$;" + PGUSER=postgres psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "DO \$\$ BEGIN IF NOT EXISTS (SELECT FROM pg_database WHERE datname = 'sinex_dev') THEN CREATE DATABASE sinex_dev OWNER sinity; END IF; END \$\$;" psql -h "$PWD" -U sinity -d sinex_dev -c 'SELECT 1' >/dev/null export PGHOST="$PWD" export DATABASE_URL="postgresql:///sinex_dev?host=$PWD" diff --git a/.github/workflows/schema-validation.yml b/.github/workflows/schema-validation.yml index 35c1c82266..42479b3bcb 100644 --- a/.github/workflows/schema-validation.yml +++ b/.github/workflows/schema-validation.yml @@ -54,8 +54,8 @@ jobs: pg_ctl start -l postgres.log -o "-k $PWD" trap 'pg_ctl stop' EXIT - psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "DO \$\$ BEGIN IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'sinity') THEN CREATE ROLE sinity LOGIN CREATEDB; END IF; END \$\$;" - psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "DO \$\$ BEGIN IF NOT EXISTS (SELECT FROM pg_database WHERE datname = 'sinex_dev') THEN CREATE DATABASE sinex_dev OWNER sinity; END IF; END \$\$;" + PGUSER=postgres psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "DO \$\$ BEGIN IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'sinity') THEN CREATE ROLE sinity LOGIN CREATEDB; END IF; END \$\$;" + PGUSER=postgres psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "DO \$\$ BEGIN IF NOT EXISTS (SELECT FROM pg_database WHERE datname = 'sinex_dev') THEN CREATE DATABASE sinex_dev OWNER sinity; END IF; END \$\$;" export PGHOST="$PWD" export DATABASE_URL="postgresql:///sinex_dev?host=$PWD" diff --git a/.github/workflows/sqlx-cache.yml b/.github/workflows/sqlx-cache.yml index bd49d54374..884b4abdb4 100644 --- a/.github/workflows/sqlx-cache.yml +++ b/.github/workflows/sqlx-cache.yml @@ -48,8 +48,8 @@ jobs: pg_ctl start -l postgres.log -o "-k $PWD" trap 'pg_ctl stop' EXIT - psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "DO \$\$ BEGIN IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'sinity') THEN CREATE ROLE sinity LOGIN CREATEDB; END IF; END \$\$;" - psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "DO \$\$ BEGIN IF NOT EXISTS (SELECT FROM pg_database WHERE datname = 'sinex_dev') THEN CREATE DATABASE sinex_dev OWNER sinity; END IF; END \$\$;" + PGUSER=postgres psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "DO \$\$ BEGIN IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'sinity') THEN CREATE ROLE sinity LOGIN CREATEDB; END IF; END \$\$;" + PGUSER=postgres psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "DO \$\$ BEGIN IF NOT EXISTS (SELECT FROM pg_database WHERE datname = 'sinex_dev') THEN CREATE DATABASE sinex_dev OWNER sinity; END IF; END \$\$;" export PGHOST="$PWD" export DATABASE_URL="postgresql:///sinex_dev?host=$PWD" diff --git a/.github/workflows/sqlx-check.yml b/.github/workflows/sqlx-check.yml index 626c1c921b..1277511bff 100644 --- a/.github/workflows/sqlx-check.yml +++ b/.github/workflows/sqlx-check.yml @@ -48,8 +48,8 @@ jobs: pg_ctl start -l postgres.log -o "-k $PWD" trap 'pg_ctl stop' EXIT - psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "DO \$\$ BEGIN IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'sinity') THEN CREATE ROLE sinity LOGIN CREATEDB; END IF; END \$\$;" - psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "DO \$\$ BEGIN IF NOT EXISTS (SELECT FROM pg_database WHERE datname = 'sinex_dev') THEN CREATE DATABASE sinex_dev OWNER sinity; END IF; END \$\$;" + PGUSER=postgres psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "DO \$\$ BEGIN IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'sinity') THEN CREATE ROLE sinity LOGIN CREATEDB; END IF; END \$\$;" + PGUSER=postgres psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "DO \$\$ BEGIN IF NOT EXISTS (SELECT FROM pg_database WHERE datname = 'sinex_dev') THEN CREATE DATABASE sinex_dev OWNER sinity; END IF; END \$\$;" export PGHOST="$PWD" export DATABASE_URL="postgresql:///sinex_dev?host=$PWD" From ea2b0e60139c2b2cec0241fd558144fe1eecd512 Mon Sep 17 00:00:00 2001 From: Sinity Date: Thu, 20 Nov 2025 10:47:37 +0100 Subject: [PATCH 20/32] ci(postgres): detect the local superuser before bootstrap Capture the current local Unix user and use it as the Postgres superuser when creating the application role and database. This adapts the socket-based workflow bootstrap to how `initdb --auth=trust` provisions local clusters. --- .github/workflows/ci.yml | 5 +++-- .github/workflows/schema-validation.yml | 5 +++-- .github/workflows/sqlx-cache.yml | 5 +++-- .github/workflows/sqlx-check.yml | 5 +++-- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e1c05dae37..351bdc3351 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,8 +46,9 @@ jobs: echo "unix_socket_directories = '$PWD'" >> "$PGDATA/postgresql.conf" pg_ctl start -l postgres.log -o "-k $PWD" trap 'pg_ctl stop' EXIT - PGUSER=postgres psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "DO \$\$ BEGIN IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'sinity') THEN CREATE ROLE sinity LOGIN CREATEDB; END IF; END \$\$;" - PGUSER=postgres psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "DO \$\$ BEGIN IF NOT EXISTS (SELECT FROM pg_database WHERE datname = 'sinex_dev') THEN CREATE DATABASE sinex_dev OWNER sinity; END IF; END \$\$;" + SUPERUSER=$(id -un) + PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "DO \$\$ BEGIN IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'sinity') THEN CREATE ROLE sinity LOGIN CREATEDB; END IF; END \$\$;" + PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "DO \$\$ BEGIN IF NOT EXISTS (SELECT FROM pg_database WHERE datname = 'sinex_dev') THEN CREATE DATABASE sinex_dev OWNER sinity; END IF; END \$\$;" psql -h "$PWD" -U sinity -d sinex_dev -c 'SELECT 1' >/dev/null export PGHOST="$PWD" export DATABASE_URL="postgresql:///sinex_dev?host=$PWD" diff --git a/.github/workflows/schema-validation.yml b/.github/workflows/schema-validation.yml index 42479b3bcb..0bce33069a 100644 --- a/.github/workflows/schema-validation.yml +++ b/.github/workflows/schema-validation.yml @@ -54,8 +54,9 @@ jobs: pg_ctl start -l postgres.log -o "-k $PWD" trap 'pg_ctl stop' EXIT - PGUSER=postgres psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "DO \$\$ BEGIN IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'sinity') THEN CREATE ROLE sinity LOGIN CREATEDB; END IF; END \$\$;" - PGUSER=postgres psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "DO \$\$ BEGIN IF NOT EXISTS (SELECT FROM pg_database WHERE datname = 'sinex_dev') THEN CREATE DATABASE sinex_dev OWNER sinity; END IF; END \$\$;" + SUPERUSER=$(id -un) + PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "DO \$\$ BEGIN IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'sinity') THEN CREATE ROLE sinity LOGIN CREATEDB; END IF; END \$\$;" + PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "DO \$\$ BEGIN IF NOT EXISTS (SELECT FROM pg_database WHERE datname = 'sinex_dev') THEN CREATE DATABASE sinex_dev OWNER sinity; END IF; END \$\$;" export PGHOST="$PWD" export DATABASE_URL="postgresql:///sinex_dev?host=$PWD" diff --git a/.github/workflows/sqlx-cache.yml b/.github/workflows/sqlx-cache.yml index 884b4abdb4..f696744876 100644 --- a/.github/workflows/sqlx-cache.yml +++ b/.github/workflows/sqlx-cache.yml @@ -48,8 +48,9 @@ jobs: pg_ctl start -l postgres.log -o "-k $PWD" trap 'pg_ctl stop' EXIT - PGUSER=postgres psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "DO \$\$ BEGIN IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'sinity') THEN CREATE ROLE sinity LOGIN CREATEDB; END IF; END \$\$;" - PGUSER=postgres psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "DO \$\$ BEGIN IF NOT EXISTS (SELECT FROM pg_database WHERE datname = 'sinex_dev') THEN CREATE DATABASE sinex_dev OWNER sinity; END IF; END \$\$;" + SUPERUSER=$(id -un) + PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "DO \$\$ BEGIN IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'sinity') THEN CREATE ROLE sinity LOGIN CREATEDB; END IF; END \$\$;" + PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "DO \$\$ BEGIN IF NOT EXISTS (SELECT FROM pg_database WHERE datname = 'sinex_dev') THEN CREATE DATABASE sinex_dev OWNER sinity; END IF; END \$\$;" export PGHOST="$PWD" export DATABASE_URL="postgresql:///sinex_dev?host=$PWD" diff --git a/.github/workflows/sqlx-check.yml b/.github/workflows/sqlx-check.yml index 1277511bff..9c524c8b40 100644 --- a/.github/workflows/sqlx-check.yml +++ b/.github/workflows/sqlx-check.yml @@ -48,8 +48,9 @@ jobs: pg_ctl start -l postgres.log -o "-k $PWD" trap 'pg_ctl stop' EXIT - PGUSER=postgres psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "DO \$\$ BEGIN IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'sinity') THEN CREATE ROLE sinity LOGIN CREATEDB; END IF; END \$\$;" - PGUSER=postgres psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "DO \$\$ BEGIN IF NOT EXISTS (SELECT FROM pg_database WHERE datname = 'sinex_dev') THEN CREATE DATABASE sinex_dev OWNER sinity; END IF; END \$\$;" + SUPERUSER=$(id -un) + PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "DO \$\$ BEGIN IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'sinity') THEN CREATE ROLE sinity LOGIN CREATEDB; END IF; END \$\$;" + PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "DO \$\$ BEGIN IF NOT EXISTS (SELECT FROM pg_database WHERE datname = 'sinex_dev') THEN CREATE DATABASE sinex_dev OWNER sinity; END IF; END \$\$;" export PGHOST="$PWD" export DATABASE_URL="postgresql:///sinex_dev?host=$PWD" From 0e9dbbae0383cb838a2f0b3c2e02a71459cf8315 Mon Sep 17 00:00:00 2001 From: Sinity Date: Thu, 20 Nov 2025 11:06:53 +0100 Subject: [PATCH 21/32] ci(postgres): replace DO blocks with explicit role and database checks Swap the bootstrap `DO $$` blocks for direct existence checks followed by `CREATE ROLE` and `CREATE DATABASE` calls when needed. This makes setup failures easier to localize in workflow logs. --- .github/workflows/ci.yml | 8 ++++++-- .github/workflows/schema-validation.yml | 8 ++++++-- .github/workflows/sqlx-cache.yml | 8 ++++++-- .github/workflows/sqlx-check.yml | 8 ++++++-- 4 files changed, 24 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 351bdc3351..d001521442 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -47,8 +47,12 @@ jobs: pg_ctl start -l postgres.log -o "-k $PWD" trap 'pg_ctl stop' EXIT SUPERUSER=$(id -un) - PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "DO \$\$ BEGIN IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'sinity') THEN CREATE ROLE sinity LOGIN CREATEDB; END IF; END \$\$;" - PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "DO \$\$ BEGIN IF NOT EXISTS (SELECT FROM pg_database WHERE datname = 'sinex_dev') THEN CREATE DATABASE sinex_dev OWNER sinity; END IF; END \$\$;" + if ! PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -tAc "SELECT 1 FROM pg_roles WHERE rolname = 'sinity'" | grep -q 1; then + PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "CREATE ROLE sinity LOGIN CREATEDB;" + fi + if ! PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -tAc "SELECT 1 FROM pg_database WHERE datname = 'sinex_dev'" | grep -q 1; then + PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "CREATE DATABASE sinex_dev OWNER sinity;" + fi psql -h "$PWD" -U sinity -d sinex_dev -c 'SELECT 1' >/dev/null export PGHOST="$PWD" export DATABASE_URL="postgresql:///sinex_dev?host=$PWD" diff --git a/.github/workflows/schema-validation.yml b/.github/workflows/schema-validation.yml index 0bce33069a..3212fca79a 100644 --- a/.github/workflows/schema-validation.yml +++ b/.github/workflows/schema-validation.yml @@ -55,8 +55,12 @@ jobs: trap 'pg_ctl stop' EXIT SUPERUSER=$(id -un) - PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "DO \$\$ BEGIN IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'sinity') THEN CREATE ROLE sinity LOGIN CREATEDB; END IF; END \$\$;" - PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "DO \$\$ BEGIN IF NOT EXISTS (SELECT FROM pg_database WHERE datname = 'sinex_dev') THEN CREATE DATABASE sinex_dev OWNER sinity; END IF; END \$\$;" + if ! PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -tAc "SELECT 1 FROM pg_roles WHERE rolname = 'sinity'" | grep -q 1; then + PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "CREATE ROLE sinity LOGIN CREATEDB;" + fi + if ! PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -tAc "SELECT 1 FROM pg_database WHERE datname = 'sinex_dev'" | grep -q 1; then + PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "CREATE DATABASE sinex_dev OWNER sinity;" + fi export PGHOST="$PWD" export DATABASE_URL="postgresql:///sinex_dev?host=$PWD" diff --git a/.github/workflows/sqlx-cache.yml b/.github/workflows/sqlx-cache.yml index f696744876..f91af7d2d6 100644 --- a/.github/workflows/sqlx-cache.yml +++ b/.github/workflows/sqlx-cache.yml @@ -49,8 +49,12 @@ jobs: trap 'pg_ctl stop' EXIT SUPERUSER=$(id -un) - PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "DO \$\$ BEGIN IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'sinity') THEN CREATE ROLE sinity LOGIN CREATEDB; END IF; END \$\$;" - PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "DO \$\$ BEGIN IF NOT EXISTS (SELECT FROM pg_database WHERE datname = 'sinex_dev') THEN CREATE DATABASE sinex_dev OWNER sinity; END IF; END \$\$;" + if ! PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -tAc "SELECT 1 FROM pg_roles WHERE rolname = 'sinity'" | grep -q 1; then + PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "CREATE ROLE sinity LOGIN CREATEDB;" + fi + if ! PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -tAc "SELECT 1 FROM pg_database WHERE datname = 'sinex_dev'" | grep -q 1; then + PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "CREATE DATABASE sinex_dev OWNER sinity;" + fi export PGHOST="$PWD" export DATABASE_URL="postgresql:///sinex_dev?host=$PWD" diff --git a/.github/workflows/sqlx-check.yml b/.github/workflows/sqlx-check.yml index 9c524c8b40..cecd6e5b4f 100644 --- a/.github/workflows/sqlx-check.yml +++ b/.github/workflows/sqlx-check.yml @@ -49,8 +49,12 @@ jobs: trap 'pg_ctl stop' EXIT SUPERUSER=$(id -un) - PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "DO \$\$ BEGIN IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'sinity') THEN CREATE ROLE sinity LOGIN CREATEDB; END IF; END \$\$;" - PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "DO \$\$ BEGIN IF NOT EXISTS (SELECT FROM pg_database WHERE datname = 'sinex_dev') THEN CREATE DATABASE sinex_dev OWNER sinity; END IF; END \$\$;" + if ! PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -tAc "SELECT 1 FROM pg_roles WHERE rolname = 'sinity'" | grep -q 1; then + PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "CREATE ROLE sinity LOGIN CREATEDB;" + fi + if ! PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -tAc "SELECT 1 FROM pg_database WHERE datname = 'sinex_dev'" | grep -q 1; then + PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "CREATE DATABASE sinex_dev OWNER sinity;" + fi export PGHOST="$PWD" export DATABASE_URL="postgresql:///sinex_dev?host=$PWD" From 61cc150d60428c375df6ad5027b05c79e6953d66 Mon Sep 17 00:00:00 2001 From: Sinity Date: Thu, 20 Nov 2025 11:24:12 +0100 Subject: [PATCH 22/32] ci(schema): run migrations directly from sinex-schema Replace the `devenv tasks run db:migrate` indirection with a direct `cargo run -- up` from `crate/lib/sinex-schema` in the relevant workflows. This removes one more layer from the bootstrap path while it is being debugged. --- .github/workflows/ci.yml | 4 +++- .github/workflows/sqlx-cache.yml | 4 +++- .github/workflows/sqlx-check.yml | 4 +++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d001521442..08761b92b0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -56,7 +56,9 @@ jobs: psql -h "$PWD" -U sinity -d sinex_dev -c 'SELECT 1' >/dev/null export PGHOST="$PWD" export DATABASE_URL="postgresql:///sinex_dev?host=$PWD" - devenv tasks run db:migrate + pushd crate/lib/sinex-schema >/dev/null + cargo run -- up + popd bash scripts/check_forbidden_patterns.sh ./scripts/schema-dev.sh generate if ! git diff --quiet -- schemas; then diff --git a/.github/workflows/sqlx-cache.yml b/.github/workflows/sqlx-cache.yml index f91af7d2d6..1b8c1f0308 100644 --- a/.github/workflows/sqlx-cache.yml +++ b/.github/workflows/sqlx-cache.yml @@ -59,7 +59,9 @@ jobs: export PGHOST="$PWD" export DATABASE_URL="postgresql:///sinex_dev?host=$PWD" - devenv tasks run db:migrate + pushd crate/lib/sinex-schema >/dev/null + cargo run -- up + popd if cargo sqlx prepare --workspace --check -- --all-targets --all-features; then exit 0 diff --git a/.github/workflows/sqlx-check.yml b/.github/workflows/sqlx-check.yml index cecd6e5b4f..ebe949f026 100644 --- a/.github/workflows/sqlx-check.yml +++ b/.github/workflows/sqlx-check.yml @@ -59,7 +59,9 @@ jobs: export PGHOST="$PWD" export DATABASE_URL="postgresql:///sinex_dev?host=$PWD" - devenv tasks run db:migrate + pushd crate/lib/sinex-schema >/dev/null + cargo run -- up + popd cargo sqlx prepare --workspace --check -- --all-targets --all-features From 0f32d56e307a736b570ec86c07ae7914c8c7f483 Mon Sep 17 00:00:00 2001 From: Sinity Date: Thu, 20 Nov 2025 11:45:53 +0100 Subject: [PATCH 23/32] ci(postgres): create required extensions in workflow bootstrap Have the workflows explicitly create the required extensions and keep `listen_addresses = ''` for the socket-based clusters. This makes the new extension-enabled Postgres build visible to CI. --- .github/workflows/ci.yml | 4 ++++ .github/workflows/schema-validation.yml | 4 ++++ .github/workflows/sqlx-cache.yml | 4 ++++ .github/workflows/sqlx-check.yml | 4 ++++ 4 files changed, 16 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 08761b92b0..c9510c87fe 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,6 +44,7 @@ jobs: mkdir -p "$PGDATA" initdb --auth=trust --no-locale --encoding=UTF8 echo "unix_socket_directories = '$PWD'" >> "$PGDATA/postgresql.conf" + echo "listen_addresses = ''" >> "$PGDATA/postgresql.conf" pg_ctl start -l postgres.log -o "-k $PWD" trap 'pg_ctl stop' EXIT SUPERUSER=$(id -un) @@ -53,6 +54,9 @@ jobs: if ! PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -tAc "SELECT 1 FROM pg_database WHERE datname = 'sinex_dev'" | grep -q 1; then PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "CREATE DATABASE sinex_dev OWNER sinity;" fi + for ext in pgx_ulid pg_jsonschema timescaledb pgvector; do + PGUSER="$SUPERUSER" psql -h "$PWD" -d sinex_dev -v ON_ERROR_STOP=1 -c "CREATE EXTENSION IF NOT EXISTS ${ext};" + done psql -h "$PWD" -U sinity -d sinex_dev -c 'SELECT 1' >/dev/null export PGHOST="$PWD" export DATABASE_URL="postgresql:///sinex_dev?host=$PWD" diff --git a/.github/workflows/schema-validation.yml b/.github/workflows/schema-validation.yml index 3212fca79a..d636a5aa35 100644 --- a/.github/workflows/schema-validation.yml +++ b/.github/workflows/schema-validation.yml @@ -51,6 +51,7 @@ jobs: mkdir -p "$PGDATA" initdb --auth=trust --no-locale --encoding=UTF8 echo "unix_socket_directories = '$PWD'" >> "$PGDATA/postgresql.conf" + echo "listen_addresses = ''" >> "$PGDATA/postgresql.conf" pg_ctl start -l postgres.log -o "-k $PWD" trap 'pg_ctl stop' EXIT @@ -61,6 +62,9 @@ jobs: if ! PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -tAc "SELECT 1 FROM pg_database WHERE datname = 'sinex_dev'" | grep -q 1; then PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "CREATE DATABASE sinex_dev OWNER sinity;" fi + for ext in pgx_ulid pg_jsonschema timescaledb pgvector; do + PGUSER="$SUPERUSER" psql -h "$PWD" -d sinex_dev -v ON_ERROR_STOP=1 -c "CREATE EXTENSION IF NOT EXISTS ${ext};" + done export PGHOST="$PWD" export DATABASE_URL="postgresql:///sinex_dev?host=$PWD" diff --git a/.github/workflows/sqlx-cache.yml b/.github/workflows/sqlx-cache.yml index 1b8c1f0308..e1f70bfe05 100644 --- a/.github/workflows/sqlx-cache.yml +++ b/.github/workflows/sqlx-cache.yml @@ -45,6 +45,7 @@ jobs: mkdir -p "$PGDATA" initdb --auth=trust --no-locale --encoding=UTF8 echo "unix_socket_directories = '$PWD'" >> "$PGDATA/postgresql.conf" + echo "listen_addresses = ''" >> "$PGDATA/postgresql.conf" pg_ctl start -l postgres.log -o "-k $PWD" trap 'pg_ctl stop' EXIT @@ -55,6 +56,9 @@ jobs: if ! PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -tAc "SELECT 1 FROM pg_database WHERE datname = 'sinex_dev'" | grep -q 1; then PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "CREATE DATABASE sinex_dev OWNER sinity;" fi + for ext in pgx_ulid pg_jsonschema timescaledb pgvector; do + PGUSER="$SUPERUSER" psql -h "$PWD" -d sinex_dev -v ON_ERROR_STOP=1 -c "CREATE EXTENSION IF NOT EXISTS ${ext};" + done export PGHOST="$PWD" export DATABASE_URL="postgresql:///sinex_dev?host=$PWD" diff --git a/.github/workflows/sqlx-check.yml b/.github/workflows/sqlx-check.yml index ebe949f026..b9c92b13b3 100644 --- a/.github/workflows/sqlx-check.yml +++ b/.github/workflows/sqlx-check.yml @@ -45,6 +45,7 @@ jobs: mkdir -p "$PGDATA" initdb --auth=trust --no-locale --encoding=UTF8 echo "unix_socket_directories = '$PWD'" >> "$PGDATA/postgresql.conf" + echo "listen_addresses = ''" >> "$PGDATA/postgresql.conf" pg_ctl start -l postgres.log -o "-k $PWD" trap 'pg_ctl stop' EXIT @@ -55,6 +56,9 @@ jobs: if ! PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -tAc "SELECT 1 FROM pg_database WHERE datname = 'sinex_dev'" | grep -q 1; then PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "CREATE DATABASE sinex_dev OWNER sinity;" fi + for ext in pgx_ulid pg_jsonschema timescaledb pgvector; do + PGUSER="$SUPERUSER" psql -h "$PWD" -d sinex_dev -v ON_ERROR_STOP=1 -c "CREATE EXTENSION IF NOT EXISTS ${ext};" + done export PGHOST="$PWD" export DATABASE_URL="postgresql:///sinex_dev?host=$PWD" From b6c651cc1317de51c25764b0ff4788980061d16e Mon Sep 17 00:00:00 2001 From: Sinity Date: Thu, 20 Nov 2025 12:22:58 +0100 Subject: [PATCH 24/32] feat(devenv): switch to postgresqlWithExtensions Use the custom `postgresqlWithExtensions` package in the dev environment instead of plain `postgresql_16`. This wires the bundled extension work into actual local and CI usage. --- devenv.nix | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/devenv.nix b/devenv.nix index 477890f36d..a3970fd85e 100644 --- a/devenv.nix +++ b/devenv.nix @@ -21,6 +21,15 @@ let }; propagatedBuildInputs = with pkgs.python3Packages; [ tiktoken ]; }; + postgresqlWithExtensions = + pkgs.postgresql_16.withPackages (ps: + lib.filter (pkg: pkg != null) [ + (if ps ? timescaledb then ps.timescaledb else null) + (if ps ? pgvector then ps.pgvector else null) + (if ps ? pg_jsonschema then ps.pg_jsonschema else null) + (if ps ? pgx_ulid then ps.pgx_ulid else null) + ] + ); basePackages = with pkgs; [ fenixPkgs.toolchain fenixPkgs.rust-analyzer @@ -42,7 +51,7 @@ let mold python3 nats-server - postgresql_16 + postgresqlWithExtensions mprocs btop jq From 946c0fe10dbb2bd470c342b1d3d43ca2d0716c63 Mon Sep 17 00:00:00 2001 From: Sinity Date: Thu, 20 Nov 2025 12:43:34 +0100 Subject: [PATCH 25/32] ci(postgres): verify extension availability before use Add an `ensure_extension()` helper with `pgx_ulid` and `ulid` fallback logic plus explicit checks for `pg_jsonschema`, `timescaledb`, and vector support. This makes extension setup failures surface earlier in workflow logs. --- .github/workflows/ci.yml | 22 +++++++++++++++++++--- .github/workflows/schema-validation.yml | 22 +++++++++++++++++++--- .github/workflows/sqlx-cache.yml | 22 +++++++++++++++++++--- .github/workflows/sqlx-check.yml | 22 +++++++++++++++++++--- 4 files changed, 76 insertions(+), 12 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c9510c87fe..b458b635ae 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -54,9 +54,25 @@ jobs: if ! PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -tAc "SELECT 1 FROM pg_database WHERE datname = 'sinex_dev'" | grep -q 1; then PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "CREATE DATABASE sinex_dev OWNER sinity;" fi - for ext in pgx_ulid pg_jsonschema timescaledb pgvector; do - PGUSER="$SUPERUSER" psql -h "$PWD" -d sinex_dev -v ON_ERROR_STOP=1 -c "CREATE EXTENSION IF NOT EXISTS ${ext};" - done + + ensure_extension() { + local db="$1" + shift + for candidate in "$@"; do + if PGUSER="$SUPERUSER" psql -h "$PWD" -d "$db" -tAc "SELECT 1 FROM pg_available_extensions WHERE name = '${candidate}'" | grep -q 1; then + PGUSER="$SUPERUSER" psql -h "$PWD" -d "$db" -v ON_ERROR_STOP=1 -c "CREATE EXTENSION IF NOT EXISTS ${candidate};" + return 0 + fi + done + echo "::error::None of the requested extensions ($*) are available in this PostgreSQL build." + PGUSER="$SUPERUSER" psql -h "$PWD" -d "$db" -tAc "SELECT name FROM pg_available_extensions ORDER BY name;" + exit 1 + } + + ensure_extension sinex_dev pgx_ulid ulid + ensure_extension sinex_dev pg_jsonschema + ensure_extension sinex_dev timescaledb + ensure_extension sinex_dev vector psql -h "$PWD" -U sinity -d sinex_dev -c 'SELECT 1' >/dev/null export PGHOST="$PWD" export DATABASE_URL="postgresql:///sinex_dev?host=$PWD" diff --git a/.github/workflows/schema-validation.yml b/.github/workflows/schema-validation.yml index d636a5aa35..326e43b5c5 100644 --- a/.github/workflows/schema-validation.yml +++ b/.github/workflows/schema-validation.yml @@ -62,9 +62,25 @@ jobs: if ! PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -tAc "SELECT 1 FROM pg_database WHERE datname = 'sinex_dev'" | grep -q 1; then PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "CREATE DATABASE sinex_dev OWNER sinity;" fi - for ext in pgx_ulid pg_jsonschema timescaledb pgvector; do - PGUSER="$SUPERUSER" psql -h "$PWD" -d sinex_dev -v ON_ERROR_STOP=1 -c "CREATE EXTENSION IF NOT EXISTS ${ext};" - done + + ensure_extension() { + local db="$1" + shift + for candidate in "$@"; do + if PGUSER="$SUPERUSER" psql -h "$PWD" -d "$db" -tAc "SELECT 1 FROM pg_available_extensions WHERE name = '${candidate}'" | grep -q 1; then + PGUSER="$SUPERUSER" psql -h "$PWD" -d "$db" -v ON_ERROR_STOP=1 -c "CREATE EXTENSION IF NOT EXISTS ${candidate};" + return 0 + fi + done + echo "::error::None of the requested extensions ($*) are available in this PostgreSQL build." + PGUSER="$SUPERUSER" psql -h "$PWD" -d "$db" -tAc "SELECT name FROM pg_available_extensions ORDER BY name;" + exit 1 + } + + ensure_extension sinex_dev pgx_ulid ulid + ensure_extension sinex_dev pg_jsonschema + ensure_extension sinex_dev timescaledb + ensure_extension sinex_dev vector export PGHOST="$PWD" export DATABASE_URL="postgresql:///sinex_dev?host=$PWD" diff --git a/.github/workflows/sqlx-cache.yml b/.github/workflows/sqlx-cache.yml index e1f70bfe05..991eb6efd1 100644 --- a/.github/workflows/sqlx-cache.yml +++ b/.github/workflows/sqlx-cache.yml @@ -56,9 +56,25 @@ jobs: if ! PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -tAc "SELECT 1 FROM pg_database WHERE datname = 'sinex_dev'" | grep -q 1; then PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "CREATE DATABASE sinex_dev OWNER sinity;" fi - for ext in pgx_ulid pg_jsonschema timescaledb pgvector; do - PGUSER="$SUPERUSER" psql -h "$PWD" -d sinex_dev -v ON_ERROR_STOP=1 -c "CREATE EXTENSION IF NOT EXISTS ${ext};" - done + + ensure_extension() { + local db="$1" + shift + for candidate in "$@"; do + if PGUSER="$SUPERUSER" psql -h "$PWD" -d "$db" -tAc "SELECT 1 FROM pg_available_extensions WHERE name = '${candidate}'" | grep -q 1; then + PGUSER="$SUPERUSER" psql -h "$PWD" -d "$db" -v ON_ERROR_STOP=1 -c "CREATE EXTENSION IF NOT EXISTS ${candidate};" + return 0 + fi + done + echo "::error::None of the requested extensions ($*) are available in this PostgreSQL build." + PGUSER="$SUPERUSER" psql -h "$PWD" -d "$db" -tAc "SELECT name FROM pg_available_extensions ORDER BY name;" + exit 1 + } + + ensure_extension sinex_dev pgx_ulid ulid + ensure_extension sinex_dev pg_jsonschema + ensure_extension sinex_dev timescaledb + ensure_extension sinex_dev vector export PGHOST="$PWD" export DATABASE_URL="postgresql:///sinex_dev?host=$PWD" diff --git a/.github/workflows/sqlx-check.yml b/.github/workflows/sqlx-check.yml index b9c92b13b3..2d8e768644 100644 --- a/.github/workflows/sqlx-check.yml +++ b/.github/workflows/sqlx-check.yml @@ -56,9 +56,25 @@ jobs: if ! PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -tAc "SELECT 1 FROM pg_database WHERE datname = 'sinex_dev'" | grep -q 1; then PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "CREATE DATABASE sinex_dev OWNER sinity;" fi - for ext in pgx_ulid pg_jsonschema timescaledb pgvector; do - PGUSER="$SUPERUSER" psql -h "$PWD" -d sinex_dev -v ON_ERROR_STOP=1 -c "CREATE EXTENSION IF NOT EXISTS ${ext};" - done + + ensure_extension() { + local db="$1" + shift + for candidate in "$@"; do + if PGUSER="$SUPERUSER" psql -h "$PWD" -d "$db" -tAc "SELECT 1 FROM pg_available_extensions WHERE name = '${candidate}'" | grep -q 1; then + PGUSER="$SUPERUSER" psql -h "$PWD" -d "$db" -v ON_ERROR_STOP=1 -c "CREATE EXTENSION IF NOT EXISTS ${candidate};" + return 0 + fi + done + echo "::error::None of the requested extensions ($*) are available in this PostgreSQL build." + PGUSER="$SUPERUSER" psql -h "$PWD" -d "$db" -tAc "SELECT name FROM pg_available_extensions ORDER BY name;" + exit 1 + } + + ensure_extension sinex_dev pgx_ulid ulid + ensure_extension sinex_dev pg_jsonschema + ensure_extension sinex_dev timescaledb + ensure_extension sinex_dev vector export PGHOST="$PWD" export DATABASE_URL="postgresql:///sinex_dev?host=$PWD" From 12247b8e764b37177d584d56ffcea15c99da85b5 Mon Sep 17 00:00:00 2001 From: Sinity Date: Thu, 20 Nov 2025 13:01:30 +0100 Subject: [PATCH 26/32] feat(devenv): bundle pg_jsonschema into the dev Postgres build Package `pg_jsonschema` from the Debian artifact into a custom Postgres derivation used by the dev shell. This gives local and CI bootstrap flows the extension set that the schema and SQLx jobs require. --- devenv.nix | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/devenv.nix b/devenv.nix index a3970fd85e..c873386c07 100644 --- a/devenv.nix +++ b/devenv.nix @@ -21,12 +21,38 @@ let }; propagatedBuildInputs = with pkgs.python3Packages; [ tiktoken ]; }; + pgJsonschemaPkg = pkgs.stdenv.mkDerivation rec { + pname = "pg_jsonschema"; + version = "0.3.3"; + + src = pkgs.fetchurl { + url = "https://github.com/supabase/pg_jsonschema/releases/download/v${version}/pg_jsonschema-v${version}-pg16-amd64-linux-gnu.deb"; + hash = "sha256-6VSbAZrrItYgnpKMhVqffC4fGp9zzPYaMB6/Bf+Ha/g="; + }; + + nativeBuildInputs = [ pkgs.dpkg ]; + + dontBuild = true; + dontStrip = true; + dontFixup = true; + + unpackPhase = '' + dpkg-deb -x $src . + ''; + + installPhase = '' + mkdir -p $out/lib $out/share/postgresql/extension + find . -name "*.so" -type f -exec cp {} $out/lib/ \; + find . -name "*.sql" -type f -exec cp {} $out/share/postgresql/extension/ \; + find . -name "*.control" -type f -exec cp {} $out/share/postgresql/extension/ \; + ''; + }; postgresqlWithExtensions = pkgs.postgresql_16.withPackages (ps: lib.filter (pkg: pkg != null) [ (if ps ? timescaledb then ps.timescaledb else null) (if ps ? pgvector then ps.pgvector else null) - (if ps ? pg_jsonschema then ps.pg_jsonschema else null) + pgJsonschemaPkg (if ps ? pgx_ulid then ps.pgx_ulid else null) ] ); From ba6357582c3243fa25906f67363a25a2f44a425d Mon Sep 17 00:00:00 2001 From: Sinity Date: Thu, 20 Nov 2025 13:21:12 +0100 Subject: [PATCH 27/32] ci(postgres): preload timescaledb in workflow clusters Add `shared_preload_libraries = 'timescaledb'` to the CI Postgres configs so migrations and tests can create the extension correctly. This keeps the local workflow cluster consistent with the pinned Timescale environment. --- .github/workflows/ci.yml | 1 + .github/workflows/schema-validation.yml | 1 + .github/workflows/sqlx-cache.yml | 1 + .github/workflows/sqlx-check.yml | 1 + 4 files changed, 4 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b458b635ae..83e32391ef 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -45,6 +45,7 @@ jobs: initdb --auth=trust --no-locale --encoding=UTF8 echo "unix_socket_directories = '$PWD'" >> "$PGDATA/postgresql.conf" echo "listen_addresses = ''" >> "$PGDATA/postgresql.conf" + echo "shared_preload_libraries = 'timescaledb'" >> "$PGDATA/postgresql.conf" pg_ctl start -l postgres.log -o "-k $PWD" trap 'pg_ctl stop' EXIT SUPERUSER=$(id -un) diff --git a/.github/workflows/schema-validation.yml b/.github/workflows/schema-validation.yml index 326e43b5c5..727aaa71cc 100644 --- a/.github/workflows/schema-validation.yml +++ b/.github/workflows/schema-validation.yml @@ -52,6 +52,7 @@ jobs: initdb --auth=trust --no-locale --encoding=UTF8 echo "unix_socket_directories = '$PWD'" >> "$PGDATA/postgresql.conf" echo "listen_addresses = ''" >> "$PGDATA/postgresql.conf" + echo "shared_preload_libraries = 'timescaledb'" >> "$PGDATA/postgresql.conf" pg_ctl start -l postgres.log -o "-k $PWD" trap 'pg_ctl stop' EXIT diff --git a/.github/workflows/sqlx-cache.yml b/.github/workflows/sqlx-cache.yml index 991eb6efd1..cb676b41c3 100644 --- a/.github/workflows/sqlx-cache.yml +++ b/.github/workflows/sqlx-cache.yml @@ -46,6 +46,7 @@ jobs: initdb --auth=trust --no-locale --encoding=UTF8 echo "unix_socket_directories = '$PWD'" >> "$PGDATA/postgresql.conf" echo "listen_addresses = ''" >> "$PGDATA/postgresql.conf" + echo "shared_preload_libraries = 'timescaledb'" >> "$PGDATA/postgresql.conf" pg_ctl start -l postgres.log -o "-k $PWD" trap 'pg_ctl stop' EXIT diff --git a/.github/workflows/sqlx-check.yml b/.github/workflows/sqlx-check.yml index 2d8e768644..19c80844ed 100644 --- a/.github/workflows/sqlx-check.yml +++ b/.github/workflows/sqlx-check.yml @@ -46,6 +46,7 @@ jobs: initdb --auth=trust --no-locale --encoding=UTF8 echo "unix_socket_directories = '$PWD'" >> "$PGDATA/postgresql.conf" echo "listen_addresses = ''" >> "$PGDATA/postgresql.conf" + echo "shared_preload_libraries = 'timescaledb'" >> "$PGDATA/postgresql.conf" pg_ctl start -l postgres.log -o "-k $PWD" trap 'pg_ctl stop' EXIT From 04fd9f4d555258c0f750cfb6def0158ecb4cb9e3 Mon Sep 17 00:00:00 2001 From: Sinity Date: Thu, 20 Nov 2025 13:40:05 +0100 Subject: [PATCH 28/32] fix(ci-postgres): run migrations with the superuser URL Point the migration step at the superuser connection rather than the anonymous socket URL so extension and schema setup succeed reliably. This is another bootstrap correction in the same chain. --- .github/workflows/ci.yml | 2 +- .github/workflows/schema-validation.yml | 2 +- .github/workflows/sqlx-cache.yml | 2 +- .github/workflows/sqlx-check.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 83e32391ef..7834bf25ad 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -76,7 +76,7 @@ jobs: ensure_extension sinex_dev vector psql -h "$PWD" -U sinity -d sinex_dev -c 'SELECT 1' >/dev/null export PGHOST="$PWD" - export DATABASE_URL="postgresql:///sinex_dev?host=$PWD" + export DATABASE_URL="postgresql://${SUPERUSER}@/sinex_dev?host=$PWD" pushd crate/lib/sinex-schema >/dev/null cargo run -- up popd diff --git a/.github/workflows/schema-validation.yml b/.github/workflows/schema-validation.yml index 727aaa71cc..8f879319c2 100644 --- a/.github/workflows/schema-validation.yml +++ b/.github/workflows/schema-validation.yml @@ -84,7 +84,7 @@ jobs: ensure_extension sinex_dev vector export PGHOST="$PWD" - export DATABASE_URL="postgresql:///sinex_dev?host=$PWD" + export DATABASE_URL="postgresql://${SUPERUSER}@/sinex_dev?host=$PWD" pushd crate/lib/sinex-schema >/dev/null cargo run -- up diff --git a/.github/workflows/sqlx-cache.yml b/.github/workflows/sqlx-cache.yml index cb676b41c3..f0b43bbe58 100644 --- a/.github/workflows/sqlx-cache.yml +++ b/.github/workflows/sqlx-cache.yml @@ -78,7 +78,7 @@ jobs: ensure_extension sinex_dev vector export PGHOST="$PWD" - export DATABASE_URL="postgresql:///sinex_dev?host=$PWD" + export DATABASE_URL="postgresql://${SUPERUSER}@/sinex_dev?host=$PWD" pushd crate/lib/sinex-schema >/dev/null cargo run -- up diff --git a/.github/workflows/sqlx-check.yml b/.github/workflows/sqlx-check.yml index 19c80844ed..644370d15a 100644 --- a/.github/workflows/sqlx-check.yml +++ b/.github/workflows/sqlx-check.yml @@ -78,7 +78,7 @@ jobs: ensure_extension sinex_dev vector export PGHOST="$PWD" - export DATABASE_URL="postgresql:///sinex_dev?host=$PWD" + export DATABASE_URL="postgresql://${SUPERUSER}@/sinex_dev?host=$PWD" pushd crate/lib/sinex-schema >/dev/null cargo run -- up From 44d98808f02cb89f47174df6652cbfdc12020553 Mon Sep 17 00:00:00 2001 From: Sinity Date: Thu, 20 Nov 2025 13:58:35 +0100 Subject: [PATCH 29/32] fix(ci-postgres): compute encoded socket URI explicitly Introduce a small Python-based helper to build the encoded socket URI before passing it into database URLs. This is the first follow-up after the socket bootstrap switch. --- .github/workflows/ci.yml | 3 ++- .github/workflows/schema-validation.yml | 3 ++- .github/workflows/sqlx-cache.yml | 3 ++- .github/workflows/sqlx-check.yml | 3 ++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7834bf25ad..c0b695fec9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -75,8 +75,9 @@ jobs: ensure_extension sinex_dev timescaledb ensure_extension sinex_dev vector psql -h "$PWD" -U sinity -d sinex_dev -c 'SELECT 1' >/dev/null + SOCKET_URI=$(python3 -c "import urllib.parse, sys; print(urllib.parse.quote(sys.argv[1]))" "$PWD") export PGHOST="$PWD" - export DATABASE_URL="postgresql://${SUPERUSER}@/sinex_dev?host=$PWD" + export DATABASE_URL="postgresql://${SUPERUSER}@${SOCKET_URI}/sinex_dev" pushd crate/lib/sinex-schema >/dev/null cargo run -- up popd diff --git a/.github/workflows/schema-validation.yml b/.github/workflows/schema-validation.yml index 8f879319c2..c29ee6f79e 100644 --- a/.github/workflows/schema-validation.yml +++ b/.github/workflows/schema-validation.yml @@ -83,8 +83,9 @@ jobs: ensure_extension sinex_dev timescaledb ensure_extension sinex_dev vector + SOCKET_URI=$(python3 -c "import urllib.parse, sys; print(urllib.parse.quote(sys.argv[1]))" "$PWD") export PGHOST="$PWD" - export DATABASE_URL="postgresql://${SUPERUSER}@/sinex_dev?host=$PWD" + export DATABASE_URL="postgresql://${SUPERUSER}@${SOCKET_URI}/sinex_dev" pushd crate/lib/sinex-schema >/dev/null cargo run -- up diff --git a/.github/workflows/sqlx-cache.yml b/.github/workflows/sqlx-cache.yml index f0b43bbe58..52336eefe1 100644 --- a/.github/workflows/sqlx-cache.yml +++ b/.github/workflows/sqlx-cache.yml @@ -77,8 +77,9 @@ jobs: ensure_extension sinex_dev timescaledb ensure_extension sinex_dev vector + SOCKET_URI=$(python3 -c "import urllib.parse, sys; print(urllib.parse.quote(sys.argv[1]))" "$PWD") export PGHOST="$PWD" - export DATABASE_URL="postgresql://${SUPERUSER}@/sinex_dev?host=$PWD" + export DATABASE_URL="postgresql://${SUPERUSER}@${SOCKET_URI}/sinex_dev" pushd crate/lib/sinex-schema >/dev/null cargo run -- up diff --git a/.github/workflows/sqlx-check.yml b/.github/workflows/sqlx-check.yml index 644370d15a..8e54d6b768 100644 --- a/.github/workflows/sqlx-check.yml +++ b/.github/workflows/sqlx-check.yml @@ -77,8 +77,9 @@ jobs: ensure_extension sinex_dev timescaledb ensure_extension sinex_dev vector + SOCKET_URI=$(python3 -c "import urllib.parse, sys; print(urllib.parse.quote(sys.argv[1]))" "$PWD") export PGHOST="$PWD" - export DATABASE_URL="postgresql://${SUPERUSER}@/sinex_dev?host=$PWD" + export DATABASE_URL="postgresql://${SUPERUSER}@${SOCKET_URI}/sinex_dev" pushd crate/lib/sinex-schema >/dev/null cargo run -- up From 9fae3b467407a859d69d4a24cbe6ef492b6a5da1 Mon Sep 17 00:00:00 2001 From: Sinity Date: Thu, 20 Nov 2025 14:16:28 +0100 Subject: [PATCH 30/32] fix(ci-postgres): URL-encode the socket path in DATABASE_URL Encode the Unix-socket path when constructing `DATABASE_URL` so clients parse it correctly. This is a targeted follow-up to the socket-based bootstrap phase. --- .github/workflows/ci.yml | 2 +- .github/workflows/schema-validation.yml | 2 +- .github/workflows/sqlx-cache.yml | 2 +- .github/workflows/sqlx-check.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c0b695fec9..7b5325b4fc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -75,7 +75,7 @@ jobs: ensure_extension sinex_dev timescaledb ensure_extension sinex_dev vector psql -h "$PWD" -U sinity -d sinex_dev -c 'SELECT 1' >/dev/null - SOCKET_URI=$(python3 -c "import urllib.parse, sys; print(urllib.parse.quote(sys.argv[1]))" "$PWD") + SOCKET_URI=$(python3 -c "import urllib.parse, sys; print(urllib.parse.quote(sys.argv[1], safe=''))" "$PWD") export PGHOST="$PWD" export DATABASE_URL="postgresql://${SUPERUSER}@${SOCKET_URI}/sinex_dev" pushd crate/lib/sinex-schema >/dev/null diff --git a/.github/workflows/schema-validation.yml b/.github/workflows/schema-validation.yml index c29ee6f79e..fe1104e583 100644 --- a/.github/workflows/schema-validation.yml +++ b/.github/workflows/schema-validation.yml @@ -83,7 +83,7 @@ jobs: ensure_extension sinex_dev timescaledb ensure_extension sinex_dev vector - SOCKET_URI=$(python3 -c "import urllib.parse, sys; print(urllib.parse.quote(sys.argv[1]))" "$PWD") + SOCKET_URI=$(python3 -c "import urllib.parse, sys; print(urllib.parse.quote(sys.argv[1], safe=''))" "$PWD") export PGHOST="$PWD" export DATABASE_URL="postgresql://${SUPERUSER}@${SOCKET_URI}/sinex_dev" diff --git a/.github/workflows/sqlx-cache.yml b/.github/workflows/sqlx-cache.yml index 52336eefe1..51805bf3da 100644 --- a/.github/workflows/sqlx-cache.yml +++ b/.github/workflows/sqlx-cache.yml @@ -77,7 +77,7 @@ jobs: ensure_extension sinex_dev timescaledb ensure_extension sinex_dev vector - SOCKET_URI=$(python3 -c "import urllib.parse, sys; print(urllib.parse.quote(sys.argv[1]))" "$PWD") + SOCKET_URI=$(python3 -c "import urllib.parse, sys; print(urllib.parse.quote(sys.argv[1], safe=''))" "$PWD") export PGHOST="$PWD" export DATABASE_URL="postgresql://${SUPERUSER}@${SOCKET_URI}/sinex_dev" diff --git a/.github/workflows/sqlx-check.yml b/.github/workflows/sqlx-check.yml index 8e54d6b768..51bc3489de 100644 --- a/.github/workflows/sqlx-check.yml +++ b/.github/workflows/sqlx-check.yml @@ -77,7 +77,7 @@ jobs: ensure_extension sinex_dev timescaledb ensure_extension sinex_dev vector - SOCKET_URI=$(python3 -c "import urllib.parse, sys; print(urllib.parse.quote(sys.argv[1]))" "$PWD") + SOCKET_URI=$(python3 -c "import urllib.parse, sys; print(urllib.parse.quote(sys.argv[1], safe=''))" "$PWD") export PGHOST="$PWD" export DATABASE_URL="postgresql://${SUPERUSER}@${SOCKET_URI}/sinex_dev" From ada8671264ac9d94198433db971d71cd2bf5d2e8 Mon Sep 17 00:00:00 2001 From: Sinity Date: Thu, 20 Nov 2025 14:40:13 +0100 Subject: [PATCH 31/32] ci(postgres): move workflow database bootstrap to TCP Run the local CI Postgres instances on `127.0.0.1:55432` and update the connection handling accordingly. This is the pivot away from socket-only bootstrap that the surrounding commits keep refining. --- .github/workflows/ci.yml | 27 ++++++++--------- .github/workflows/schema-validation.yml | 31 ++++++++++---------- .github/workflows/sqlx-cache.yml | 25 ++++++++-------- .github/workflows/sqlx-check.yml | 39 +++++++++++++------------ 4 files changed, 63 insertions(+), 59 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7b5325b4fc..523fff0517 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,31 +42,34 @@ jobs: export PGDATA="$PWD/postgres_data" rm -rf "$PGDATA" mkdir -p "$PGDATA" + export PGHOST=127.0.0.1 + export PGPORT=55432 initdb --auth=trust --no-locale --encoding=UTF8 echo "unix_socket_directories = '$PWD'" >> "$PGDATA/postgresql.conf" - echo "listen_addresses = ''" >> "$PGDATA/postgresql.conf" + echo "listen_addresses = '127.0.0.1'" >> "$PGDATA/postgresql.conf" + echo "port = $PGPORT" >> "$PGDATA/postgresql.conf" echo "shared_preload_libraries = 'timescaledb'" >> "$PGDATA/postgresql.conf" - pg_ctl start -l postgres.log -o "-k $PWD" + pg_ctl start -l postgres.log -o "-k $PWD -p $PGPORT" trap 'pg_ctl stop' EXIT SUPERUSER=$(id -un) - if ! PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -tAc "SELECT 1 FROM pg_roles WHERE rolname = 'sinity'" | grep -q 1; then - PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "CREATE ROLE sinity LOGIN CREATEDB;" + if ! PGUSER="$SUPERUSER" psql -h "$PGHOST" -p "$PGPORT" -d postgres -tAc "SELECT 1 FROM pg_roles WHERE rolname = 'sinity'" | grep -q 1; then + PGUSER="$SUPERUSER" psql -h "$PGHOST" -p "$PGPORT" -d postgres -v ON_ERROR_STOP=1 -c "CREATE ROLE sinity LOGIN CREATEDB;" fi - if ! PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -tAc "SELECT 1 FROM pg_database WHERE datname = 'sinex_dev'" | grep -q 1; then - PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "CREATE DATABASE sinex_dev OWNER sinity;" + if ! PGUSER="$SUPERUSER" psql -h "$PGHOST" -p "$PGPORT" -d postgres -tAc "SELECT 1 FROM pg_database WHERE datname = 'sinex_dev'" | grep -q 1; then + PGUSER="$SUPERUSER" psql -h "$PGHOST" -p "$PGPORT" -d postgres -v ON_ERROR_STOP=1 -c "CREATE DATABASE sinex_dev OWNER sinity;" fi ensure_extension() { local db="$1" shift for candidate in "$@"; do - if PGUSER="$SUPERUSER" psql -h "$PWD" -d "$db" -tAc "SELECT 1 FROM pg_available_extensions WHERE name = '${candidate}'" | grep -q 1; then - PGUSER="$SUPERUSER" psql -h "$PWD" -d "$db" -v ON_ERROR_STOP=1 -c "CREATE EXTENSION IF NOT EXISTS ${candidate};" + if PGUSER="$SUPERUSER" psql -h "$PGHOST" -p "$PGPORT" -d "$db" -tAc "SELECT 1 FROM pg_available_extensions WHERE name = '${candidate}'" | grep -q 1; then + PGUSER="$SUPERUSER" psql -h "$PGHOST" -p "$PGPORT" -d "$db" -v ON_ERROR_STOP=1 -c "CREATE EXTENSION IF NOT EXISTS ${candidate};" return 0 fi done echo "::error::None of the requested extensions ($*) are available in this PostgreSQL build." - PGUSER="$SUPERUSER" psql -h "$PWD" -d "$db" -tAc "SELECT name FROM pg_available_extensions ORDER BY name;" + PGUSER="$SUPERUSER" psql -h "$PGHOST" -p "$PGPORT" -d "$db" -tAc "SELECT name FROM pg_available_extensions ORDER BY name;" exit 1 } @@ -74,10 +77,8 @@ jobs: ensure_extension sinex_dev pg_jsonschema ensure_extension sinex_dev timescaledb ensure_extension sinex_dev vector - psql -h "$PWD" -U sinity -d sinex_dev -c 'SELECT 1' >/dev/null - SOCKET_URI=$(python3 -c "import urllib.parse, sys; print(urllib.parse.quote(sys.argv[1], safe=''))" "$PWD") - export PGHOST="$PWD" - export DATABASE_URL="postgresql://${SUPERUSER}@${SOCKET_URI}/sinex_dev" + psql -h "$PGHOST" -p "$PGPORT" -U sinity -d sinex_dev -c 'SELECT 1' >/dev/null + export DATABASE_URL="postgresql://${SUPERUSER}@${PGHOST}:${PGPORT}/sinex_dev" pushd crate/lib/sinex-schema >/dev/null cargo run -- up popd diff --git a/.github/workflows/schema-validation.yml b/.github/workflows/schema-validation.yml index fe1104e583..82b1e7da71 100644 --- a/.github/workflows/schema-validation.yml +++ b/.github/workflows/schema-validation.yml @@ -49,32 +49,35 @@ jobs: export PGDATA="$PWD/postgres_data" rm -rf "$PGDATA" mkdir -p "$PGDATA" + export PGHOST=127.0.0.1 + export PGPORT=55432 initdb --auth=trust --no-locale --encoding=UTF8 echo "unix_socket_directories = '$PWD'" >> "$PGDATA/postgresql.conf" - echo "listen_addresses = ''" >> "$PGDATA/postgresql.conf" + echo "listen_addresses = '127.0.0.1'" >> "$PGDATA/postgresql.conf" + echo "port = $PGPORT" >> "$PGDATA/postgresql.conf" echo "shared_preload_libraries = 'timescaledb'" >> "$PGDATA/postgresql.conf" - pg_ctl start -l postgres.log -o "-k $PWD" + pg_ctl start -l postgres.log -o "-k $PWD -p $PGPORT" trap 'pg_ctl stop' EXIT SUPERUSER=$(id -un) - if ! PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -tAc "SELECT 1 FROM pg_roles WHERE rolname = 'sinity'" | grep -q 1; then - PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "CREATE ROLE sinity LOGIN CREATEDB;" + if ! PGUSER="$SUPERUSER" psql -h "$PGHOST" -p "$PGPORT" -d postgres -tAc "SELECT 1 FROM pg_roles WHERE rolname = 'sinity'" | grep -q 1; then + PGUSER="$SUPERUSER" psql -h "$PGHOST" -p "$PGPORT" -d postgres -v ON_ERROR_STOP=1 -c "CREATE ROLE sinity LOGIN CREATEDB;" fi - if ! PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -tAc "SELECT 1 FROM pg_database WHERE datname = 'sinex_dev'" | grep -q 1; then - PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "CREATE DATABASE sinex_dev OWNER sinity;" + if ! PGUSER="$SUPERUSER" psql -h "$PGHOST" -p "$PGPORT" -d postgres -tAc "SELECT 1 FROM pg_database WHERE datname = 'sinex_dev'" | grep -q 1; then + PGUSER="$SUPERUSER" psql -h "$PGHOST" -p "$PGPORT" -d postgres -v ON_ERROR_STOP=1 -c "CREATE DATABASE sinex_dev OWNER sinity;" fi ensure_extension() { local db="$1" shift for candidate in "$@"; do - if PGUSER="$SUPERUSER" psql -h "$PWD" -d "$db" -tAc "SELECT 1 FROM pg_available_extensions WHERE name = '${candidate}'" | grep -q 1; then - PGUSER="$SUPERUSER" psql -h "$PWD" -d "$db" -v ON_ERROR_STOP=1 -c "CREATE EXTENSION IF NOT EXISTS ${candidate};" + if PGUSER="$SUPERUSER" psql -h "$PGHOST" -p "$PGPORT" -d "$db" -tAc "SELECT 1 FROM pg_available_extensions WHERE name = '${candidate}'" | grep -q 1; then + PGUSER="$SUPERUSER" psql -h "$PGHOST" -p "$PGPORT" -d "$db" -v ON_ERROR_STOP=1 -c "CREATE EXTENSION IF NOT EXISTS ${candidate};" return 0 fi done echo "::error::None of the requested extensions ($*) are available in this PostgreSQL build." - PGUSER="$SUPERUSER" psql -h "$PWD" -d "$db" -tAc "SELECT name FROM pg_available_extensions ORDER BY name;" + PGUSER="$SUPERUSER" psql -h "$PGHOST" -p "$PGPORT" -d "$db" -tAc "SELECT name FROM pg_available_extensions ORDER BY name;" exit 1 } @@ -83,17 +86,15 @@ jobs: ensure_extension sinex_dev timescaledb ensure_extension sinex_dev vector - SOCKET_URI=$(python3 -c "import urllib.parse, sys; print(urllib.parse.quote(sys.argv[1], safe=''))" "$PWD") - export PGHOST="$PWD" - export DATABASE_URL="postgresql://${SUPERUSER}@${SOCKET_URI}/sinex_dev" + export DATABASE_URL="postgresql://${SUPERUSER}@${PGHOST}:${PGPORT}/sinex_dev" pushd crate/lib/sinex-schema >/dev/null cargo run -- up popd - psql -h "$PGHOST" -U sinity -d sinex_dev -v ON_ERROR_STOP=1 -c "INSERT INTO sinex_schemas.event_payload_schemas (source, event_type, schema_version, schema_content, content_hash) VALUES ('test.source', 'test.event', '1.0.0', '{}'::jsonb, md5(random()::text)) ON CONFLICT (source, event_type, schema_version) DO NOTHING;" - psql -h "$PGHOST" -U sinity -d sinex_dev -v ON_ERROR_STOP=1 -c "UPDATE sinex_schemas.event_payload_schemas SET is_active = true WHERE source = 'test.source' AND event_type = 'test.event';" - psql -h "$PGHOST" -U sinity -d sinex_dev -v ON_ERROR_STOP=1 -c "SELECT COUNT(*) FROM sinex_schemas.event_payload_schemas WHERE source = 'test.source';" + psql -h "$PGHOST" -p "$PGPORT" -U sinity -d sinex_dev -v ON_ERROR_STOP=1 -c "INSERT INTO sinex_schemas.event_payload_schemas (source, event_type, schema_version, schema_content, content_hash) VALUES ('test.source', 'test.event', '1.0.0', '{}'::jsonb, md5(random()::text)) ON CONFLICT (source, event_type, schema_version) DO NOTHING;" + psql -h "$PGHOST" -p "$PGPORT" -U sinity -d sinex_dev -v ON_ERROR_STOP=1 -c "UPDATE sinex_schemas.event_payload_schemas SET is_active = true WHERE source = 'test.source' AND event_type = 'test.event';" + psql -h "$PGHOST" -p "$PGPORT" -U sinity -d sinex_dev -v ON_ERROR_STOP=1 -c "SELECT COUNT(*) FROM sinex_schemas.event_payload_schemas WHERE source = 'test.source';" TMP_DIR=$(mktemp -d) cargo run -p sinex-core --bin sinex-schema --features schema-manager -- generate --output "$TMP_DIR" --sync diff --git a/.github/workflows/sqlx-cache.yml b/.github/workflows/sqlx-cache.yml index 51805bf3da..37b60c81d5 100644 --- a/.github/workflows/sqlx-cache.yml +++ b/.github/workflows/sqlx-cache.yml @@ -43,32 +43,35 @@ jobs: export PGDATA="$PWD/postgres_data" rm -rf "$PGDATA" mkdir -p "$PGDATA" + export PGHOST=127.0.0.1 + export PGPORT=55432 initdb --auth=trust --no-locale --encoding=UTF8 echo "unix_socket_directories = '$PWD'" >> "$PGDATA/postgresql.conf" - echo "listen_addresses = ''" >> "$PGDATA/postgresql.conf" + echo "listen_addresses = '127.0.0.1'" >> "$PGDATA/postgresql.conf" + echo "port = $PGPORT" >> "$PGDATA/postgresql.conf" echo "shared_preload_libraries = 'timescaledb'" >> "$PGDATA/postgresql.conf" - pg_ctl start -l postgres.log -o "-k $PWD" + pg_ctl start -l postgres.log -o "-k $PWD -p $PGPORT" trap 'pg_ctl stop' EXIT SUPERUSER=$(id -un) - if ! PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -tAc "SELECT 1 FROM pg_roles WHERE rolname = 'sinity'" | grep -q 1; then - PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "CREATE ROLE sinity LOGIN CREATEDB;" + if ! PGUSER="$SUPERUSER" psql -h "$PGHOST" -p "$PGPORT" -d postgres -tAc "SELECT 1 FROM pg_roles WHERE rolname = 'sinity'" | grep -q 1; then + PGUSER="$SUPERUSER" psql -h "$PGHOST" -p "$PGPORT" -d postgres -v ON_ERROR_STOP=1 -c "CREATE ROLE sinity LOGIN CREATEDB;" fi - if ! PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -tAc "SELECT 1 FROM pg_database WHERE datname = 'sinex_dev'" | grep -q 1; then - PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "CREATE DATABASE sinex_dev OWNER sinity;" + if ! PGUSER="$SUPERUSER" psql -h "$PGHOST" -p "$PGPORT" -d postgres -tAc "SELECT 1 FROM pg_database WHERE datname = 'sinex_dev'" | grep -q 1; then + PGUSER="$SUPERUSER" psql -h "$PGHOST" -p "$PGPORT" -d postgres -v ON_ERROR_STOP=1 -c "CREATE DATABASE sinex_dev OWNER sinity;" fi ensure_extension() { local db="$1" shift for candidate in "$@"; do - if PGUSER="$SUPERUSER" psql -h "$PWD" -d "$db" -tAc "SELECT 1 FROM pg_available_extensions WHERE name = '${candidate}'" | grep -q 1; then - PGUSER="$SUPERUSER" psql -h "$PWD" -d "$db" -v ON_ERROR_STOP=1 -c "CREATE EXTENSION IF NOT EXISTS ${candidate};" + if PGUSER="$SUPERUSER" psql -h "$PGHOST" -p "$PGPORT" -d "$db" -tAc "SELECT 1 FROM pg_available_extensions WHERE name = '${candidate}'" | grep -q 1; then + PGUSER="$SUPERUSER" psql -h "$PGHOST" -p "$PGPORT" -d "$db" -v ON_ERROR_STOP=1 -c "CREATE EXTENSION IF NOT EXISTS ${candidate};" return 0 fi done echo "::error::None of the requested extensions ($*) are available in this PostgreSQL build." - PGUSER="$SUPERUSER" psql -h "$PWD" -d "$db" -tAc "SELECT name FROM pg_available_extensions ORDER BY name;" + PGUSER="$SUPERUSER" psql -h "$PGHOST" -p "$PGPORT" -d "$db" -tAc "SELECT name FROM pg_available_extensions ORDER BY name;" exit 1 } @@ -77,9 +80,7 @@ jobs: ensure_extension sinex_dev timescaledb ensure_extension sinex_dev vector - SOCKET_URI=$(python3 -c "import urllib.parse, sys; print(urllib.parse.quote(sys.argv[1], safe=''))" "$PWD") - export PGHOST="$PWD" - export DATABASE_URL="postgresql://${SUPERUSER}@${SOCKET_URI}/sinex_dev" + export DATABASE_URL="postgresql://${SUPERUSER}@${PGHOST}:${PGPORT}/sinex_dev" pushd crate/lib/sinex-schema >/dev/null cargo run -- up diff --git a/.github/workflows/sqlx-check.yml b/.github/workflows/sqlx-check.yml index 51bc3489de..e580078318 100644 --- a/.github/workflows/sqlx-check.yml +++ b/.github/workflows/sqlx-check.yml @@ -43,43 +43,44 @@ jobs: export PGDATA="$PWD/postgres_data" rm -rf "$PGDATA" mkdir -p "$PGDATA" + export PGHOST=127.0.0.1 + export PGPORT=55432 initdb --auth=trust --no-locale --encoding=UTF8 echo "unix_socket_directories = '$PWD'" >> "$PGDATA/postgresql.conf" - echo "listen_addresses = ''" >> "$PGDATA/postgresql.conf" + echo "listen_addresses = '127.0.0.1'" >> "$PGDATA/postgresql.conf" + echo "port = $PGPORT" >> "$PGDATA/postgresql.conf" echo "shared_preload_libraries = 'timescaledb'" >> "$PGDATA/postgresql.conf" - pg_ctl start -l postgres.log -o "-k $PWD" + pg_ctl start -l postgres.log -o "-k $PWD -p $PGPORT" trap 'pg_ctl stop' EXIT SUPERUSER=$(id -un) - if ! PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -tAc "SELECT 1 FROM pg_roles WHERE rolname = 'sinity'" | grep -q 1; then - PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "CREATE ROLE sinity LOGIN CREATEDB;" + if ! PGUSER="$SUPERUSER" psql -h "$PGHOST" -p "$PGPORT" -d postgres -tAc "SELECT 1 FROM pg_roles WHERE rolname = 'sinity'" | grep -q 1; then + PGUSER="$SUPERUSER" psql -h "$PGHOST" -p "$PGPORT" -d postgres -v ON_ERROR_STOP=1 -c "CREATE ROLE sinity LOGIN CREATEDB;" fi - if ! PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -tAc "SELECT 1 FROM pg_database WHERE datname = 'sinex_dev'" | grep -q 1; then - PGUSER="$SUPERUSER" psql -h "$PWD" -d postgres -v ON_ERROR_STOP=1 -c "CREATE DATABASE sinex_dev OWNER sinity;" + if ! PGUSER="$SUPERUSER" psql -h "$PGHOST" -p "$PGPORT" -d postgres -tAc "SELECT 1 FROM pg_database WHERE datname = 'sinex_dev'" | grep -q 1; then + PGUSER="$SUPERUSER" psql -h "$PGHOST" -p "$PGPORT" -d postgres -v ON_ERROR_STOP=1 -c "CREATE DATABASE sinex_dev OWNER sinity;" fi ensure_extension() { local db="$1" shift for candidate in "$@"; do - if PGUSER="$SUPERUSER" psql -h "$PWD" -d "$db" -tAc "SELECT 1 FROM pg_available_extensions WHERE name = '${candidate}'" | grep -q 1; then - PGUSER="$SUPERUSER" psql -h "$PWD" -d "$db" -v ON_ERROR_STOP=1 -c "CREATE EXTENSION IF NOT EXISTS ${candidate};" - return 0 - fi - done - echo "::error::None of the requested extensions ($*) are available in this PostgreSQL build." - PGUSER="$SUPERUSER" psql -h "$PWD" -d "$db" -tAc "SELECT name FROM pg_available_extensions ORDER BY name;" - exit 1 - } + if PGUSER="$SUPERUSER" psql -h "$PGHOST" -p "$PGPORT" -d "$db" -tAc "SELECT 1 FROM pg_available_extensions WHERE name = '${candidate}'" | grep -q 1; then + PGUSER="$SUPERUSER" psql -h "$PGHOST" -p "$PGPORT" -d "$db" -v ON_ERROR_STOP=1 -c "CREATE EXTENSION IF NOT EXISTS ${candidate};" + return 0 + fi + done + echo "::error::None of the requested extensions ($*) are available in this PostgreSQL build." + PGUSER="$SUPERUSER" psql -h "$PGHOST" -p "$PGPORT" -d "$db" -tAc "SELECT name FROM pg_available_extensions ORDER BY name;" + exit 1 + } ensure_extension sinex_dev pgx_ulid ulid ensure_extension sinex_dev pg_jsonschema ensure_extension sinex_dev timescaledb ensure_extension sinex_dev vector - SOCKET_URI=$(python3 -c "import urllib.parse, sys; print(urllib.parse.quote(sys.argv[1], safe=''))" "$PWD") - export PGHOST="$PWD" - export DATABASE_URL="postgresql://${SUPERUSER}@${SOCKET_URI}/sinex_dev" + export DATABASE_URL="postgresql://${SUPERUSER}@${PGHOST}:${PGPORT}/sinex_dev" pushd crate/lib/sinex-schema >/dev/null cargo run -- up @@ -89,5 +90,5 @@ jobs: SQLX_OFFLINE=1 cargo check --all-features - SQLX_OFFLINE=1 TEST_DATABASE_URL="postgresql:///sinex_dev?host=$PWD" \ + SQLX_OFFLINE=1 TEST_DATABASE_URL="postgresql://${SUPERUSER}@${PGHOST}:${PGPORT}/sinex_dev" \ cargo nextest run --all-features From a785260af62c00cd00cd75f04f9ea5690ae8dee4 Mon Sep 17 00:00:00 2001 From: Sinity Date: Thu, 20 Nov 2025 15:09:22 +0100 Subject: [PATCH 32/32] ci(postgres): wait for startup with pg_ctl -w Switch CI and SQLx and schema workflows to `pg_ctl start -w` so they block until Postgres is actually ready. This removes the race that earlier bootstrap steps had to work around. --- .github/workflows/ci.yml | 2 +- .github/workflows/schema-validation.yml | 2 +- .github/workflows/sqlx-cache.yml | 2 +- .github/workflows/sqlx-check.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 523fff0517..05e0e64551 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,7 +49,7 @@ jobs: echo "listen_addresses = '127.0.0.1'" >> "$PGDATA/postgresql.conf" echo "port = $PGPORT" >> "$PGDATA/postgresql.conf" echo "shared_preload_libraries = 'timescaledb'" >> "$PGDATA/postgresql.conf" - pg_ctl start -l postgres.log -o "-k $PWD -p $PGPORT" + pg_ctl start -w -l postgres.log -o "-k $PWD -p $PGPORT" trap 'pg_ctl stop' EXIT SUPERUSER=$(id -un) if ! PGUSER="$SUPERUSER" psql -h "$PGHOST" -p "$PGPORT" -d postgres -tAc "SELECT 1 FROM pg_roles WHERE rolname = 'sinity'" | grep -q 1; then diff --git a/.github/workflows/schema-validation.yml b/.github/workflows/schema-validation.yml index 82b1e7da71..3beb3848bb 100644 --- a/.github/workflows/schema-validation.yml +++ b/.github/workflows/schema-validation.yml @@ -56,7 +56,7 @@ jobs: echo "listen_addresses = '127.0.0.1'" >> "$PGDATA/postgresql.conf" echo "port = $PGPORT" >> "$PGDATA/postgresql.conf" echo "shared_preload_libraries = 'timescaledb'" >> "$PGDATA/postgresql.conf" - pg_ctl start -l postgres.log -o "-k $PWD -p $PGPORT" + pg_ctl start -w -l postgres.log -o "-k $PWD -p $PGPORT" trap 'pg_ctl stop' EXIT SUPERUSER=$(id -un) diff --git a/.github/workflows/sqlx-cache.yml b/.github/workflows/sqlx-cache.yml index 37b60c81d5..c34e06bb62 100644 --- a/.github/workflows/sqlx-cache.yml +++ b/.github/workflows/sqlx-cache.yml @@ -50,7 +50,7 @@ jobs: echo "listen_addresses = '127.0.0.1'" >> "$PGDATA/postgresql.conf" echo "port = $PGPORT" >> "$PGDATA/postgresql.conf" echo "shared_preload_libraries = 'timescaledb'" >> "$PGDATA/postgresql.conf" - pg_ctl start -l postgres.log -o "-k $PWD -p $PGPORT" + pg_ctl start -w -l postgres.log -o "-k $PWD -p $PGPORT" trap 'pg_ctl stop' EXIT SUPERUSER=$(id -un) diff --git a/.github/workflows/sqlx-check.yml b/.github/workflows/sqlx-check.yml index e580078318..56e22ee418 100644 --- a/.github/workflows/sqlx-check.yml +++ b/.github/workflows/sqlx-check.yml @@ -50,7 +50,7 @@ jobs: echo "listen_addresses = '127.0.0.1'" >> "$PGDATA/postgresql.conf" echo "port = $PGPORT" >> "$PGDATA/postgresql.conf" echo "shared_preload_libraries = 'timescaledb'" >> "$PGDATA/postgresql.conf" - pg_ctl start -l postgres.log -o "-k $PWD -p $PGPORT" + pg_ctl start -w -l postgres.log -o "-k $PWD -p $PGPORT" trap 'pg_ctl stop' EXIT SUPERUSER=$(id -un)