pgrls vs. squawk — do I need both?

Short answer: yes. They run at adjacent stages of the same pipeline and never look at the same artifact. squawk reads your migration files before you apply them; pgrls reads the live database after you do.

The kind of bug each one catches

squawk (github.com/sbdchd/squawk) fires on migration hazards like this:

ALTER TABLE huge_table ADD COLUMN status TEXT NOT NULL DEFAULT 'ok';
-- ^ rewrites every row, holds AccessExclusiveLock,
--   blocks reads & writes for the duration.

That’s the canonical migration-safety bug it’s built to prevent.

pgrls fires on security bugs in the result of a migration — the policies that ended up in the catalog after the SQL ran:

CREATE POLICY tenant_read ON public.documents
    FOR SELECT
    USING (auth.uid() IS NULL OR owner_id = auth.uid());

That policy applies cleanly (no lock issues, squawk would not flag it) and admits every row to anonymous clients. pgrls flags it as SEC004.

Capability check

  squawk pgrls
Catches lock-blocking DDL
Catches RLS bypass / row-leak bugs
Reads .sql migration files
Reads live Postgres catalog
Auto-fix ✓ (12 of 46 rules)
Postgres-version-aware ✓ (PG15+)

Wire both into CI

- run: squawk migrations/*.sql        # pre-apply: refuse unsafe DDL
- run: psql … -f migrations/0001.sql  # apply to ephemeral Postgres
- run: pgrls lint --schemas public    # post-apply: audit the result

squawk only sees the migration text; pgrls only sees the resulting state. They cannot collide.

Verdict

Use both. squawk asks “can I deploy this without taking the database down?” pgrls asks “if I do deploy it, are the rows actually safe?” Both are CI questions, both deserve answers, neither tool tries to do the other’s job.