ClickMigrate 1.0.0__tar.gz → 1.0.3__tar.gz

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.
@@ -1,9 +1,25 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ClickMigrate
3
- Version: 1.0.0
3
+ Version: 1.0.3
4
4
  Summary: A modern, simple database migration framework for ClickHouse.
5
5
  Author-email: Ivo Theis <ivo.theis@queueforge.dev>
6
6
  License: MIT
7
+ Project-URL: Homepage, https://queueforge.dev
8
+ Project-URL: Repository, https://github.com/queueforge/ClickMigrate
9
+ Project-URL: Issues, https://github.com/queueforge/ClickMigrate/issues
10
+ Project-URL: Releases, https://github.com/queueforge/ClickMigrate/releases
11
+ Project-URL: Changelog, https://github.com/queueforge/ClickMigrate/blob/main/CHANGELOG.md
12
+ Keywords: clickhouse,database,migration,migrations,sql,cli
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Database
22
+ Classifier: Topic :: Software Development :: Libraries
7
23
  Requires-Python: >=3.11
8
24
  Description-Content-Type: text/markdown
9
25
  License-File: LICENSE
@@ -16,6 +32,7 @@ Requires-Dist: pytest>=7.0.0; extra == "dev"
16
32
  Requires-Dist: black>=23.0.0; extra == "dev"
17
33
  Requires-Dist: ruff>=0.1.0; extra == "dev"
18
34
  Requires-Dist: mypy>=1.0.0; extra == "dev"
35
+ Requires-Dist: types-PyYAML>=6.0.12.20260518; extra == "dev"
19
36
  Requires-Dist: build>=1.0.0; extra == "dev"
20
37
  Requires-Dist: twine>=4.0.0; extra == "dev"
21
38
  Dynamic: license-file
@@ -26,6 +43,10 @@ A modern, simple, and reliable database migration framework for ClickHouse, insp
26
43
 
27
44
  ClickMigrate is designed to be lightweight and easy to understand. It avoids over-engineered abstractions, offering a straightforward CLI and a clean Python API for managing your ClickHouse schema evolutions.
28
45
 
