Contributing to Threadline

Copy Markdown View Source

Development environment

Requirements:

  • Elixir 1.15+ (CI uses 1.17.3)
  • OTP 26+ (CI uses OTP 27.0)
  • PostgreSQL 14+ (PostgreSQL 16 recommended; matches CI and docker-compose.yml)

Setup

  1. Clone the repository.

  2. Install dependencies: mix deps.get

  3. Start PostgreSQL — no manual createdb required: the test helper creates threadline_test when missing.

    docker compose up -d
    

    Wait until Postgres is healthy (docker compose ps).

    Port 5432 already in use (e.g. Homebrew PostgreSQL): Compose maps the container to host port 5433 by default (THREADLINE_DB_PORT in docker-compose.yml). Point Mix at it:

    DB_PORT=5433 mix ci.all
    
  4. Run the full local gate (same steps CI runs, modulo Postgres). The project sets preferred_envs: ["ci.all": :test] in mix.exs, so the whole chain (format, credo, compile strict, tests, Threadline trigger coverage, doc contract tests) runs in the test environment and picks up config/test.exs.

    MIX_ENV=test mix ci.all
    

    mix ci.all is equivalent when invoked without MIX_ENV because of preferred_envs.

    With the alternate Compose port: DB_PORT=5433 mix ci.all.

Running tests

mix verify.test          # format of CI: full suite (needs PostgreSQL)
mix test test/path.exs   # single file

Integration tests use a real database and triggers; they are not excluded from mix test.

Environment: DB_HOST defaults to localhost; DB_PORT defaults to 5432 (see config/test.exs). Override if Postgres listens on another port (e.g. DB_PORT=5433 with the default docker-compose.yml mapping).

CI parity and act

GitHub Actions workflow: .github/workflows/ci.yml. Live runs (branch main): https://github.com/szTheory/threadline/actions?query=branch%3Amain — Stable job keys (do not rename; used by docs, act, and branch protection):

Job keyPurpose
verify-formatmix verify.format
verify-credomix verify.credo
verify-compile-no-optionalmix verify.compile_no_optional (compile without optional deps; gates against missing Phoenix/LiveView)
verify-testcompile --warnings-as-errors + mix verify.test (Postgres service)
verify-pgbouncer-topologyPostgres + PgBouncer (POOL_MODE=transaction)priv/ci/topology_bootstrap.exs on direct Postgres, then mix verify.topology + mix verify.threadline on the pooler port
verify-docsMIX_ENV=devmix docs (ExDoc + extras)
verify-hex-packagemix hex.build + assert tarball contains lib/
verify-release-shapebin/verify-release-shape@version / dated CHANGELOG for release versions

Hex publish runs from .github/workflows/release.yml (canonical) using the HEX_API_KEY repository secret — see Hex publish (maintainers) below. Legacy tag-only fallback: .github/workflows/hex-publish.yml.

For running the test job locally with nektos/act, see scripts/ci/README.md.

PgBouncer topology CI parity

docker-compose.yml includes pgbouncer (transaction mode) on host port 6432 by default (THREADLINE_PGBOUNCER_PORT), alongside Postgres on 5433 (THREADLINE_DB_PORT).

  1. docker compose up -d and wait until both services are healthy.

  2. Bootstrap migrations + topology fixture on direct Postgres (DDL does not go through PgBouncer):

    MIX_ENV=test DB_HOST=localhost DB_PORT=5433 THREADLINE_TOPOLOGY_BOOTSTRAP=1 mix run priv/ci/topology_bootstrap.exs
    
  3. Run topology tests + verify.threadline through the pooler:

    MIX_ENV=test DB_HOST=localhost DB_PORT=6432 THREADLINE_PGBOUNCER_TOPOLOGY=1 mix verify.topology
    MIX_ENV=test DB_HOST=localhost DB_PORT=6432 THREADLINE_PGBOUNCER_TOPOLOGY=1 mix verify.threadline
    

mix verify.topology requires THREADLINE_PGBOUNCER_TOPOLOGY=1 so it cannot accidentally pass against direct Postgres only.

Host STG evidence (integrators)

Host staging / pooler parity (requirements STG-01STG-03) is integrator-owned attestation: detailed topology, logs, and runbooks live in your repo or docs under your control. Threadline maintainers do not operate your staging stack.

