Skip to content

chore(ci,devenv): standardize Postgres bootstrap and extension provisioning - #104

Closed
Sinity wants to merge 32 commits into
masterfrom
feature/chore/ci-devenv-postgres-bootstrap
Closed

Sinity wants to merge 32 commits into
masterfrom
feature/chore/ci-devenv-postgres-bootstrap

Conversation

@Sinity

@Sinity Sinity commented Apr 9, 2026

Copy link
Copy Markdown
Owner

Summary

I moved the repository’s workflow validation onto a single extension-aware Postgres bootstrap story shared by CI and the local devenv shell. Instead of juggling GitHub service containers in one place, shell-initialized local clusters in another, and a dev shell that did not guarantee the same extension set, the workflows now run migrations/schema generation/SQLx checks inside nix develop against a deliberately provisioned sinex_dev database and sinity role. I also tightened extension setup for Timescale, vector, ULID, and optional pg_jsonschema, and updated the few tests that needed to tolerate ulid vs pgx_ulid differences. The branch is essentially a long stabilization loop that ends with CI and the dev shell describing the same database.

Motivation

The repository had a real “works in one environment, fails in another” problem around Postgres.

Migrations and schema/sqlx jobs need more than vanilla PostgreSQL: they rely on Timescale, vector, ULID support, and sometimes pg_jsonschema. Different workflow jobs were provisioning those extensions differently, some used GitHub Actions service images, some tried to emulate the local shell, and the dev shell itself was not guaranteeing the full extension set in one explicit package. That made failures hard to classify. Was the migration broken, or did the job just not have ulid? Did schema generation fail because the DB was wrong, or because the cluster was not ready yet?

The branch went through several approaches because the exact bootstrap point mattered. I tried leaning harder on GitHub service containers, then moved the work inside devenv, then had to fix pure-eval issues, role creation, socket readiness, extension availability, and superuser-vs-application-role behavior. I’m mentioning that because the final shape is the result of those pivots, not a first-pass design.

Changes grouped by concern

1. Run workflow database work inside nix develop

The decisive structural change is that the important database-aware jobs now run inside nix develop --no-pure-eval rather than relying on a looser outer shell plus a partially separate Postgres story.

That gives us a single environment in which to:

  • run devenv test,
  • start a local Postgres cluster,
  • create sinity / sinex_dev,
  • run migrations from crate/lib/sinex-schema,
  • regenerate schemas,
  • run SQLx preparation/checks.

I also made DEVENV_ROOT and DEVENV_DOTFILE explicit where nested bootstrap commands need them. That turns an implicit “hope the current directory and child shell agree” assumption into a real contract.

2. Make the Postgres cluster extension-aware everywhere

The workflows now provision the database with the extension set the repo actually needs. Over the course of the branch I tightened this from broad “CREATE EXTENSION” attempts to availability-aware logic and a local/dev shell that ships the extensions directly.

The key devenv-side change is the postgresqlWithExtensions package:

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)
    ]
  );

That matters because it stops the local shell from lagging behind the workflow expectations. If CI and the dev shell are both talking about an extension-capable PostgreSQL package, the remaining failures are much easier to trust.

3. Fix bootstrap sequencing and role behavior

Several of the mid-branch fixes were about ordering and permissions rather than architecture, but they are the difference between a reproducible bootstrap and a flaky one.

The workflows now:

  • wait for Postgres to be ready,
  • create the role/database explicitly,
  • run migrations before schema regeneration,
  • use pg_ctl -w or explicit probes instead of timing assumptions,
  • detect extension availability before trying to create optional extensions.

I also corrected the “who runs migrations?” story. The branch briefly tried running migrations as the application role and then reassigning ownership, but the cleaner outcome was to keep the migration/bootstrap path explicit and then make sure the application role can connect and operate normally afterward. That is less magical and easier to debug in CI logs.

4. Adjust schema-related tests to the real extension matrix

validator_schema_order_tests.rs now has an ensure_ulid_extension() helper that accepts either ulid or pgx_ulid. This is a small change, but it is exactly the kind of thing that keeps test intent focused on schema ordering rather than on whichever extension name the current Postgres build exposes.