46
+ ## About
47
+
48
+ **ClickMigrate** is an open-source project developed and maintained by **QueueForge**. It is designed to provide a modern, simple, and reliable migration framework for ClickHouse databases. Learn more about QueueForge at [**https://queueforge.dev**](https://queueforge.dev?utm_source=README&utm_campaign=ClickMigrate).
49
+
29
50
  ---
30
51
 
31
52
  ## Features
@@ -4,6 +4,10 @@ A modern, simple, and reliable database migration framework for ClickHouse, insp
4
4
 
5
5
  ClickMigrate is designed to be lightweight and easy to understand. It avoids over-engineered abstractions, offering a straightforward CLI and a clean Python API for managing your ClickHouse schema evolutions.
6
6
 
7
+ ## About
8
+
9
+ **ClickMigrate** is an open-source project developed and maintained by **QueueForge**. It is designed to provide a modern, simple, and reliable migration framework for ClickHouse databases. Learn more about QueueForge at [**https://queueforge.dev**](https://queueforge.dev?utm_source=README&utm_campaign=ClickMigrate).
10
+
7
11
  ---
8
12
 
9
13
  ## Features
@@ -0,0 +1,80 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "ClickMigrate"
7
+ version = "1.0.3"
8
+ description = "A modern, simple database migration framework for ClickHouse."
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ license = { text = "MIT" }
12
+
13
+ authors = [
14
+ { name = "Ivo Theis", email = "ivo.theis@queueforge.dev" }
15
+ ]
16
+
17
+ dependencies = [
18
+ "clickhouse-connect>=0.6.8",
19
+ "typer>=0.9.0",
20
+ "rich>=13.0.0",
21
+ "pyyaml>=6.0.1",
22
+ ]
23
+
24
+ keywords = [
25
+ "clickhouse",
26
+ "database",
27
+ "migration",
28
+ "migrations",
29
+ "sql",
30
+ "cli",
31
+ ]
32
+
33
+ classifiers = [
34
+ "Development Status :: 4 - Beta",
35
+ "Intended Audience :: Developers",
36
+ "License :: OSI Approved :: MIT License",
37
+ "Operating System :: OS Independent",
38
+ "Programming Language :: Python :: 3",
39
+ "Programming Language :: Python :: 3.11",
40
+ "Programming Language :: Python :: 3.12",
41
+ "Programming Language :: Python :: 3.13",
42
+ "Topic :: Database",
43
+ "Topic :: Software Development :: Libraries",
44
+ ]
45
+
46
+ [project.urls]
47
+ Homepage = "https://queueforge.dev"
48
+ Repository = "https://github.com/queueforge/ClickMigrate"
49
+ Issues = "https://github.com/queueforge/ClickMigrate/issues"
50
+ Releases = "https://github.com/queueforge/ClickMigrate/releases"
51
+ Changelog = "https://github.com/queueforge/ClickMigrate/blob/main/CHANGELOG.md"
52
+
53
+ [project.scripts]
54
+ clickmigrate = "clickmigrate.cli:app"
55
+
56
+ [project.optional-dependencies]
57
+ dev = [
58
+ "pytest>=7.0.0",
59
+ "black>=23.0.0",
60
+ "ruff>=0.1.0",
61
+ "mypy>=1.0.0",
62
+ "types-PyYAML>=6.0.12.20260518",
63
+ "build>=1.0.0",
64
+ "twine>=4.0.0"
65
+ ]
66
+
67
+ [tool.setuptools.packages.find]
68
+ where = ["src"]
69
+
70
+ [tool.black]
71
+ line-length = 88
72
+ target-version = ["py311"]
73
+
74
+ [tool.ruff]
75
+ line-length = 88
76
+ target-version = "py311"
77
+
78
+ [tool.mypy]
79
+ python_version = "3.11"
80
+ strict = true
@@ -1,9 +1,25 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ClickMigrate
3
- Version: 1.0.0
3
+ Version: 1.0.3
4
4
  Summary: A modern, simple database migration framework for ClickHouse.
5
5
  Author-email: Ivo Theis <ivo.theis@queueforge.dev>
6
6
  License: MIT
7
+ Project-URL: Homepage, https://queueforge.dev
8
+ Project-URL: Repository, https://github.com/queueforge/ClickMigrate
9
+ Project-URL: Issues, https://github.com/queueforge/ClickMigrate/issues
10
+ Project-URL: Releases, https://github.com/queueforge/ClickMigrate/releases
11
+ Project-URL: Changelog, https://github.com/queueforge/ClickMigrate/blob/main/CHANGELOG.md
12
+ Keywords: clickhouse,database,migration,migrations,sql,cli
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Database
22
+ Classifier: Topic :: Software Development :: Libraries
7
23
  Requires-Python: >=3.11
8
24
  Description-Content-Type: text/markdown
9
25
  License-File: LICENSE
@@ -16,6 +32,7 @@ Requires-Dist: pytest>=7.0.0; extra == "dev"
16
32
  Requires-Dist: black>=23.0.0; extra == "dev"
17
33
  Requires-Dist: ruff>=0.1.0; extra == "dev"
18
34
  Requires-Dist: mypy>=1.0.0; extra == "dev"
35
+ Requires-Dist: types-PyYAML>=6.0.12.20260518; extra == "dev"
19
36
  Requires-Dist: build>=1.0.0; extra == "dev"
20
37
  Requires-Dist: twine>=4.0.0; extra == "dev"
21
38
  Dynamic: license-file
@@ -26,6 +43,10 @@ A modern, simple, and reliable database migration framework for ClickHouse, insp
26
43
 
27
44
  ClickMigrate is designed to be lightweight and easy to understand. It avoids over-engineered abstractions, offering a straightforward CLI and a clean Python API for managing your ClickHouse schema evolutions.
28
45
 
46
+ ## About
47
+
48
+ **ClickMigrate** is an open-source project developed and maintained by **QueueForge**. It is designed to provide a modern, simple, and reliable migration framework for ClickHouse databases. Learn more about QueueForge at [**https://queueforge.dev**](https://queueforge.dev?utm_source=README&utm_campaign=ClickMigrate).
49
+
29
50
  ---
30
51
 
31
52
  ## Features
@@ -8,5 +8,6 @@ pytest>=7.0.0
8
8
  black>=23.0.0
9
9
  ruff>=0.1.0
10
10
  mypy>=1.0.0
11
+ types-PyYAML>=6.0.12.20260518
11
12
  build>=1.0.0
12
13
  twine>=4.0.0
@@ -3,4 +3,4 @@
3
3
  from clickmigrate.manager import MigrationManager
4
4
  from clickmigrate.config import Config
5
5
 
6
- __all__ = ["MigrationManager", "Config"]
6
+ __all__ = ["MigrationManager", "Config"]
@@ -3,4 +3,4 @@
3
3
  from clickmigrate.cli import app
4
4
 
5
5
  if __name__ == "__main__":
6
- app()
6
+ app()
@@ -2,7 +2,6 @@
2
2
 
3
3
  import typer
4
4
  from rich.console import Console
5
- from rich.table import Table
6
5
  from clickmigrate.config import load_config
7
6
  from clickmigrate.manager import MigrationManager
8
7
  from clickmigrate.exceptions import ClickMigrateError
@@ -10,6 +9,7 @@ from clickmigrate.exceptions import ClickMigrateError
10
9
  app = typer.Typer(help="ClickMigrate: A modern ClickHouse migration framework.")
11
10
  console = Console()
12
11
 
12
+
13
13
  def get_manager() -> MigrationManager:
14
14
  try:
15
15
  config = load_config()
@@ -18,49 +18,64 @@ def get_manager() -> MigrationManager:
18
18
  console.print(f"[bold red]Initialization Error:[/bold red] {e}")
19
19
  raise typer.Exit(code=1)
20
20
 
21
+
21
22
  @app.command()
22
23
  def init() -> None:
23
24
  """Initialize a new ClickMigrate environment."""
24
25
  config = load_config()
25
26
  import os
27
+
26
28
  os.makedirs(config.migration_directory, exist_ok=True)
27
- console.print(f"[green]Initialized ClickMigrate in ./{config.migration_directory}/[/green]")
29
+ console.print(
30
+ f"[green]Initialized ClickMigrate in ./{config.migration_directory}/[/green]"
31
+ )
32
+
28
33
 
29
34
  @app.command()
30
- def revision(message: str = typer.Option(..., "-m", "--message", help="Migration description")) -> None:
35
+ def revision(
36
+ message: str = typer.Option(..., "-m", "--message", help="Migration description")
37
+ ) -> None:
31
38
  """Create a new migration file."""
32
39
  manager = get_manager()
33
40
  filepath = manager.create_revision(message)
34
41
  console.print(f"[green]Created revision:[/green] {filepath}")
35
42
 
43
+
36
44
  @app.command()
37
- def migrate(dry_run: bool = typer.Option(False, "--dry-run", help="Simulate migration without applying")) -> None:
45
+ def migrate(
46
+ dry_run: bool = typer.Option(
47
+ False, "--dry-run", help="Simulate migration without applying"
48
+ )
49
+ ) -> None:
38
50
  """Apply all pending migrations."""
39
51
  console.print("\n[bold]ClickMigrate[/bold]\n")
40
52
  manager = get_manager()
41
-
53
+
42
54
  try:
43
55
  applied_count, pending_count = manager.status()
44
56
  if pending_count == 0:
45
57
  console.print("No pending migrations.\n")
46
58
  return
47
-
59
+
48
60
  console.print(f"Applying [yellow]{pending_count}[/yellow] migrations...\n")
49
-
61
+
50
62
  if dry_run:
51
- console.print("[yellow]DRY RUN: No changes will be made to the database.[/yellow]\n")
52
-
63
+ console.print(
64
+ "[yellow]DRY RUN: No changes will be made to the database.[/yellow]\n"
65
+ )
66
+
53
67
  applied, duration = manager.migrate(dry_run=dry_run)
54
-
68
+
55
69
  console.print("\n[bold green]SUCCESS[/bold green]\n")
56
70
  console.print(f"Applied migrations : {applied}")
57
71
  console.print(f"Pending migrations : {pending_count - applied}")
58
72
  console.print(f"Execution time : {duration:.2f} seconds\n")
59
-
73
+
60
74
  except ClickMigrateError as e:
61
75
  console.print(f"\n[bold red]FAILED[/bold red]\n{e}")
62
76
  raise typer.Exit(code=1)
63
77
 
78
+
64
79
  @app.command()
65
80
  def status() -> None:
66
81
  """Show current migration status."""
@@ -69,16 +84,20 @@ def status() -> None:
69
84
  console.print(f"Applied migrations : [green]{applied}[/green]")
70
85
  console.print(f"Pending migrations : [yellow]{pending}[/yellow]")
71
86
 
87
+
72
88
  @app.command()
73
89
  def validate() -> None:
74
90
  """Validate checksums of applied migrations against local files."""
75
91
  manager = get_manager()
76
92
  try:
77
93
  manager.validate()
78
- console.print("[green]All migrations are valid. No checksum mismatches.[/green]")
94
+ console.print(
95
+ "[green]All migrations are valid. No checksum mismatches.[/green]"
96
+ )
79
97
  except ClickMigrateError as e:
80
98
  console.print(f"[bold red]Validation Failed:[/bold red] {e}")
81
99
  raise typer.Exit(code=1)
82
100
 
101
+
83
102
  if __name__ == "__main__":
84
- app()
103
+ app()
@@ -6,11 +6,12 @@ import tomllib
6
6
  from dataclasses import dataclass
7
7
  from typing import Any, Dict
8
8
  import yaml
9
- from clickmigrate.exceptions import ConfigurationError
9
+
10
10
 
11
11
  @dataclass
12
12
  class Config:
13
13
  """Stores the ClickMigrate configuration."""
14
+
14
15
  host: str = "localhost"
15
16
  port: int = 8123
16
17
  database: str = "default"
@@ -19,6 +20,7 @@ class Config:
19
20
  migration_directory: str = "migrations"
20
21
  migration_table: str = "clickmigrate_history"
21
22
 
23
+
22
24
  def load_config() -> Config:
23
25
  """Loads configuration from environment variables or files."""
24
26
  config_data: Dict[str, Any] = {}
@@ -49,10 +51,10 @@ def load_config() -> Config:
49
51
  "CLICKMIGRATE_DIRECTORY": "migration_directory",
50
52
  "CLICKMIGRATE_TABLE": "migration_table",
51
53
  }
52
-
54
+
53
55
  for env_var, key in env_mapping.items():
54
56
  if env_var in os.environ:
55
57
  val = os.environ[env_var]
56
58
  config_data[key] = int(val) if key == "port" else val
57
59
 
58
- return Config(**config_data)
60
+ return Config(**config_data)
@@ -1,18 +1,34 @@
1
1
  """Database interaction layer."""
2
2
 
3
+ import re
4
+ from typing import Dict
5
+
3
6
  import clickhouse_connect
4
7
  from clickhouse_connect.driver.client import Client
5
- from typing import List, Dict, Any
8
+
6
9
  from clickmigrate.config import Config
7
10
  from clickmigrate.exceptions import MigrationError
8
11
 
12
+ _IDENTIFIER_RE = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*$")
13
+
14
+
9
15
  class Database:
10
16
  """Handles ClickHouse connections and queries."""
11
17
 
12
18
  def __init__(self, config: Config) -> None:
13
19
  self.config = config
20
+ self.config.migration_table = self._validate_identifier(
21
+ self.config.migration_table
22
+ )
14
23
  self.client: Client = self._connect()
15
24
 
25
+ @staticmethod
26
+ def _validate_identifier(identifier: str) -> str:
27
+ """Validates SQL identifiers (table names, etc.)."""
28
+ if not _IDENTIFIER_RE.fullmatch(identifier):
29
+ raise MigrationError(f"Invalid SQL identifier: {identifier}")
30
+ return identifier
31
+
16
32
  def _connect(self) -> Client:
17
33
  """Establishes a connection to ClickHouse."""
18
34
  try:
@@ -24,7 +40,7 @@ class Database:
24
40
  database=self.config.database,
25
41
  )
26
42
  except Exception as e:
27
- raise MigrationError(f"Failed to connect to ClickHouse: {e}")
43
+ raise MigrationError(f"Failed to connect to ClickHouse: {e}") from e
28
44
 
29
45
  def ensure_history_table(self) -> None:
30
46
  """Creates the migration history table if it doesn't exist."""
@@ -41,26 +57,47 @@ class Database:
41
57
 
42
58
  def get_applied_migrations(self) -> Dict[str, str]:
43
59
  """Retrieves applied migrations and their checksums.
44
-
60
+
45
61
  Returns:
46
62
  Dict mapping version to checksum.
47
63
  """
48
64
  self.ensure_history_table()
65
+
49
66
  query = f"SELECT version, checksum FROM {self.config.migration_table}"
50
67
  result = self.client.query(query)
68
+
51
69
  return {row[0]: row[1] for row in result.result_rows}
52
70
 
53
- def apply_migration(self, version: str, name: str, checksum: str, sql: str) -> None:
71
+ def apply_migration(
72
+ self,
73
+ version: str,
74
+ name: str,
75
+ checksum: str,
76
+ sql: str,
77
+ ) -> None:
54
78
  """Executes a migration script and records it in the history table."""
55
79
  try:
56
- # Execute the actual migration
80
+ # Execute the migration SQL
57
81
  self.client.command(sql)
58
-
59
- # Record in history
60
- record_query = f"""
61
- INSERT INTO {self.config.migration_table} (version, name, checksum)
62
- VALUES ('{version}', '{name}', '{checksum}')
82
+
83
+ # Record migration using parameterized query
84
+ query = f"""
85
+ INSERT INTO {self.config.migration_table}
86
+ (version, name, checksum)
87
+ VALUES
88
+ (%(version)s, %(name)s, %(checksum)s)
63
89
  """
64
- self.client.command(record_query)
90
+
91
+ self.client.command(
92
+ query,
93
+ parameters={
94
+ "version": version,
95
+ "name": name,
96
+ "checksum": checksum,
97
+ },
98
+ )
99
+
65
100
  except Exception as e:
66
- raise MigrationError(f"Failed to apply migration {version}_{name}: {e}")
101
+ raise MigrationError(
102
+ f"Failed to apply migration {version}_{name}: {e}"
103
+ ) from e
@@ -1,17 +1,25 @@
1
1
  """Custom exceptions for ClickMigrate."""
2
2
 
3
+
3
4
  class ClickMigrateError(Exception):
4
5
  """Base exception for all ClickMigrate errors."""
6
+
5
7
  pass
6
8
 
9
+
7
10
  class ConfigurationError(ClickMigrateError):
8
11
  """Raised when configuration is invalid or missing."""
12
+
9
13
  pass
10
14
 
15
+
11
16
  class MigrationError(ClickMigrateError):
12
17
  """Raised when a migration fails to apply."""
18
+
13
19
  pass
14
20
 
21
+
15
22
  class ChecksumError(ClickMigrateError):
16
23
  """Raised when an applied migration's checksum differs from the file."""
17
- pass
24
+
25
+ pass
@@ -1,23 +1,27 @@
1
1
  """Core migration management logic."""
2
2
 
3
3
  import os
4
+ import re
4
5
  import hashlib
5
6
  import time
6
7
  from typing import List, Tuple
7
8
  from dataclasses import dataclass
8
9
  from clickmigrate.config import Config
9
10
  from clickmigrate.database import Database
10
- from clickmigrate.exceptions import ChecksumError, ClickMigrateError
11
+ from clickmigrate.exceptions import ChecksumError
12
+
11
13
 
12
14
  @dataclass
13
15
  class Migration:
14
16
  """Represents a single SQL migration."""
17
+
15
18
  version: str
16
19
  name: str
17
20
  filepath: str
18
21
  content: str
19
22
  checksum: str
20
23
 
24
+
21
25
  class MigrationManager:
22
26
  """API for managing ClickHouse migrations."""
23
27
 
@@ -31,7 +35,7 @@ class MigrationManager:
31
35
 
32
36
  def _get_local_migrations(self) -> List[Migration]:
33
37
  """Discovers and parses local .sql migration files."""
34
- migrations = []
38
+ migrations: List[Migration] = []
35
39
  if not os.path.exists(self.config.migration_directory):
36
40
  return migrations
37
41
 
@@ -40,16 +44,16 @@ class MigrationManager:
40
44
  parts = filename.replace(".sql", "").split("_", 1)
41
45
  if len(parts) != 2:
42
46
  continue
43
-
47
+
44
48
  version, name = parts
45
49
  filepath = os.path.join(self.config.migration_directory, filename)
46
-
50
+
47
51
  with open(filepath, "r", encoding="utf-8") as f:
48
52
  content = f.read()
49
-
53
+
50
54
  checksum = self._calculate_checksum(content)
51
55
  migrations.append(Migration(version, name, filepath, content, checksum))
52
-
56
+
53
57
  return migrations
54
58
 
55
59
  def validate(self) -> None:
@@ -74,16 +78,16 @@ class MigrationManager:
74
78
 
75
79
  def migrate(self, dry_run: bool = False) -> Tuple[int, float]:
76
80
  """Applies all pending migrations.
77
-
81
+
78
82
  Returns:
79
83
  Tuple containing (number of migrations applied, execution time in seconds).
80
84
  """
81
85
  self.validate()
82
86
  applied_map = self.db.get_applied_migrations()
83
87
  local = self._get_local_migrations()
84
-
88
+
85
89
  pending = [m for m in local if m.version not in applied_map]
86
-
90
+
87
91
  if dry_run or not pending:
88
92
  return len(pending), 0.0
89
93
 
@@ -93,24 +97,24 @@ class MigrationManager:
93
97
  version=migration.version,
94
98
  name=migration.name,
95
99
  checksum=migration.checksum,
96
- sql=migration.content
100
+ sql=migration.content,
97
101
  )
98
-
102
+
99
103
  return len(pending), time.time() - start_time
100
104
 
101
105
  def create_revision(self, message: str) -> str:
102
106
  """Creates a new empty migration file."""
103
107
  os.makedirs(self.config.migration_directory, exist_ok=True)
104
108
  local = self._get_local_migrations()
105
-
109
+
106
110
  next_version = 1 if not local else int(local[-1].version) + 1
107
111
  version_str = f"{next_version:03d}"
108
-
109
- safe_name = message.lower().replace(" ", "_").replace("-", "_")
112
+
113
+ safe_name = re.sub(r"[^a-zA-Z0-9_]", "", message.lower()).replace(" ", "_").replace("-", "_")
110
114
  filename = f"{version_str}_{safe_name}.sql"
111
115
  filepath = os.path.join(self.config.migration_directory, filename)
112
-
116
+
113
117
  with open(filepath, "w", encoding="utf-8") as f:
114
118
  f.write(f"-- Migration: {message}\n-- Version: {version_str}\n\n")
115
-
116
- return filepath
119
+
120
+ return filepath
@@ -1,46 +0,0 @@
1
- [build-system]
2
- requires = ["setuptools>=61.0.0", "wheel"]
3
- build-backend = "setuptools.build_meta"
4
-
5
- [project]
6
- name = "ClickMigrate"
7
- version = "1.0.0"
8
- description = "A modern, simple database migration framework for ClickHouse."
9
- readme = "README.md"
10
- requires-python = ">=3.11"
11
- license = { text = "MIT" }
12
- authors = [{ name = "Ivo Theis", email = "ivo.theis@queueforge.dev" }]
13
- dependencies = [
14
- "clickhouse-connect>=0.6.8",
15
- "typer>=0.9.0",
16
- "rich>=13.0.0",
17
- "pyyaml>=6.0.1", # Added for YAML config support
18
- ]
19
-
20
- [project.scripts]
21
- clickmigrate = "clickmigrate.cli:app"
22
-
23
- [project.optional-dependencies]
24
- dev = [
25
- "pytest>=7.0.0",
26
- "black>=23.0.0",
27
- "ruff>=0.1.0",
28
- "mypy>=1.0.0",
29
- "build>=1.0.0",
30
- "twine>=4.0.0"
31
- ]
32
-
33
- [tool.setuptools.packages.find]
34
- where = ["src"]
35
-
36
- [tool.black]
37
- line-length = 88
38
- target-version = ["py311"]
39
-
40
- [tool.ruff]
41
- line-length = 88
42
- target-version = "py311"
43
-
44
- [tool.mypy]
45
- python_version = "3.11"
46
- strict = true
File without changes
File without changes