To contribute a short in-repo index (tables, links, redacted excerpts) that helps other operators, use a fork and open a pull request against this repository. Maintainers merge for modesty of claims, redaction, and link hygiene only — not to vouch for third-party environments.

Fill the canonical scaffolds in guides/adoption-pilot-backlog.md: search for STG-HOST-TOPOLOGY-TEMPLATE (fixed-field topology narrative) and STG-AUDITED-PATH-RUBRIC (HTTP + job paths with OK / Issue / N/A / Not run and evidence pointers). Long-form evidence stays in integrator-controlled artifacts; the PR updates the small, reviewable surface in main.

Submitting a Pull Request

  1. Fork the repository and create a branch from main.
  2. Make your changes and run the full gate: mix ci.all (requires PostgreSQL — see Setup above).
  3. Open a pull request against main. Describe what changed and why.
  4. All CI checks on the PR must pass (including verify-docs, verify-hex-package, and verify-release-shape when present on main).

Branch protection (maintainers)

In GitHub repository settings, require these checks on main (names match the workflow name: fields or job summaries as shown in the PR UI):

  • Check formatting (verify-format)
  • Run Credo (strict) (verify-credo)
  • Run test suite (verify-test)
  • PgBouncer transaction topology (verify-pgbouncer-topology)
  • Build ExDoc (dev) (verify-docs)
  • Hex package tarball (verify-hex-package)
  • Release metadata (version / changelog) (verify-release-shape)

Exact labels depend on GitHub’s UI; map them to the job keys above.

Hex publish (maintainers)

Canonical path: .github/workflows/release.yml — Release Please on main (0.6.1+) or workflow_dispatch bootstrap/recovery (e.g. first v0.6.0 cut).

The release workflow:

  1. Resolves the release ref (Release Please tag or dispatch inputs).
  2. Waits for green ci.yml on the release SHA (gate-ci-green).
  3. Runs mix verify.release, then mix hex.publish --yes (idempotent if version already on Hex).
  4. Polls Hex.pm until the version is indexed.
  5. Opens a distribution sync PR (bin/post-publish-distribution-sync) that flips the adoption-pilot Hex row to OK, trims evaluating-guide lag prose, and writes .planning/phases/122-release-distribution-truth/122-VERIFICATION.md.

Secrets: HEX_API_KEY (required). RELEASE_PLEASE_TOKEN (optional fine-grained PAT — recommended for Release Please PRs and distribution sync PRs).

Bootstrap v0.6.0 (one-shot)

After Wave 1 distribution doc work is on main and CI is green:

  1. Actions → ReleaseRun workflow
  2. Inputs: tag = v0.6.0, release_version = 0.6.0
  3. Merge the automated distribution sync PR when mix verify.doc_contract passes on that PR

The workflow creates tag v0.6.0 on green main HEAD if the tag does not exist yet.

Ongoing releases (0.6.1+)

  1. Merge conventional commits to main — Release Please opens/updates a Release PR (release-please-config.json, manifest .release-please-manifest.json).
  2. Merge the Release PR when CI is green — Release Please tags, then the same publish + distribution sync chain runs.

Recovery / dry-run

workflow_dispatch inputs:

InputPurpose
tagExisting or to-be-created vX.Y.Z
release_versionMust match @version in mix.exs at that ref
dry_runmix hex.publish --dry-run --yes only
skip_distribution_syncPublish without opening the doc sync PR

Legacy fallback: pushing tag v*.*.* still triggers .github/workflows/hex-publish.yml (no CI gate, no doc sync).

Local manual runbook (optional): mix hex.publish --dry-run / mix hex.publish with mix hex.user auth instead of CI.

Post-publish distribution proof for adopters: adoption-pilot Distribution preflight OK row in guides/adoption-pilot-backlog.md plus .planning/phases/122-release-distribution-truth/122-VERIFICATION.md.

Maintainer manual checklist (release)

Use when preparing or debugging a release (no secrets in logs):

  1. Clean tree: git status --porcelain empty (local preflight only).
  2. Run mix verify.release.
  3. Run DB_PORT=5433 mix ci.all (or mix ci.all) with Postgres up.
  4. Ensure main CI is green on the commit to release.
  5. Release workflow: dispatch release.yml or merge Release Please PR — do not rely on manual mix hex.info copy-paste; the workflow polls Hex.pm and opens the distribution sync PR.
  6. Merge the distribution sync PR after doc contracts pass.