I also carried a few harness/property-test touch-ups that were required by the new execution path so that the workflows continue to use the same sinex_test_utils shape as local development.

Testing

The branch’s strongest validation is the workflows themselves: CI, schema validation, SQLx cache, and SQLx check now all exercise the standardized bootstrap path. The schema regeneration check remains in place, so the branch does not just boot a DB—it proves the migration/generation ordering against the actual supported toolchain.

At the Rust-test level, validator_schema_order_tests.rs now explicitly handles the ulid/pgx_ulid split, which covers one of the more annoying extension-availability edge cases exposed by the branch.

The main gap is that the bootstrap logic is mostly exercised through workflow execution rather than through a standalone unit-test suite. That is acceptable here because the behavior is inherently operational and tightly coupled to the shell/database environment.

Notes

This branch is full of “fix(ci)” commits because the problem only really showed up once the workflows were forced onto a single path. I think that is a feature rather than a weakness: each fix removed one more silent assumption about extensions, readiness, or role ownership.

The final result I cared about was simple: if a developer or workflow says “run migrations, regenerate schemas, and validate SQLx,” they should all be talking to the same kind of PostgreSQL cluster with the same extension story.

Ref #103

- 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.
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.
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.
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.
Only create `pg_jsonschema` when the extension is present in the CI image, matching the `timescaledb-ha` service chosen in the preceding workflow change.
… 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.
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.
…allback

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.
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.
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.
- 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.
Add `--no-pure-eval` to the GitHub Actions `nix develop` invocation so the devenv-based workflow can evaluate the local flake in CI.
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.
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.
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.
- 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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
@Sinity Sinity added type:chore Build, CI, tooling, or dependency changes area:schema Schema, migrations, database area:nix NixOS modules, flake, packaging area:tests Test infrastructure and harnesses labels Apr 9, 2026
Copilot AI review requested due to automatic review settings April 9, 2026 09:56
@Sinity Sinity added the area:tests Test infrastructure and harnesses label Apr 9, 2026
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@coderabbitai

coderabbitai Bot commented Apr 9, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@Sinity has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 7 minutes and 14 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 7 minutes and 14 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 64e0fc6b-42d0-466a-9962-de5089e108fa

📥 Commits

Reviewing files that changed from the base of the PR and between f9cb49a and a785260.

📒 Files selected for processing (16)
  • .github/workflows/ci.yml
  • .github/workflows/schema-validation.yml
  • .github/workflows/sqlx-cache.yml
  • .github/workflows/sqlx-check.yml
  • crate/core/sinex-ingestd/tests/validator_schema_order_tests.rs
  • crate/lib/sinex-core/tests/property/event_model_fuzzing_test.rs
  • crate/lib/sinex-core/tests/property/schema_property_test.rs
  • crate/lib/sinex-core/tests/property/ulid_property_test.rs
  • crate/lib/sinex-schema/src/migrations/m20241028_000001_create_canonical_schema.rs
  • crate/lib/sinex-test-utils/macros/src/lib.rs
  • crate/lib/sinex-test-utils/src/lib.rs
  • crate/lib/sinex-test-utils/src/property_testing.rs
  • crate/lib/sinex-test-utils/src/snapshot_helper.rs
  • crate/lib/sinex-test-utils/src/test_context.rs
  • devenv.nix
  • rustfmt.toml

Note

.coderabbit.yaml has unrecognized properties

CodeRabbit is using all valid settings from your configuration. Unrecognized properties (listed below) have been ignored and may indicate typos or deprecated fields that can be removed.

⚠️ Parsing warnings (1)
Validation error: Unrecognized key(s) in object: 'finishing_touches'
⚙️ Configuration instructions
  • Please see the configuration documentation for more information.
  • You can also validate your configuration using the online YAML validator.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/chore/ci-devenv-postgres-bootstrap

Comment @coderabbitai help to get the list of available commands and usage tips.

