fraiseql-confiture 0.1.0__cp311-cp311-manylinux_2_34_x86_64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of fraiseql-confiture might be problematic. Click here for more details.

@@ -0,0 +1,95 @@
1
+ """Migration base class for database migrations."""
2
+
3
+ from abc import ABC, abstractmethod
4
+ from typing import Any
5
+
6
+ import psycopg
7
+
8
+
9
+ class Migration(ABC):
10
+ """Base class for all database migrations.
11
+
12
+ Each migration must:
13
+ - Define a version (e.g., "001", "002")
14
+ - Define a name (e.g., "create_users")
15
+ - Implement up() method for applying the migration
16
+ - Implement down() method for rolling back the migration
17
+
18
+ Example:
19
+ >>> class CreateUsers(Migration):
20
+ ... version = "001"
21
+ ... name = "create_users"
22
+ ...
23
+ ... def up(self):
24
+ ... self.execute('''
25
+ ... CREATE TABLE users (
26
+ ... id SERIAL PRIMARY KEY,
27
+ ... username TEXT NOT NULL
28
+ ... )
29
+ ... ''')
30
+ ...
31
+ ... def down(self):
32
+ ... self.execute('DROP TABLE users')
33
+ """
34
+
35
+ # Subclasses must define these
36
+ version: str
37
+ name: str
38
+
39
+ def __init__(self, connection: psycopg.Connection):
40
+ """Initialize migration with database connection.
41
+
42
+ Args:
43
+ connection: psycopg3 database connection
44
+
45
+ Raises:
46
+ TypeError: If version or name not defined in subclass
47
+ """
48
+ self.connection = connection
49
+
50
+ # Ensure subclass defined version and name
51
+ if not hasattr(self.__class__, "version") or self.__class__.version is None:
52
+ raise TypeError(f"{self.__class__.__name__} must define a 'version' class attribute")
53
+ if not hasattr(self.__class__, "name") or self.__class__.name is None:
54
+ raise TypeError(f"{self.__class__.__name__} must define a 'name' class attribute")
55
+
56
+ @abstractmethod
57
+ def up(self) -> None:
58
+ """Apply the migration.
59
+
60
+ This method must be implemented by subclasses to perform
61
+ the forward migration (e.g., CREATE TABLE, ALTER TABLE).
62
+
63
+ Raises:
64
+ NotImplementedError: If not implemented by subclass
65
+ """
66
+ raise NotImplementedError(f"{self.__class__.__name__}.up() must be implemented")
67
+
68
+ @abstractmethod
69
+ def down(self) -> None:
70
+ """Rollback the migration.
71
+
72
+ This method must be implemented by subclasses to perform
73
+ the reverse migration (e.g., DROP TABLE, revert ALTER).
74
+
75
+ Raises:
76
+ NotImplementedError: If not implemented by subclass
77
+ """
78
+ raise NotImplementedError(f"{self.__class__.__name__}.down() must be implemented")
79
+
80
+ def execute(self, sql: str, params: tuple[Any, ...] | None = None) -> None:
81
+ """Execute a SQL statement.
82
+
83
+ Args:
84
+ sql: SQL statement to execute
85
+ params: Optional query parameters (for parameterized queries)
86
+
87
+ Example:
88
+ >>> self.execute("CREATE TABLE users (id INT)")
89
+ >>> self.execute("INSERT INTO users (name) VALUES (%s)", ("Alice",))
90
+ """
91
+ with self.connection.cursor() as cursor:
92
+ if params:
93
+ cursor.execute(sql, params)
94
+ else:
95
+ cursor.execute(sql)
@@ -0,0 +1,203 @@
1
+ """Data models for schema representation.
2
+
3
+ These models represent database schema objects (tables, columns, indexes, etc.)
4
+ in a structured format for diff detection and comparison.
5
+ """
6
+
7
+ from dataclasses import dataclass, field
8
+ from enum import Enum
9
+
10
+
11
+ class ColumnType(str, Enum):
12
+ """PostgreSQL column types."""
13
+
14
+ # Integer types
15
+ SMALLINT = "SMALLINT"
16
+ INTEGER = "INTEGER"
17
+ BIGINT = "BIGINT"
18
+ SERIAL = "SERIAL"
19
+ BIGSERIAL = "BIGSERIAL"
20
+
21
+ # Numeric types
22
+ NUMERIC = "NUMERIC"
23
+ DECIMAL = "DECIMAL"
24
+ REAL = "REAL"
25
+ DOUBLE_PRECISION = "DOUBLE PRECISION"
26
+
27
+ # Text types
28
+ VARCHAR = "VARCHAR"
29
+ CHAR = "CHAR"
30
+ TEXT = "TEXT"
31
+
32
+ # Boolean
33
+ BOOLEAN = "BOOLEAN"
34
+
35
+ # Date/Time
36
+ DATE = "DATE"
37
+ TIME = "TIME"
38
+ TIMESTAMP = "TIMESTAMP"
39
+ TIMESTAMPTZ = "TIMESTAMPTZ"
40
+
41
+ # UUID
42
+ UUID = "UUID"
43
+
44
+ # JSON
45
+ JSON = "JSON"
46
+ JSONB = "JSONB"
47
+
48
+ # Binary
49
+ BYTEA = "BYTEA"
50
+
51
+ # Unknown/Custom
52
+ UNKNOWN = "UNKNOWN"
53
+
54
+
55
+ @dataclass
56
+ class Column:
57
+ """Represents a database column."""
58
+
59
+ name: str
60
+ type: ColumnType
61
+ nullable: bool = True
62
+ default: str | None = None
63
+ primary_key: bool = False
64
+ unique: bool = False
65
+ length: int | None = None # For VARCHAR(n), etc.
66
+
67
+ def __eq__(self, other: object) -> bool:
68
+ """Compare columns for equality."""
69
+ if not isinstance(other, Column):
70
+ return NotImplemented
71
+ return (
72
+ self.name == other.name
73
+ and self.type == other.type
74
+ and self.nullable == other.nullable
75
+ and self.default == other.default
76
+ and self.primary_key == other.primary_key
77
+ and self.unique == other.unique
78
+ and self.length == other.length
79
+ )
80
+
81
+ def __hash__(self) -> int:
82
+ """Make column hashable for use in sets."""
83
+ return hash(
84
+ (
85
+ self.name,
86
+ self.type,
87
+ self.nullable,
88
+ self.default,
89
+ self.primary_key,
90
+ self.unique,
91
+ self.length,
92
+ )
93
+ )
94
+
95
+
96
+ @dataclass
97
+ class Table:
98
+ """Represents a database table."""
99
+
100
+ name: str
101
+ columns: list[Column] = field(default_factory=list)
102
+ indexes: list[str] = field(default_factory=list) # Simplified for MVP
103
+ constraints: list[str] = field(default_factory=list) # Simplified for MVP
104
+
105
+ def get_column(self, name: str) -> Column | None:
106
+ """Get column by name."""
107
+ for col in self.columns:
108
+ if col.name == name:
109
+ return col
110
+ return None
111
+
112
+ def has_column(self, name: str) -> bool:
113
+ """Check if table has column."""
114
+ return self.get_column(name) is not None
115
+
116
+ def __eq__(self, other: object) -> bool:
117
+ """Compare tables for equality."""
118
+ if not isinstance(other, Table):
119
+ return NotImplemented
120
+ return (
121
+ self.name == other.name
122
+ and self.columns == other.columns
123
+ and self.indexes == other.indexes
124
+ and self.constraints == other.constraints
125
+ )
126
+
127
+
128
+ @dataclass
129
+ class Schema:
130
+ """Represents a complete database schema."""
131
+
132
+ tables: list[Table] = field(default_factory=list)
133
+
134
+ def get_table(self, name: str) -> Table | None:
135
+ """Get table by name."""
136
+ for table in self.tables:
137
+ if table.name == name:
138
+ return table
139
+ return None
140
+
141
+ def has_table(self, name: str) -> bool:
142
+ """Check if schema has table."""
143
+ return self.get_table(name) is not None
144
+
145
+ def table_names(self) -> list[str]:
146
+ """Get list of all table names."""
147
+ return [table.name for table in self.tables]
148
+
149
+
150
+ @dataclass
151
+ class SchemaChange:
152
+ """Represents a single change between two schemas."""
153
+
154
+ type: str # ADD_TABLE, DROP_TABLE, ADD_COLUMN, etc.
155
+ table: str | None = None
156
+ column: str | None = None
157
+ old_value: str | None = None
158
+ new_value: str | None = None
159
+ details: dict[str, str] | None = None
160
+
161
+ def __str__(self) -> str:
162
+ """String representation of change."""
163
+ if self.type == "ADD_TABLE":
164
+ return f"ADD TABLE {self.table}"
165
+ elif self.type == "DROP_TABLE":
166
+ return f"DROP TABLE {self.table}"
167
+ elif self.type == "RENAME_TABLE":
168
+ return f"RENAME TABLE {self.old_value} TO {self.new_value}"
169
+ elif self.type == "ADD_COLUMN":
170
+ return f"ADD COLUMN {self.table}.{self.column}"
171
+ elif self.type == "DROP_COLUMN":
172
+ return f"DROP COLUMN {self.table}.{self.column}"
173
+ elif self.type == "RENAME_COLUMN":
174
+ return f"RENAME COLUMN {self.table}.{self.old_value} TO {self.new_value}"
175
+ elif self.type == "CHANGE_COLUMN_TYPE":
176
+ return f"CHANGE COLUMN TYPE {self.table}.{self.column} FROM {self.old_value} TO {self.new_value}"
177
+ elif self.type == "CHANGE_COLUMN_NULLABLE":
178
+ return f"CHANGE COLUMN NULLABLE {self.table}.{self.column} FROM {self.old_value} TO {self.new_value}"
179
+ elif self.type == "CHANGE_COLUMN_DEFAULT":
180
+ return f"CHANGE COLUMN DEFAULT {self.table}.{self.column}"
181
+ else:
182
+ return f"{self.type}: {self.table}.{self.column if self.column else ''}"
183
+
184
+
185
+ @dataclass
186
+ class SchemaDiff:
187
+ """Represents the difference between two schemas."""
188
+
189
+ changes: list[SchemaChange] = field(default_factory=list)
190
+
191
+ def has_changes(self) -> bool:
192
+ """Check if there are any changes."""
193
+ return len(self.changes) > 0
194
+
195
+ def count_by_type(self, change_type: str) -> int:
196
+ """Count changes of a specific type."""
197
+ return sum(1 for c in self.changes if c.type == change_type)
198
+
199
+ def __str__(self) -> str:
200
+ """String representation of diff."""
201
+ if not self.has_changes():
202
+ return "No changes detected"
203
+ return "\n".join(str(c) for c in self.changes)
@@ -0,0 +1,350 @@
1
+ Metadata-Version: 2.4
2
+ Name: fraiseql-confiture
3
+ Version: 0.1.0
4
+ Classifier: Development Status :: 3 - Alpha
5
+ Classifier: Intended Audience :: Developers
6
+ Classifier: License :: OSI Approved :: MIT License
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: Programming Language :: Python :: 3.11
9
+ Classifier: Programming Language :: Python :: 3.12
10
+ Classifier: Programming Language :: Python :: 3.13
11
+ Classifier: Topic :: Database
12
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
13
+ Requires-Dist: typer>=0.12.0
14
+ Requires-Dist: rich>=13.7.0
15
+ Requires-Dist: pydantic>=2.5.0
16
+ Requires-Dist: pyyaml>=6.0.1
17
+ Requires-Dist: psycopg[binary]>=3.1.0
18
+ Requires-Dist: sqlparse>=0.5.0
19
+ Requires-Dist: pytest>=8.0.0 ; extra == 'dev'
20
+ Requires-Dist: pytest-asyncio>=0.23.0 ; extra == 'dev'
21
+ Requires-Dist: pytest-cov>=4.1.0 ; extra == 'dev'
22
+ Requires-Dist: pytest-watch>=4.2.0 ; extra == 'dev'
23
+ Requires-Dist: ruff>=0.6.0 ; extra == 'dev'
24
+ Requires-Dist: mypy>=1.11.0 ; extra == 'dev'
25
+ Requires-Dist: pre-commit>=3.5.0 ; extra == 'dev'
26
+ Requires-Dist: types-pyyaml>=6.0.0 ; extra == 'dev'
27
+ Requires-Dist: maturin>=1.7.0 ; extra == 'dev'
28
+ Provides-Extra: dev
29
+ Provides-Extra: fraiseql
30
+ License-File: LICENSE
31
+ Summary: PostgreSQL migrations, sweetly done ๐Ÿ“
32
+ Keywords: postgresql,migration,database,schema,ddl
33
+ Home-Page: https://github.com/fraiseql/confiture
34
+ Author-email: evoludigit <lionel@fraiseql.com>
35
+ License: MIT
36
+ Requires-Python: >=3.11
37
+ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
38
+ Project-URL: Homepage, https://github.com/fraiseql/confiture
39
+ Project-URL: Documentation, https://github.com/fraiseql/confiture
40
+ Project-URL: Repository, https://github.com/fraiseql/confiture
41
+ Project-URL: Issues, https://github.com/fraiseql/confiture/issues
42
+ Project-URL: FraiseQL, https://github.com/fraiseql/fraiseql
43
+
44
+ # Confiture ๐Ÿ“
45
+
46
+ **PostgreSQL migrations, sweetly done**
47
+
48
+ Confiture is the official migration tool for [FraiseQL](https://github.com/fraiseql/fraiseql), designed with a **build-from-scratch philosophy** and **4 migration strategies** to handle every scenario from local development to zero-downtime production deployments.
49
+
50
+ > **Part of the FraiseQL ecosystem** - While Confiture works standalone for any PostgreSQL project, it's designed to integrate seamlessly with FraiseQL's GraphQL-first approach.
51
+
52
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
53
+ [![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)
54
+ [![PostgreSQL 12+](https://img.shields.io/badge/PostgreSQL-12%2B-blue?logo=postgresql&logoColor=white)](https://www.postgresql.org/)
55
+ [![CI](https://img.shields.io/github/actions/workflow/status/fraiseql/confiture/ci.yml?branch=main&label=CI&logo=github)](https://github.com/fraiseql/confiture/actions/workflows/ci.yml)
56
+ [![Code style: ruff](https://img.shields.io/badge/code%20style-ruff-000000.svg)](https://github.com/astral-sh/ruff)
57
+ [![Type checked: mypy](https://img.shields.io/badge/type%20checked-mypy-blue.svg)](https://github.com/python/mypy)
58
+ [![Made with Rust](https://img.shields.io/badge/Made%20with-Rust-orange?logo=rust)](https://www.rust-lang.org/)
59
+ [![Part of FraiseQL](https://img.shields.io/badge/Part%20of-FraiseQL-ff69b4)](https://github.com/fraiseql/fraiseql)
60
+ [![Status: Beta](https://img.shields.io/badge/status-beta-orange)](https://github.com/fraiseql/confiture)
61
+
62
+ ---
63
+
64
+ ## Why Confiture?
65
+
66
+ Traditional migration tools (Alembic, Django migrations) **replay migration history** to build databases. This is slow and brittle.
67
+
68
+ Confiture treats **DDL source files as the single source of truth**:
69
+
70
+ - โœ… **Fresh databases in <1 second** (not minutes)
71
+ - โœ… **4 migration strategies** (simple ALTER to zero-downtime FDW)
72
+ - โœ… **Production data sync** built-in (with PII anonymization)
73
+ - โœ… **Python + Rust performance** (10-50x faster than pure Python)
74
+ - โœ… **Perfect with FraiseQL**, useful for everyone
75
+
76
+ ---
77
+
78
+ ## The Four Mediums
79
+
80
+ ### 1๏ธโƒฃ Build from DDL
81
+ ```bash
82
+ confiture build --env production
83
+ ```
84
+ Build fresh database from `db/schema/` DDL files in <1 second.
85
+
86
+ ### 2๏ธโƒฃ Incremental Migrations (ALTER)
87
+ ```bash
88
+ confiture migrate up
89
+ ```
90
+ Apply migrations to existing database (simple schema changes).
91
+
92
+ ### 3๏ธโƒฃ Production Data Sync
93
+ ```bash
94
+ confiture sync --from production --anonymize users.email
95
+ ```
96
+ Copy production data to local/staging with PII anonymization.
97
+
98
+ ### 4๏ธโƒฃ Schema-to-Schema Migration (Zero-Downtime)
99
+ ```bash
100
+ confiture migrate schema-to-schema --strategy fdw
101
+ ```
102
+ Complex migrations via FDW with 0-5 second downtime.
103
+
104
+ ---
105
+
106
+ ## Quick Start
107
+
108
+ ### Installation
109
+
110
+ ```bash
111
+ pip install fraiseql-confiture
112
+
113
+ # Or with FraiseQL integration
114
+ pip install fraiseql-confiture[fraiseql]
115
+ ```
116
+
117
+ ### Initialize Project
118
+
119
+ ```bash
120
+ confiture init
121
+ ```
122
+
123
+ Creates:
124
+ ```
125
+ db/
126
+ โ”œโ”€โ”€ schema/ # DDL: CREATE TABLE, views, functions
127
+ โ”‚ โ”œโ”€โ”€ 00_common/
128
+ โ”‚ โ”œโ”€โ”€ 10_tables/
129
+ โ”‚ โ””โ”€โ”€ 20_views/
130
+ โ”œโ”€โ”€ seeds/ # INSERT: Environment-specific test data
131
+ โ”‚ โ”œโ”€โ”€ common/
132
+ โ”‚ โ”œโ”€โ”€ development/
133
+ โ”‚ โ””โ”€โ”€ test/
134
+ โ”œโ”€โ”€ migrations/ # Generated migration files
135
+ โ””โ”€โ”€ environments/ # Environment configurations
136
+ โ”œโ”€โ”€ local.yaml
137
+ โ”œโ”€โ”€ test.yaml
138
+ โ””โ”€โ”€ production.yaml
139
+ ```
140
+
141
+ ### Build Schema
142
+
143
+ ```bash
144
+ # Build local database
145
+ confiture build --env local
146
+
147
+ # Build production schema
148
+ confiture build --env production
149
+ ```
150
+
151
+ ### Create Migration
152
+
153
+ ```bash
154
+ # Edit schema
155
+ vim db/schema/10_tables/users.sql
156
+
157
+ # Generate migration
158
+ confiture migrate generate --name "add_user_bio"
159
+
160
+ # Apply migration
161
+ confiture migrate up
162
+ ```
163
+
164
+ ---
165
+
166
+ ## Documentation
167
+
168
+ ### ๐Ÿ“– User Guides
169
+ - **[Medium 1: Build from DDL](docs/guides/medium-1-build-from-ddl.md)** - Fresh databases in <1 second
170
+ - **[Medium 2: Incremental Migrations](docs/guides/medium-2-incremental-migrations.md)** - ALTER-based changes
171
+ - **[Medium 3: Production Data Sync](docs/guides/medium-3-production-sync.md)** - Copy and anonymize data
172
+ - **[Medium 4: Zero-Downtime Migrations](docs/guides/medium-4-schema-to-schema.md)** - Schema-to-schema via FDW
173
+ - **[Migration Decision Tree](docs/guides/migration-decision-tree.md)** - Choose the right strategy
174
+
175
+ ### ๐Ÿ“š API Reference
176
+ - **[CLI Reference](docs/reference/cli.md)** - All commands documented
177
+ - **[Configuration Reference](docs/reference/configuration.md)** - Environment configuration
178
+ - **[Schema Builder API](docs/api/builder.md)** - Building schemas programmatically
179
+ - **[Migrator API](docs/api/migrator.md)** - Migration execution
180
+ - **[Syncer API](docs/api/syncer.md)** - Production data sync
181
+ - **[Schema-to-Schema API](docs/api/schema-to-schema.md)** - Zero-downtime migrations
182
+
183
+ ### ๐Ÿ’ก Examples
184
+ - **[Examples Overview](examples/)** - 5 complete production examples
185
+ - **[Basic Migration](examples/01-basic-migration/)** - Learn the fundamentals (15 min)
186
+ - **[FraiseQL Integration](examples/02-fraiseql-integration/)** - GraphQL workflow (20 min)
187
+ - **[Zero-Downtime](examples/03-zero-downtime-migration/)** - Production deployment (30 min)
188
+ - **[Production Sync](examples/04-production-sync-anonymization/)** - PII anonymization (25 min)
189
+ - **[Multi-Environment Workflow](examples/05-multi-environment-workflow/)** - Complete CI/CD (30 min)
190
+
191
+ ---
192
+
193
+ ## Features
194
+
195
+ ### โœ… Complete (Phases 1-3)
196
+
197
+ **Core Migration System**:
198
+ - โœ… Build from DDL (Medium 1) - Fresh databases in <1 second
199
+ - โœ… Incremental migrations (Medium 2) - Simple ALTER-based changes
200
+ - โœ… Production data sync (Medium 3) - Copy with PII anonymization
201
+ - โœ… Zero-downtime migrations (Medium 4) - Schema-to-schema via FDW
202
+
203
+ **Performance & Distribution**:
204
+ - โœ… **Rust performance layer** (10-50x speedup) ๐Ÿš€
205
+ - โœ… **Binary wheels** for Linux, macOS, Windows
206
+ - โœ… Parallel migration execution
207
+ - โœ… Progress tracking with resumability
208
+
209
+ **Developer Experience**:
210
+ - โœ… Environment-specific seed data (development/test/production)
211
+ - โœ… Schema diff detection with auto-generation
212
+ - โœ… CLI with rich terminal output and colors
213
+ - โœ… Comprehensive documentation (5 guides, 4 API docs)
214
+ - โœ… Production-ready examples (5 complete scenarios)
215
+
216
+ **Integration & Safety**:
217
+ - โœ… FraiseQL GraphQL integration
218
+ - โœ… Multi-environment configuration
219
+ - โœ… Transaction safety with rollback support
220
+ - โœ… PII anonymization with compliance tools
221
+ - โœ… CI/CD pipeline examples (GitHub Actions)
222
+
223
+ ### ๐Ÿšง Coming Soon (Phase 4)
224
+ - Advanced migration hooks (before/after)
225
+ - Custom anonymization strategies
226
+ - Interactive migration wizard
227
+ - Migration dry-run mode
228
+ - Database schema linting
229
+
230
+ ---
231
+
232
+ ## Comparison
233
+
234
+ | Feature | Alembic | pgroll | **Confiture** |
235
+ |---------|---------|--------|---------------|
236
+ | **Philosophy** | Migration replay | Multi-version schema | **Build-from-DDL** |
237
+ | **Fresh DB setup** | Minutes | Minutes | **<1 second** |
238
+ | **Zero-downtime** | โŒ No | โœ… Yes | **โœ… Yes (FDW)** |
239
+ | **Production sync** | โŒ No | โŒ No | **โœ… Built-in** |
240
+ | **Language** | Python | Go | **Python + Rust** |
241
+
242
+ ---
243
+
244
+ ## Development Status
245
+
246
+ **Current Version**: 0.1.0 (Initial PyPI Release) ๐ŸŽ‰
247
+
248
+ **Milestone Progress**:
249
+ - โœ… Phase 1: Python MVP (Complete - Oct 2025)
250
+ - โœ… Phase 2: Rust Performance Layer (Complete - Oct 2025)
251
+ - โœ… Phase 3: Production Features (Complete - Oct 2025)
252
+ - โœ… Zero-downtime migrations (FDW)
253
+ - โœ… Production data sync with PII anonymization
254
+ - โœ… Comprehensive documentation (5 guides, 4 API references)
255
+ - โœ… Production examples (5 complete scenarios)
256
+ - โณ Phase 4: Advanced Features (Q1 2026)
257
+ - Migration hooks, wizards, dry-run mode
258
+
259
+ **Statistics**:
260
+ - ๐Ÿ“ฆ 4 migration strategies implemented
261
+ - ๐Ÿ“– 5 comprehensive user guides
262
+ - ๐Ÿ“š 4 API reference pages
263
+ - ๐Ÿ’ก 5 production-ready examples
264
+ - ๐Ÿงช 95% test coverage
265
+ - โšก 10-50x performance with Rust
266
+
267
+ See [PHASES.md](PHASES.md) for detailed roadmap.
268
+
269
+ ---
270
+
271
+ ## Contributing
272
+
273
+ Contributions welcome! We'd love your help making Confiture even better.
274
+
275
+ **Quick Start**:
276
+ ```bash
277
+ # Clone repository
278
+ git clone https://github.com/fraiseql/confiture.git
279
+ cd confiture
280
+
281
+ # Install dependencies (includes Rust build)
282
+ uv sync --all-extras
283
+
284
+ # Build Rust extension
285
+ uv run maturin develop
286
+
287
+ # Run tests
288
+ uv run pytest --cov=confiture
289
+
290
+ # Format code
291
+ uv run ruff format .
292
+
293
+ # Type checking
294
+ uv run mypy python/confiture/
295
+ ```
296
+
297
+ **Resources**:
298
+ - **[CONTRIBUTING.md](CONTRIBUTING.md)** - Contributing guidelines
299
+ - **[CLAUDE.md](CLAUDE.md)** - AI-assisted development guide
300
+ - **[PHASES.md](PHASES.md)** - Detailed roadmap
301
+
302
+ **What to contribute**:
303
+ - ๐Ÿ› Bug fixes
304
+ - โœจ New features
305
+ - ๐Ÿ“– Documentation improvements
306
+ - ๐Ÿ’ก New examples
307
+ - ๐Ÿงช Test coverage improvements
308
+
309
+ ---
310
+
311
+ ## Author
312
+
313
+ **Vibe-engineered by [Lionel Hamayon](https://github.com/LionelHamayon)** ๐Ÿ“
314
+
315
+ Confiture was crafted with care as the migration tool for the FraiseQL ecosystem, combining the elegance of Python with the performance of Rust, and the sweetness of strawberry jam.
316
+
317
+ ---
318
+
319
+ ## License
320
+
321
+ MIT License - see [LICENSE](LICENSE) for details.
322
+
323
+ Copyright (c) 2025 Lionel Hamayon
324
+
325
+ ---
326
+
327
+ ## Acknowledgments
328
+
329
+ - Inspired by printoptim_backend's build-from-scratch approach
330
+ - Built for [FraiseQL](https://github.com/fraiseql/fraiseql) GraphQL framework
331
+ - Influenced by pgroll, Alembic, and Reshape
332
+ - Developed with AI-assisted vibe engineering โœจ
333
+
334
+ ---
335
+
336
+ ## FraiseQL Ecosystem
337
+
338
+ Confiture is part of the FraiseQL family:
339
+
340
+ - **[FraiseQL](https://github.com/fraiseql/fraiseql)** - Modern GraphQL framework for Python
341
+ - **[Confiture](https://github.com/fraiseql/confiture)** - PostgreSQL migration tool (you are here)
342
+
343
+ ---
344
+
345
+ *Making jam from strawberries, one migration at a time.* ๐Ÿ“โ†’๐Ÿฏ
346
+
347
+ *Vibe-engineered with โค๏ธ by Lionel Hamayon*
348
+
349
+ **[Documentation](https://github.com/fraiseql/confiture)** โ€ข **[GitHub](https://github.com/fraiseql/confiture)** โ€ข **[PyPI](https://pypi.org/project/fraiseql-confiture/)**
350
+
@@ -0,0 +1,24 @@
1
+ confiture/__init__.py,sha256=tCAHIJRp5S5JI_5rzwmI_jqOSJKy0QuOuSzPv3l2lhI,1162
2
+ confiture/_core.cpython-311-x86_64-linux-gnu.so,sha256=YNx1x-t6ulDeH03S5qK9V3LyycIPJE86Rru_GU7Hj34,504496
3
+ confiture/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ confiture/cli/main.py,sha256=iYFN2hgkK6a_sbKrkpq6rMmdQvi58EOW7ojOaTfs0g4,22479
5
+ confiture/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ confiture/config/environment.py,sha256=8o2Dt6fko6Ovvn1GBW76l_zjaZABIHk5DKqBbfGcMTA,6229
7
+ confiture/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ confiture/core/builder.py,sha256=PlFiyD1T81briS25LkYm0EbQjo34VDBKcAGlmn7H4rc,11388
9
+ confiture/core/connection.py,sha256=s6ZACflypVRYUc9MZ9KNZuFObbtYjLWQbcGN5ebisaw,3397
10
+ confiture/core/differ.py,sha256=etICDfHY9wOvqxFkiwuVIbHS7N8gmKG04qhbfWtB2t0,18024
11
+ confiture/core/migration_generator.py,sha256=-vOzZO8ENjJqDUOaNsE6s28SgxQXP9NYDwgV_7RiYW4,9732
12
+ confiture/core/migrator.py,sha256=m-__eQ-ArzZ_Vgd0Vo0kFkjb9G6EQl-QRhmcw0ocFoQ,13352
13
+ confiture/core/schema_to_schema.py,sha256=ZilXOhK4O_SStqzqG3oztNpakskF2BW74bA4buZnKz8,22292
14
+ confiture/core/syncer.py,sha256=d1b4u1QjrRaw2jia37wiNVZYZ51GaKoJ1E7dJ3vCeM0,18393
15
+ confiture/exceptions.py,sha256=wuSzE-XpX5QeZEqNQqi85efKu5l2-QUhmBbjDLKm02w,3339
16
+ confiture/integrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
+ confiture/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
+ confiture/models/migration.py,sha256=KcTY2EUCrsFOqogoAzVDpZAoQAKFFaH13-Zyjf-yYfg,3106
19
+ confiture/models/schema.py,sha256=9zQzyyw35HR0T1b0T9hI9xoripdTeWLDFTnO_ibcrMI,5909
20
+ fraiseql_confiture-0.1.0.dist-info/METADATA,sha256=t_i1-h4Bq0oSaWT6zDX25vwwfCrPjnbRsk30CA5X1us,11826
21
+ fraiseql_confiture-0.1.0.dist-info/WHEEL,sha256=OVlAaljgWPhfhJRVM6VsLWLOev5VbYFkuMRMXaOVkcY,108
22
+ fraiseql_confiture-0.1.0.dist-info/entry_points.txt,sha256=nRq8YKFFl7Pr0462GxN2Y1PAe77UJVXC0xrw92qnZks,51
23
+ fraiseql_confiture-0.1.0.dist-info/licenses/LICENSE,sha256=c6fKI61h1Rn-8ZkldOQxeyxEQYfzjpXpu_HgVjSu1ZI,1071
24
+ fraiseql_confiture-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: maturin (1.9.6)
3
+ Root-Is-Purelib: false
4
+ Tag: cp311-cp311-manylinux_2_34_x86_64
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ confiture=confiture.cli.main:app