Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
21183b5
feat: support split-config layout for database remotes by separating …
supunappri99 May 7, 2026
1728f83
feat: support ephemeral Postgres containers in db commands for local …
supunappri99 May 7, 2026
a6ac308
feat: align local container Postgres version with remote and perform …
supunappri99 May 7, 2026
3bda5c8
feat: add container management for database sessions and implement re…
supunappri99 May 7, 2026
ffc5455
feat: implement auto-container management for local databases and ref…
supunappri99 May 7, 2026
389defe
refactor: move all database remote data from public config to gitigno…
supunappri99 May 7, 2026
f21e8ec
refactor: move all database remote configurations from committed conf…
supunappri99 May 7, 2026
9ccb4fd
refactor: introduce resolveLocalDb to unify and simplify container st…
supunappri99 May 8, 2026
7031717
refactor: migrate sensitive credentials and remote configurations fro…
supunappri99 May 8, 2026
026e2c6
refactor: update gitignore strategy to track migrations and auth stat…
supunappri99 May 8, 2026
dcc817d
feat: implement auto-container mode for local database provisioning i…
supunappri99 May 8, 2026
37882c6
chore: remove local Claude settings file
supunappri99 May 8, 2026
12046bd
refactor: simplify CLI session handling and error reporting by centra…
supunappri99 May 8, 2026
3d96b7b
refactor: move connection logic into try block and modernize session …
supunappri99 May 8, 2026
a4d5e0f
docs: update documentation regarding db file persistence, remote conf…
supunappri99 May 8, 2026
e1cd6aa
docs: update PR template and refine create-pr skill to support config…
supunappri99 May 8, 2026
ea64fdd
docs: update create-pr skill instructions to use remote tracking bran…
supunappri99 May 8, 2026
6ce6256
chore: bump package version to 1.2.1
supunappri99 May 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 51 additions & 12 deletions .claude/skills/create-pr/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ description: Generate a PR description and save to temp/pr-description.md.

Generate a standardized PR description for the PostKit project and save it to `temp/pr-description.md`.

## Arguments

Optional: base branch to compare against (e.g. `/create-pr dev` or `/create-pr main`).
Default to `main` if no argument is provided.

## Project Context

Read `CLAUDE.md` at the project root for project conventions.
Expand All @@ -15,16 +20,21 @@ Use the PR template at `.github/pull_request_template.md`.
## Workflow

### Step 1: Analyze Changes
Gather branch information:

Determine the base branch from the argument (default: `main`). Always compare against the **remote** tracking branch, not a local branch. First fetch to ensure the remote ref is up to date, then gather branch information:

```bash
git log origin/main...HEAD --oneline
git diff origin/main...HEAD --stat
git diff origin/main...HEAD
git fetch origin <base>
git log origin/<base>...HEAD --oneline
git diff origin/<base>...HEAD --stat
git diff origin/<base>...HEAD
```

Always use `origin/<base>` (not `<base>`) in all git commands so the comparison is against the remote state, not a potentially stale local branch.

Categorize changes into: features, fixes, refactors, tests, docs, chore.

### Step 2: Generate PR Description
Using the PR template structure, generate:

**Title** — under 70 characters with conventional commit prefix:
- `feat: <description>` for new features
Expand All @@ -34,15 +44,44 @@ Using the PR template structure, generate:
- `docs: <description>` for documentation changes
- `chore: <description>` for build/tooling changes

**Body** — using the template sections:
- Summary (1-3 bullet points)
- Changes (specific list)
- Type of Change (check one)
- Test Plan (checklist)
**Body** — follow the exact section order from `.github/pull_request_template.md`:
1. **Summary** — 1-3 bullet points describing what this PR does
2. **Changes** — specific list of changes made
3. **Type of Change** — check one box (`[x]`) matching the primary change type
4. **Test Plan** — check completed items; fill in "Manually tested" description
5. **Breaking Changes** — check "No breaking changes" if none; otherwise describe them

### Step 3: Save to File

Create `temp/` directory if needed and save to `temp/pr-description.md`.
Use the exact format from `.github/pull_request_template.md` — read that file and follow its structure.

The file must follow this exact structure:
```
# <title>

**Branch:** `<current-branch>` → `<base-branch>`

## Summary
...

## Changes
...

## Type of Change
...

## Test Plan
...

## Breaking Changes
...
```

Get the current branch name with:
```bash
git rev-parse --abbrev-ref HEAD
```

### Step 4: Show to User
Display the generated PR description and ask for confirmation or edits before saving.

Display the full generated PR description from the saved file and invite the user to request edits.
2 changes: 2 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
**Branch:** `<from-branch>` → `<to-branch>`

## Summary
<!-- 1-3 bullet points describing what this PR does -->

Expand Down
82 changes: 54 additions & 28 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,33 +77,44 @@ Then import and call the registration function in `cli/src/index.ts`.

The `db` module implements a **session-based migration workflow**:

1. **Session state**: Tracked in `.postkit/db/session.json`. Includes `remoteName` to track which remote was used.
2. **Named remotes**: Users can configure multiple named remote databases via `db.remotes` in config:
1. **Session state**: Tracked in `.postkit/db/session.json`. Includes `remoteName` to track which remote was used, and optional `containerID` for auto Docker containers.
2. **Named remotes**: Users can configure multiple named remote databases via `db.remotes` in secrets:
- At least one remote must be configured
- One remote can be marked as `default: true`
- Managed via `postkit db remote` commands
- All remote data (url, default, addedAt) stored entirely in `postkit.secrets.json` — nothing remote-related in `postkit.config.json`
3. **Binary resolution**: Both `pgschema` and `dbmate` binaries are auto-resolved:
- `pgschema`: Bundled in `vendor/pgschema/` for all platforms (darwin-{arm64,amd64}, linux-{arm64,amd64}, windows-{arm64,amd64})
- `dbmate`: npm-installed via the `dbmate` package
4. **Migration steps execution**: The `deploy` command uses `runSteps()` to execute multi-step operations with resume capability - if a step fails, re-running resumes from where it left off.
5. **Schema directory structure** (`db/schema/`):
4. **Auto Docker container** (`modules/db/services/container.ts`): When `localDbUrl` is empty, PostKit uses `resolveLocalDb(localDbUrl, remoteUrl, spinner)` which:
- Checks Docker availability (`checkDockerAvailable()`)
- Queries remote PG version via `getRemotePgMajorVersion()` (uses `SHOW server_version_num`) — callers do not pass the version
- Starts `postgres:{version}-alpine` on a free port in range 15432–15532
- Runs `pg_dump`/`psql` inside the container via `docker exec` (`cloneDatabaseViaContainer()`)
- Stores `containerID` in session; cleaned up on `db abort` or `db deploy` completion
- Used by `start`, `deploy`, and `import` commands
5. **Migration steps execution**: The `deploy` command uses `runSteps()` to execute multi-step operations with resume capability - if a step fails, re-running resumes from where it left off.
6. **Schema directory structure** (`db/schema/`):
- `infra/` - Pre-migration (roles, schemas, extensions) - excluded from pgschema
- `extensions/`, `types/`, `enums/`, `tables/`, etc. - pgschema-managed
- `seeds/` - Post-migration seed data - excluded from pgschema

### PostKit Directory Structure

All PostKit runtime files are stored in `.postkit/` (gitignored):
PostKit files are split between committed (shared with team) and gitignored (user-specific/ephemeral):

```
.postkit/
└── db/
├── session.json # Current session state
├── committed.json # Committed migrations tracking
├── plan.sql # Generated migration plan
├── schema.sql # Generated schema from files
├── session/ # Session migrations (temporary)
└── migrations/ # Committed migrations (for deploy)
├── db/
│ ├── session.json # GITIGNORED — active session state, local DB URL, container ID
│ ├── plan.sql # GITIGNORED — generated migration diff (ephemeral)
│ ├── schema.sql # GITIGNORED — generated schema artifact (ephemeral)
│ ├── session/ # GITIGNORED — temporary in-progress migrations
│ ├── committed.json # COMMITTED — migration tracking index (shared)
│ └── migrations/ # COMMITTED — committed SQL migrations for deploy (shared)
└── auth/
├── raw/ # COMMITTED — auth raw config (shared)
└── realm/ # COMMITTED — auth realm config (shared)
```

**Key paths** (from `modules/db/utils/db-config.ts`):
Expand All @@ -117,38 +128,53 @@ All PostKit runtime files are stored in `.postkit/` (gitignored):

**Key functions** (from `modules/db/services/`):
- `generateSchemaSQLAndFingerprint()` - Reads all schema files once and returns both the output path (`.postkit/db/schema.sql`) and a SHA-256 fingerprint of the source files
- `resolveLocalDb(localDbUrl, remoteUrl, spinner, spinnerText?)` (`container.ts`) - When `localDbUrl` is empty, fetches PG version from `remoteUrl` and starts an auto Docker container. Used by `start`, `deploy`, and `import` commands.
- `withPgClient<T>(url, fn)` (`database.ts`) - Scoped pg client wrapper; opens a connection, runs `fn`, closes on completion or error
- `checkDbPrerequisites(verbose)` (`prerequisites.ts`) - Shared pgschema + dbmate availability check used by all commands that need them
- `requireActiveSession()` (`utils/session.ts`) - Returns active session or throws a descriptive error
- `assertLocalConnection(session, spinner)` (`utils/session.ts`) - Tests local DB connection from session; throws if unreachable
- `resolveApplyTarget(target?)` (`utils/apply-target.ts`) - Resolves `"local"` or `"remote"` apply target; used by infra and seed commands
- `readJsonFile<T>(path)` / `writeJsonFile(path, data)` (`utils/json-file.ts`) - Typed JSON helpers used by remotes and committed migration tracking

### Configuration System

Config is loaded from `postkit.config.json` in the project root. Use `loadPostkitConfig()` from `common/config.ts`.
Config is loaded by `loadPostkitConfig()` from `common/config.ts`, which deep-merges two files:

**Database config structure:**
| File | Committed | Purpose |
|------|-----------|---------|
| `postkit.config.json` | Yes | Non-sensitive project settings (schema paths, flags) |
| `postkit.secrets.json` | No (gitignored) | Credentials + all remote config (URLs, names, defaults) |

**`postkit.config.json` (committed):**
```json
{
"db": {
"schemaPath": "db/schema",
"schema": "public"
}
}
```

**`postkit.secrets.json` (gitignored):**
```json
{
"db": {
"localDbUrl": "postgres://...",
"schemaPath": "schema",
"schema": "public",
"pgSchemaBin": "pgschema",
"dbmateBin": "dbmate",
"remotes": {
"dev": {
"url": "postgres://...",
"default": true,
"addedAt": "2024-12-31T10:00:00.000Z"
},
"staging": {
"url": "postgres://..."
}
"dev": { "url": "postgres://...", "default": true, "addedAt": "2024-12-31T10:00:00.000Z" },
"staging": { "url": "postgres://..." }
}
}
}
```

**`localDbUrl`**: Leave empty to have PostKit automatically start a `postgres:{version}-alpine` Docker container. The version is queried from the remote DB via `SHOW server_version_num`. The container is started on `db start` and stopped on `db abort`.

**Auto-migration:** When loading config, if `remotes` is missing but `remoteDbUrl` exists, it's auto-migrated to create a `default` remote.

Key config paths:
- `POSTKIT_CONFIG_FILE` = "postkit.config.json"
- `POSTKIT_SECRETS_FILE` = "postkit.secrets.json"
- `POSTKIT_DIR` = ".postkit" (session state, staged files)
- `vendor/` = Bundled binaries (resolved relative to CLI root, not project root)

Expand Down Expand Up @@ -236,8 +262,8 @@ logger.debug(`Remote URL: ${maskRemoteUrl(url)}`, options.verbose);
- All paths in `common/config.ts` are resolved relative to either `cliRoot` (the CLI installation) or `projectRoot` (where the user runs commands).
- Session files in `.postkit/db/` track migration state and enable resume capability.
- The `vendor/` directory contains platform-specific binaries that are bundled with the CLI - no separate installation required.
- The `.gitignore` should include `.postkit/` to ignore all runtime files.
- All migration-related files are in `.postkit/db/` - the only user-maintained DB files should be in `db/schema/`.
- The `.gitignore` includes specific ephemeral paths (session.json, plan.sql, schema.sql, session/) — NOT the whole `.postkit/` directory. Committed migrations and auth state ARE tracked by git.
- All migration-related files are in `.postkit/db/` the only user-maintained DB files should be in `db/schema/`.

## Claude Code Skills & Agents

Expand Down
17 changes: 0 additions & 17 deletions cli/.claude/settings.local.json

This file was deleted.

49 changes: 20 additions & 29 deletions cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,10 @@ npm install -g @appritech/postkit

### Requirements

| Requirement | Version | Download |
|-------------|---------|----------|
| **Node.js** | >= 18.0.0 | [nodejs.org](https://nodejs.org/) |
| **Docker** | Latest | [docker.com](https://www.docker.com/products/docker-desktop/) |
| **PostgreSQL CLI** | `psql`, `pg_dump` | [postgresql.org/download](https://www.postgresql.org/download/) |
| Requirement | Version | Notes |
|-------------|---------|-------|
| **Node.js** | >= 18.0.0 | Required |
| **Docker** | Latest | Required only when `localDbUrl` is empty (auto-container mode) |

### Basic Usage

Expand Down Expand Up @@ -110,41 +109,33 @@ Full documentation available at: **[https://docs.postkitstack.com/](https://docs

## 🔧 Configuration

Create a `postkit.config.json` in your project root:
PostKit uses two config files — run `postkit init` to generate both.

**`postkit.config.json`** (commit this):
```json
{
"db": {
"localDbUrl": "postgres://user:pass@localhost:5432/myapp_local",
"schemaPath": "db/schema",
"schema": "public",
"schema": "public"
}
}
```

**`postkit.secrets.json`** (gitignored — contains credentials and all remote config):
```json
{
"db": {
"localDbUrl": "postgres://user:pass@localhost:5432/myapp_local",
"remotes": {
"dev": {
"url": "postgres://user:pass@dev-host:5432/myapp",
"default": true
},
"staging": {
"url": "postgres://user:pass@staging-host:5432/myapp"
}
"dev": { "url": "postgres://user:pass@dev-host:5432/myapp", "default": true }
}
},
"auth": {
"source": {
"url": "https://keycloak-dev.example.com",
"adminUser": "admin",
"adminPass": "dev-password",
"realm": "myapp-realm"
},
"target": {
"url": "https://keycloak-staging.example.com",
"adminUser": "admin",
"adminPass": "staging-password"
},
"configCliImage": "adorsys/keycloak-config-cli:6.4.0-24"
}
}
```

> Leave `localDbUrl` empty to have PostKit automatically spin up a version-matched Docker container for your local database. No PostgreSQL client tools required on your host.
```

Run `postkit init` to create the `.postkit/` directory structure:

```
Expand Down
Loading