Sinity added a commit that referenced this pull request Apr 9, 2026
…ioning (#104)

Standardize CI and local devenv Postgres bootstrap around an extension-capable cluster so migrations, schema generation, and SQLx validation all run against the same database shape instead of a patchwork of service containers and implicit local assumptions.

GitHub Actions workflows:
- Rework `.github/workflows/ci.yml`, `schema-validation.yml`, `sqlx-cache.yml`, and `sqlx-check.yml` to run through `nix develop --no-pure-eval`, bootstrap a local Postgres instance, and provision the `sinity` role plus `sinex_dev` database before running migrations, schema regeneration, or SQLx preparation.
- Fix the ordering bugs in those jobs so database readiness checks, migrations, and schema generation happen in the right sequence, which removes the earlier “generate first, migrate later” failure mode.
- Make extension setup explicit for Timescale, vector/pgvector, ULID, and optional `pg_jsonschema`, and add availability checks/fallback logic where CI images differ in what they ship.

devenv and Nix packaging:
- Restore the full `devenv` configuration behind the fenix fallback, then switch the dev shell to a `postgresqlWithExtensions` package so the local shell and the workflow bootstrap agree on which extensions are supposed to exist.
- Keep the workflow execution path inside the devenv shell and fix the root/env propagation (`DEVENV_ROOT`, `DEVENV_DOTFILE`) so nested Nix/devenv commands stop depending on fragile current-directory assumptions.
- Add smaller reliability fixes such as `pg_ctl -w`, explicit socket/TCP probing, and build dependency installation (`mold`, ripgrep, PostgreSQL client`) so bootstrap failures become deterministic instead of timing-sensitive.

schema/tests and supporting code:
- Update `crate/core/sinex-ingestd/tests/validator_schema_order_tests.rs` with an `ensure_ulid_extension()` helper that can work with either `ulid` or `pgx_ulid`, which keeps the test suite compatible with the extension set available in the running Postgres build.
- Carry the small `sinex-test-utils` and property-test adjustments required by the new CI/devenv execution path so the workflows exercise the same harness shape developers use locally.
- Preserve the schema regeneration check in CI and keep the migrations anchored in `crate/lib/sinex-schema`, which makes the workflow explicit about the only supported migration entrypoint.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Standardizes Postgres bootstrap + extension provisioning so CI workflows and the local devenv environment run migrations/schema generation/SQLx checks against the same extension-capable Postgres setup, and adjusts test utilities/macros to better capture failure context and handle ULID extension variants.

Changes:

  • Reworks CI + schema/SQLx workflows to run DB-dependent steps inside nix develop and provision a consistent Postgres cluster + extensions.
  • Extends devenv Postgres packaging to include required extensions (Timescale/vector/ULID/pg_jsonschema) and updates schema/tests to tolerate ulid vs pgx_ulid.
  • Improves test failure artifact capture (snapshot context + proptest runner configuration).

Reviewed changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
rustfmt.toml Ignores rustfmt on large property test files.
devenv.nix Adds extension-aware Postgres package + pinned fenix fallback + devenv root wiring.
crate/lib/sinex-test-utils/src/test_context.rs Adds failure snapshot type to persist context/logs across moves.
crate/lib/sinex-test-utils/src/snapshot_helper.rs Extends failure persistence API to accept borrowed or snapshotted context.
crate/lib/sinex-test-utils/src/property_testing.rs Centralizes proptest runner config + regression file path selection.
crate/lib/sinex-test-utils/src/lib.rs Exposes proptest runner config helper + re-exports failure snapshot.
crate/lib/sinex-test-utils/macros/src/lib.rs Updates sinex_prop config/seed handling and failure persistence calls.
crate/lib/sinex-schema/src/migrations/m20241028_000001_create_canonical_schema.rs Adds ulid → pgx_ulid fallback when provisioning extensions.
crate/lib/sinex-core/tests/property/ulid_property_test.rs Fixes/adjusts proptest block structure.
crate/lib/sinex-core/tests/property/schema_property_test.rs Makes proptest returns explicit with Report turbofish.
crate/lib/sinex-core/tests/property/event_model_fuzzing_test.rs Makes proptest returns explicit and adjusts macro block structure.
crate/core/sinex-ingestd/tests/validator_schema_order_tests.rs Adds helper to create whichever ULID extension is available.
.github/workflows/sqlx-check.yml Runs SQLx check inside nix develop with local Postgres bootstrap + extensions.
.github/workflows/sqlx-cache.yml Validates SQLx cache inside nix develop with standardized Postgres bootstrap.
.github/workflows/schema-validation.yml Validates schema generation inside nix develop with standardized Postgres bootstrap.
.github/workflows/ci.yml Consolidates CI to a devenv-driven integration-style job with standardized Postgres bootstrap.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

));
};

let stmt = format!(r#"CREATE EXTENSION IF NOT EXISTS "{extension}""#);

Copilot AI Apr 9, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CREATE EXTENSION statement is malformed: format!(r#"CREATE EXTENSION IF NOT EXISTS "{extension}""#) produces an extra trailing quote and no semicolon, so this will error at runtime. Build the statement without the trailing quote (and ideally add ;).

Suggested change
let stmt = format!(r#"CREATE EXTENSION IF NOT EXISTS "{extension}""#);
let stmt = format!(r#"CREATE EXTENSION IF NOT EXISTS "{extension}";"#);

Copilot uses AI. Check for mistakes.
Comment thread devenv.nix
lib.filter (pkg: pkg != null) [
(if ps ? timescaledb then ps.timescaledb else null)
(if ps ? pgvector then ps.pgvector else null)
pgJsonschemaPkg

Copilot AI Apr 9, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pgJsonschemaPkg fetches a prebuilt .deb for pg16-amd64-linux-gnu, but it’s included unconditionally in postgresqlWithExtensions. This will fail to evaluate/build on non-x86_64 Linux systems (and likely macOS). Consider guarding this derivation/package with a system == "x86_64-linux" check (or using ps.pg_jsonschema when available) so devenv remains usable cross-platform.

Suggested change
pgJsonschemaPkg
(if ps ? pg_jsonschema then ps.pg_jsonschema else if system == "x86_64-linux" then pgJsonschemaPkg else null)

Copilot uses AI. Check for mistakes.
Comment on lines +89 to +97
export DATABASE_URL="postgresql://${SUPERUSER}@${PGHOST}:${PGPORT}/sinex_dev"

pushd crate/lib/sinex-schema >/dev/null
cargo run -- up
popd

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';"

Copilot AI Apr 9, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This workflow runs migrations with DATABASE_URL as the initdb superuser (line 89-93) but then verifies schema registry writes by connecting as sinity (line 95-97). Since migrations create schemas/tables owned by the migration role, sinity is likely to hit permission errors unless ownership/grants are explicitly handled. To make this check reliable, run migrations as sinity after extension provisioning (or add explicit ALTER ... OWNER TO sinity / GRANTs in the migration/bootstrap).

Copilot uses AI. Check for mistakes.
Comment thread .github/workflows/ci.yml
Comment on lines 12 to +40
jobs:
check:
name: Nix Flake Check
test:
name: Devenv Integration Test
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Install Nix
uses: cachix/install-nix-action@v24
- uses: actions/checkout@v5

- uses: cachix/install-nix-action@v31
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
skipPush: ${{ github.event_name == 'pull_request' }}

- name: Nix Flake Check
run: nix flake check --accept-flake-config

test:
name: Cargo Test
runs-on: ubuntu-latest

services:
postgres:
image: timescale/timescaledb:2.15.2-pg16 # Pin exact Timescale/Postgres versions for deterministic CI
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
- uses: cachix/cachix-action@v16
with:
name: sinity
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
skipPush: true

steps:
- uses: actions/checkout@v4

- name: Install ripgrep
run: |
sudo apt-get update
sudo apt-get install -y ripgrep

- name: Enforce forbidden pattern guards
run: bash scripts/check_forbidden_patterns.sh
- name: Install devenv.sh
shell: bash
run: nix profile install nixpkgs#devenv

- name: Regenerate schemas and ensure repository is clean
# Treat schemas/ like a generated artifact (similar to cargo fmt).
- name: Run migrations and tests
shell: 'nix develop --no-pure-eval --command bash --noprofile --norc -euo pipefail {0}'
env:
DATABASE_URL: postgres://sinex_test:test_password@localhost/sinex_test
DEVENV_ROOT: ${{ github.workspace }}
DEVENV_DOTFILE: ${{ github.workspace }}/.devenv
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
devenv test

Copilot AI Apr 9, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CI is now a single Devenv Integration Test job. If the previous Nix flake checks and coverage reporting are still required quality gates, they appear to have been dropped rather than moved into devenv test/dev:test. Consider re-adding equivalent steps (or documenting that these checks are intentionally removed/replaced) so CI coverage doesn’t regress silently.

Copilot uses AI. Check for mistakes.
Comment on lines +43 to +83
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 = '127.0.0.1'" >> "$PGDATA/postgresql.conf"
echo "port = $PGPORT" >> "$PGDATA/postgresql.conf"
echo "shared_preload_libraries = 'timescaledb'" >> "$PGDATA/postgresql.conf"
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
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 "$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 "$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

export DATABASE_URL="postgresql://${SUPERUSER}@${PGHOST}:${PGPORT}/sinex_dev"

Copilot AI Apr 9, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Postgres bootstrap shell (initdb/pg_ctl/role+db creation/ensure_extension) is duplicated inline here and in other workflows (sqlx-check.yml, sqlx-cache.yml, schema-validation.yml). This increases the risk of drift and subtle inconsistencies over time; consider extracting it into a shared script (e.g. scripts/ci/postgres-bootstrap.sh) or a composite action and invoking it from each workflow.

Copilot uses AI. Check for mistakes.
@Sinity Sinity added the faux-pull-request PR created as part of a history rewrite process label Apr 10, 2026
@Sinity Sinity closed this Apr 10, 2026
@Sinity
Sinity deleted the feature/chore/ci-devenv-postgres-bootstrap branch April 16, 2026 00:19
Sinity added a commit that referenced this pull request Jul 13, 2026
…ioning (#104)

Standardize CI and local devenv Postgres bootstrap around an extension-capable cluster so migrations, schema generation, and SQLx validation all run against the same database shape instead of a patchwork of service containers and implicit local assumptions.

GitHub Actions workflows:
- Rework `.github/workflows/ci.yml`, `schema-validation.yml`, `sqlx-cache.yml`, and `sqlx-check.yml` to run through `nix develop --no-pure-eval`, bootstrap a local Postgres instance, and provision the `sinity` role plus `sinex_dev` database before running migrations, schema regeneration, or SQLx preparation.
- Fix the ordering bugs in those jobs so database readiness checks, migrations, and schema generation happen in the right sequence, which removes the earlier “generate first, migrate later” failure mode.
- Make extension setup explicit for Timescale, vector/pgvector, ULID, and optional `pg_jsonschema`, and add availability checks/fallback logic where CI images differ in what they ship.

devenv and Nix packaging:
- Restore the full `devenv` configuration behind the fenix fallback, then switch the dev shell to a `postgresqlWithExtensions` package so the local shell and the workflow bootstrap agree on which extensions are supposed to exist.
- Keep the workflow execution path inside the devenv shell and fix the root/env propagation (`DEVENV_ROOT`, `DEVENV_DOTFILE`) so nested Nix/devenv commands stop depending on fragile current-directory assumptions.
- Add smaller reliability fixes such as `pg_ctl -w`, explicit socket/TCP probing, and build dependency installation (`mold`, ripgrep, PostgreSQL client`) so bootstrap failures become deterministic instead of timing-sensitive.

schema/tests and supporting code:
- Update `crate/core/sinex-ingestd/tests/validator_schema_order_tests.rs` with an `ensure_ulid_extension()` helper that can work with either `ulid` or `pgx_ulid`, which keeps the test suite compatible with the extension set available in the running Postgres build.
- Carry the small `sinex-test-utils` and property-test adjustments required by the new CI/devenv execution path so the workflows exercise the same harness shape developers use locally.
- Preserve the schema regeneration check in CI and keep the migrations anchored in `crate/lib/sinex-schema`, which makes the workflow explicit about the only supported migration entrypoint.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:nix NixOS modules, flake, packaging area:schema Schema, migrations, database area:tests Test infrastructure and harnesses faux-pull-request PR created as part of a history rewrite process type:chore Build, CI, tooling, or dependency changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants