{"id":48276864,"url":"https://github.com/llk23r/kapsicum-migration-kit","last_synced_at":"2026-04-04T22:37:02.151Z","repository":{"id":338954005,"uuid":"1159831837","full_name":"llk23r/kapsicum-migration-kit","owner":"llk23r","description":"Reusable migration toolkit for Swift apps with optional GRDB and CLI layers.","archived":false,"fork":false,"pushed_at":"2026-02-17T08:50:15.000Z","size":27,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-02-17T13:47:26.760Z","etag":null,"topics":["data-migration","migrations","semver","swift","swift-package"],"latest_commit_sha":null,"homepage":null,"language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/llk23r.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-02-17T08:04:38.000Z","updated_at":"2026-02-17T08:50:18.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/llk23r/kapsicum-migration-kit","commit_stats":null,"previous_names":["llk23r/kapsicum-migration-kit"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/llk23r/kapsicum-migration-kit","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/llk23r%2Fkapsicum-migration-kit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/llk23r%2Fkapsicum-migration-kit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/llk23r%2Fkapsicum-migration-kit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/llk23r%2Fkapsicum-migration-kit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/llk23r","download_url":"https://codeload.github.com/llk23r/kapsicum-migration-kit/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/llk23r%2Fkapsicum-migration-kit/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31417101,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-04T20:09:54.854Z","status":"ssl_error","status_checked_at":"2026-04-04T20:09:44.350Z","response_time":60,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["data-migration","migrations","semver","swift","swift-package"],"created_at":"2026-04-04T22:37:00.802Z","updated_at":"2026-04-04T22:37:02.141Z","avatar_url":"https://github.com/llk23r.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# `MigrationKit`\n\nA migration toolkit for Swift apps. The core module is database-agnostic; a GRDB runner and a CLI layer ship alongside it.\n\n```swift\nimport MigrationKit\nimport MigrationKitGRDB\n\nlet steps: [MigrationStep\u003cDatabase\u003e] = [\n    .init(identifier: \"0001_create_items\", sourceFile: \"M0001_CreateItems.swift\") { db in\n        try db.create(table: \"items\") { t in\n            t.column(\"id\", .integer).primaryKey()\n        }\n    }\n]\n\nlet runner = try GRDBMigrationRunner(steps: steps)\ntry runner.migrate(in: writer)\n```\n\n## Table of Contents\n\n- [Why this exists](#why-this-exists)\n- [Installation](#installation)\n- [Products](#products)\n- [Quick Start](#quick-start)\n- [CLI](#cli)\n- [API Overview](#api-overview)\n- [How It Works](docs/HOW_IT_WORKS.md)\n- [Versioning](#versioning)\n- [Contributing](#contributing)\n- [License](#license)\n\n## Why this exists\n\nMigration logic tends to live inside the app that needs it, which makes it hard to share across targets or test in isolation. MigrationKit pulls that logic into a standalone package.\n\nSteps are registered with lexicographically ordered identifiers (e.g. `0001_create_users`, `0002_add_email`). The registry validates uniqueness and ordering at init time, before anything touches the database. Each step can optionally declare a rollback closure. After a run, the verifier checks SQLite integrity and foreign keys automatically.\n\n## Installation\n\nRequires Swift 6.1+, macOS 13+ / iOS 16+.\n\n```swift\n.package(url: \"https://github.com/llk23r/kapsicum-migration-kit.git\", from: \"0.2.0\")\n```\n\nThen add whichever products you need to your target:\n\n```swift\n.product(name: \"MigrationKit\", package: \"kapsicum-migration-kit\"),\n.product(name: \"MigrationKitGRDB\", package: \"kapsicum-migration-kit\"),\n.product(name: \"MigrationKitCLI\", package: \"kapsicum-migration-kit\"),\n```\n\n## Products\n\n| Product | What it contains |\n|---------|-----------------|\n| `MigrationKit` | Core types: `MigrationStep`, `MigrationRegistry`, errors, host integration hooks |\n| `MigrationKitGRDB` | GRDB-backed runner, verifier, SQL helpers, schema snapshot provider |\n| `MigrationKitCLI` | ArgumentParser CLI host with migrate/status/rollback/verify/schema-dump commands |\n| `migrationkit-cli` | Executable shell; embed `MigrationCLI.run(arguments:host:)` in your own binary |\n\n## Quick Start\n\nDefine steps, create a runner, migrate. The runner builds a `MigrationRegistry` internally, so ordering and uniqueness checks happen at init.\n\n```swift\nimport MigrationKit\nimport MigrationKitGRDB\n\nlet steps: [MigrationStep\u003cDatabase\u003e] = [\n    .init(\n        identifier: \"0001_create_users\",\n        sourceFile: \"M0001_CreateUsers.swift\",\n        apply: { db in\n            try db.create(table: \"users\") { t in\n                t.autoIncrementedPrimaryKey(\"id\")\n                t.column(\"email\", .text).notNull().unique()\n            }\n        },\n        rollback: { db in try db.drop(table: \"users\") }\n    ),\n    .init(\n        identifier: \"0002_add_avatar_url\",\n        sourceFile: \"M0002_AddAvatarURL.swift\",\n        apply: { db in\n            try db.alter(table: \"users\") { t in\n                t.add(column: \"avatar_url\", .text)\n            }\n        }\n    )\n]\n\nlet runner = try GRDBMigrationRunner(steps: steps)\n\n// Apply all pending migrations\ntry runner.migrate(in: writer)\n\n// Check what has been applied\nlet statuses = try runner.migrationStatus(in: writer)\nlet pending = try runner.pendingMigrationIdentifiers(in: writer)\n\n// Undo the last applied migration\ntry runner.rollbackLastMigration(in: writer)\n```\n\n## CLI\n\nWire `MigrationCLI` into your executable to get ActiveRecord-style commands:\n\n```swift\nlet host = MigrationCLIHost(\n    runner: runner,\n    openWriter: { options in try openDatabase(options) },\n    schemaSnapshotProvider: GRDBSchemaSnapshotProvider(\n        migrate: { queue in\n            try runner.migrate(in: queue)\n        }\n    )\n)\ntry MigrationCLI.run(arguments: CommandLine.arguments, host: host)\n```\n\n`MigrationCLI.run(arguments:host:)` accepts either:\n- `[\"migrationkit\", \"status\", ...]`\n- `[\"status\", ...]`\n\n| Command | What it does |\n|---------|-------------|\n| `migrate` | Apply pending migrations, optionally `--to \u003cidentifier\u003e` |\n| `status` | Print up/down for every registered migration |\n| `rollback` | Undo the last N steps (`--step N`) |\n| `verify` | Run integrity checks without migrating |\n| `schema-dump` | Write canonical schema SQL to a file (requires `schemaSnapshotProvider`) |\n\n## API Overview\n\nCore (`MigrationKit`):\n\n| Type | What it does |\n|------|-------------|\n| `MigrationStep\u003cDatabase\u003e` | Holds an identifier, source file, apply closure, and optional rollback closure |\n| `MigrationRegistry\u003cDatabase\u003e` | Validates ordering and uniqueness; exposes the manifest and rollback-capable identifiers |\n| `MigrationHostIntegration` | Optional hooks for schema bootstrap, integrity checks, and post-migration verification |\n| `SchemaSnapshotProvider` | Protocol for generating a canonical schema snapshot |\n\nGRDB (`MigrationKitGRDB`):\n\n| Type | What it does |\n|------|-------------|\n| `GRDBMigrationRunner` | Migrate forward, roll back, query status and pending steps |\n| `GRDBMigrationVerifier` | SQLite `quick_check`, foreign key validation, required index checks |\n\nErrors:\n\n| Error | When it's thrown |\n|-------|-----------------|\n| `MigrationKitError.duplicateIdentifiers` | Two steps share the same identifier |\n| `MigrationKitError.identifiersOutOfOrder` | Steps aren't in lexicographic order |\n| `MigrationKitError.rollbackNotDefined` | Rollback requested on a step that doesn't have one |\n| `GRDBMigrationVerificationError.quickCheckFailed` | SQLite integrity check fails |\n| `GRDBMigrationVerificationError.foreignKeyViolations` | Foreign key violations found |\n\n## Versioning\n\n[Semantic Versioning](https://semver.org). Tags use the `v` prefix (e.g. `v0.2.0`). See [CHANGELOG.md](CHANGELOG.md).\n\n## Contributing\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md).\n\n## License\n\nMIT. See [LICENSE](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fllk23r%2Fkapsicum-migration-kit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fllk23r%2Fkapsicum-migration-kit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fllk23r%2Fkapsicum-migration-kit/lists"}