coderag-ai 0.3.2__tar.gz → 0.3.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.
Files changed (140) hide show
  1. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/PKG-INFO +1 -1
  2. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/pyproject.toml +1 -1
  3. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/cli/main.py +26 -11
  4. coderag_ai-0.3.3/src/coderag/db/migrate.py +51 -0
  5. coderag_ai-0.3.3/tests/test_phase15_migrate.py +43 -0
  6. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/.env.example +0 -0
  7. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/.gitignore +0 -0
  8. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/CODE_OF_CONDUCT.md +0 -0
  9. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/CONTRIBUTING.md +0 -0
  10. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/Dockerfile +0 -0
  11. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/LICENSE +0 -0
  12. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/Makefile +0 -0
  13. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/NOTICE +0 -0
  14. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/README.md +0 -0
  15. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/SECURITY.md +0 -0
  16. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/alembic.ini +0 -0
  17. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/docker/entrypoint.sh +0 -0
  18. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/docker-compose.yml +0 -0
  19. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/docs/adding-language.md +0 -0
  20. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/docs/adr/001-postgres-pgvector.md +0 -0
  21. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/docs/adr/002-structural-chunks.md +0 -0
  22. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/docs/adr/003-hybrid-search.md +0 -0
  23. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/docs/adr/004-tree-sitter.md +0 -0
  24. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/docs/adr/005-llm-provider-independence.md +0 -0
  25. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/docs/adr/006-repository-authorization.md +0 -0
  26. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/docs/adr/007-token-budgeting.md +0 -0
  27. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/docs/architecture.md +0 -0
  28. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/docs/claude-code.md +0 -0
  29. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/docs/evaluation.md +0 -0
  30. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/docs/install-macos.md +0 -0
  31. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/docs/licenses.md +0 -0
  32. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/docs/llm-providers.md +0 -0
  33. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/docs/retrieval.md +0 -0
  34. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/docs/security.md +0 -0
  35. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/docs/vscode-extension.md +0 -0
  36. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/examples/demo-repository/payments/__init__.py +0 -0
  37. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/examples/demo-repository/payments/api.py +0 -0
  38. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/examples/demo-repository/payments/auth.py +0 -0
  39. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/examples/demo-repository/payments/checkout_service.py +0 -0
  40. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/examples/demo-repository/payments/models.py +0 -0
  41. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/examples/demo-repository/payments/payment_repository.py +0 -0
  42. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/examples/demo-repository/payments/payment_service.py +0 -0
  43. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/examples/demo-repository/payments/retry_policy.py +0 -0
  44. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/examples/demo-repository/payments/users.py +0 -0
  45. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/examples/demo-repository/tests/test_payment_service.py +0 -0
  46. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/examples/demo-repository/tests/test_retry_policy.py +0 -0
  47. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/examples/eval/eval_dataset.json +0 -0
  48. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/scripts/gen_initial_migration.py +0 -0
  49. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/__init__.py +0 -0
  50. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/analyzers/__init__.py +0 -0
  51. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/analyzers/base.py +0 -0
  52. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/analyzers/flake8_adapter.py +0 -0
  53. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/analyzers/pylint_adapter.py +0 -0
  54. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/analyzers/workflow.py +0 -0
  55. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/api/__init__.py +0 -0
  56. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/api/app.py +0 -0
  57. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/api/dashboard.py +0 -0
  58. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/api/schemas.py +0 -0
  59. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/cli/__init__.py +0 -0
  60. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/context/__init__.py +0 -0
  61. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/context/builder.py +0 -0
  62. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/context/package.py +0 -0
  63. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/core/__init__.py +0 -0
  64. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/core/config.py +0 -0
  65. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/core/logging.py +0 -0
  66. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/core/text.py +0 -0
  67. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/core/tokens.py +0 -0
  68. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/db/__init__.py +0 -0
  69. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/db/base.py +0 -0
  70. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/db/migrations/env.py +0 -0
  71. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/db/migrations/script.py.mako +0 -0
  72. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/db/migrations/versions/.gitkeep +0 -0
  73. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/db/migrations/versions/9a973932b94d_measure_baseline_tokens.py +0 -0
  74. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/db/migrations/versions/c8a40feeda41_initial_schema.py +0 -0
  75. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/db/models.py +0 -0
  76. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/embeddings/__init__.py +0 -0
  77. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/embeddings/base.py +0 -0
  78. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/embeddings/hashing.py +0 -0
  79. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/embeddings/pipeline.py +0 -0
  80. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/embeddings/registry.py +0 -0
  81. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/embeddings/sentence_transformer.py +0 -0
  82. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/evaluation/__init__.py +0 -0
  83. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/evaluation/datasets.py +0 -0
  84. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/evaluation/harness.py +0 -0
  85. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/git/__init__.py +0 -0
  86. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/git/repo.py +0 -0
  87. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/indexing/__init__.py +0 -0
  88. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/indexing/ignore.py +0 -0
  89. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/indexing/indexer.py +0 -0
  90. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/llm/__init__.py +0 -0
  91. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/llm/anthropic.py +0 -0
  92. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/llm/base.py +0 -0
  93. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/llm/null.py +0 -0
  94. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/llm/registry.py +0 -0
  95. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/localdb.py +0 -0
  96. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/mcp_server.py +0 -0
  97. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/parsing/__init__.py +0 -0
  98. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/parsing/base.py +0 -0
  99. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/parsing/python.py +0 -0
  100. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/parsing/registry.py +0 -0
  101. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/retrieval/__init__.py +0 -0
  102. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/retrieval/base.py +0 -0
  103. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/retrieval/engine.py +0 -0
  104. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/retrieval/fusion.py +0 -0
  105. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/retrieval/graph.py +0 -0
  106. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/retrieval/lexical.py +0 -0
  107. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/retrieval/reranking.py +0 -0
  108. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/retrieval/semantic.py +0 -0
  109. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/retrieval/symbol.py +0 -0
  110. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/security/__init__.py +0 -0
  111. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/security/authz.py +0 -0
  112. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/security/secrets.py +0 -0
  113. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/service.py +0 -0
  114. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/src/coderag/telemetry.py +0 -0
  115. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/tests/__init__.py +0 -0
  116. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/tests/conftest.py +0 -0
  117. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/tests/test_phase0_db.py +0 -0
  118. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/tests/test_phase0_migrations.py +0 -0
  119. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/tests/test_phase10_analyzers.py +0 -0
  120. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/tests/test_phase11_dashboard.py +0 -0
  121. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/tests/test_phase12_mcp.py +0 -0
  122. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/tests/test_phase13_measure.py +0 -0
  123. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/tests/test_phase14_localdb.py +0 -0
  124. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/tests/test_phase1_indexer.py +0 -0
  125. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/tests/test_phase1_parser.py +0 -0
  126. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/tests/test_phase2_cli.py +0 -0
  127. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/tests/test_phase2_retrieval.py +0 -0
  128. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/tests/test_phase3_embeddings.py +0 -0
  129. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/tests/test_phase3_semantic.py +0 -0
  130. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/tests/test_phase3_sentence_transformer.py +0 -0
  131. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/tests/test_phase4_graph.py +0 -0
  132. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/tests/test_phase5_context_db.py +0 -0
  133. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/tests/test_phase5_context_unit.py +0 -0
  134. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/tests/test_phase6_api.py +0 -0
  135. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/tests/test_phase6_ask.py +0 -0
  136. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/tests/test_phase6_llm.py +0 -0
  137. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/tests/test_phase7_eval.py +0 -0
  138. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/tests/test_phase8_incremental.py +0 -0
  139. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/tests/test_phase9_rerank.py +0 -0
  140. {coderag_ai-0.3.2 → coderag_ai-0.3.3}/tests/test_security_secrets.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: coderag-ai
3
- Version: 0.3.2
3
+ Version: 0.3.3
4
4
  Summary: Token-efficient, structure-aware Code Intelligence + RAG for private source repositories.
5
5
  Project-URL: Homepage, https://github.com/galacticsurfer/coderag
6
6
  Project-URL: Repository, https://github.com/galacticsurfer/coderag
@@ -7,7 +7,7 @@ build-backend = "hatchling.build"
7
7
  # command are both `coderag`. Plain "coderag" is taken on PyPI by an unrelated project,
8
8
  # and "code-rag" is rejected as too similar (PyPI collapses -/_/. when comparing names).
9
9
  name = "coderag-ai"
10
- version = "0.3.2"
10
+ version = "0.3.3"
11
11
  description = "Token-efficient, structure-aware Code Intelligence + RAG for private source repositories."
12
12
  readme = "README.md"
13
13
  requires-python = ">=3.12"
@@ -45,19 +45,17 @@ def localdb_start(
45
45
  raise typer.Exit(1) from None
46
46
  console.print(f"[green]PostgreSQL running.[/]\n\n export CODERAG_DATABASE_URL='{url}'\n")
47
47
  if migrate:
48
- import os
49
- import subprocess
48
+ # Uses Alembic's Python API against the packaged migrations — an installed
49
+ # wheel has neither the `alembic` console script on PATH nor alembic.ini.
50
+ from coderag.db.migrate import upgrade_to_head
50
51
 
51
- env = dict(os.environ, CODERAG_DATABASE_URL=url)
52
- r = subprocess.run(["alembic", "upgrade", "head"], env=env,
53
- capture_output=True, text=True)
54
- if r.returncode == 0:
52
+ try:
53
+ upgrade_to_head(url)
55
54
  console.print("[green]Migrations applied.[/]")
56
- else:
57
- console.print(
58
- "[yellow]Could not run migrations automatically "
59
- "(run `alembic upgrade head` from a repo checkout).[/]"
60
- )
55
+ except Exception as exc:
56
+ console.print(f"[red]Migrations failed:[/] {exc}")
57
+ console.print("[yellow]Retry with:[/] coderag migrate")
58
+ raise typer.Exit(1) from None
61
59
 
62
60
 
63
61
  @localdb_app.command("stop")
@@ -82,6 +80,23 @@ def localdb_status(
82
80
  console.print(f"[green]running (pid {pid})[/]" if pid else "[yellow]not running[/]")
83
81
 
84
82
 
83
+ @app.command()
84
+ def migrate(
85
+ database_url: str | None = typer.Option(
86
+ None, "--database-url", help="Defaults to CODERAG_DATABASE_URL."
87
+ ),
88
+ ) -> None:
89
+ """Create/upgrade the database schema (works from a pip/pipx install)."""
90
+ from coderag.db.migrate import current_revision, upgrade_to_head
91
+
92
+ try:
93
+ url = upgrade_to_head(database_url)
94
+ except Exception as exc:
95
+ console.print(f"[red]Migration failed:[/] {exc}")
96
+ raise typer.Exit(1) from None
97
+ console.print(f"[green]Database is up to date[/] (revision {current_revision(url)}).")
98
+
99
+
85
100
  @app.command()
86
101
  def index(
87
102
  path: str = typer.Argument(..., help="Path to the repository to index."),
@@ -0,0 +1,51 @@
1
+ """Run database migrations from an installed package.
2
+
3
+ The `alembic` console script and `alembic.ini` are only available in a source
4
+ checkout. When CodeRAG is installed as a wheel (pipx/pip), neither is on PATH, so
5
+ migrations are driven through Alembic's Python API with the migration directory
6
+ resolved from the installed package.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from pathlib import Path
12
+
13
+ from coderag.core.config import get_settings
14
+
15
+
16
+ def migrations_path() -> Path:
17
+ """Filesystem path of the packaged Alembic migration directory."""
18
+ return Path(__file__).resolve().parent / "migrations"
19
+
20
+
21
+ def _config(url: str):
22
+ from alembic.config import Config
23
+
24
+ cfg = Config()
25
+ cfg.set_main_option("script_location", str(migrations_path()))
26
+ # Escape '%' so ConfigParser interpolation doesn't choke on URL-encoded values.
27
+ cfg.set_main_option("sqlalchemy.url", url.replace("%", "%%"))
28
+ return cfg
29
+
30
+
31
+ def upgrade_to_head(url: str | None = None) -> str:
32
+ """Upgrade the database to the latest revision. Returns the URL used."""
33
+ from alembic import command
34
+
35
+ target = url or get_settings().database_url
36
+ command.upgrade(_config(target), "head")
37
+ return target
38
+
39
+
40
+ def current_revision(url: str | None = None) -> str | None:
41
+ """Return the revision the database is currently stamped at (None if empty)."""
42
+ from alembic.migration import MigrationContext
43
+ from sqlalchemy import create_engine
44
+
45
+ target = url or get_settings().database_url
46
+ engine = create_engine(target)
47
+ try:
48
+ with engine.connect() as conn:
49
+ return MigrationContext.configure(conn).get_current_revision()
50
+ finally:
51
+ engine.dispose()
@@ -0,0 +1,43 @@
1
+ """Migrations must work from an installed wheel (no alembic.ini, no CLI on PATH)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import uuid
6
+
7
+ import pytest
8
+ from sqlalchemy import create_engine, text
9
+
10
+ from coderag.db.migrate import current_revision, migrations_path, upgrade_to_head
11
+
12
+ pytestmark = pytest.mark.db
13
+
14
+
15
+ def test_migrations_are_packaged():
16
+ """The migration scripts ship inside the package, not just the source tree."""
17
+ path = migrations_path()
18
+ assert (path / "env.py").is_file()
19
+ versions = list((path / "versions").glob("*.py"))
20
+ assert versions, "no migration revisions packaged"
21
+
22
+
23
+ def test_upgrade_to_head_without_alembic_ini(pg_uri: str):
24
+ """Regression: `localdb start` used to shell out to an `alembic` binary that
25
+ isn't on PATH in a pipx install. Migrations now run via the Python API."""
26
+ dbname = "migapi_" + uuid.uuid4().hex[:8]
27
+ admin = create_engine(pg_uri, isolation_level="AUTOCOMMIT")
28
+ with admin.connect() as conn:
29
+ conn.execute(text(f'CREATE DATABASE "{dbname}"'))
30
+ admin.dispose()
31
+
32
+ url = pg_uri.replace("/postgres?", f"/{dbname}?")
33
+ assert current_revision(url) is None # fresh database
34
+ upgrade_to_head(url)
35
+ assert current_revision(url) is not None # stamped after upgrade
36
+
37
+ eng = create_engine(url)
38
+ with eng.connect() as conn:
39
+ tables = set(conn.execute(text(
40
+ "SELECT table_name FROM information_schema.tables WHERE table_schema='public'"
41
+ )).scalars())
42
+ eng.dispose()
43
+ assert {"repositories", "symbols", "symbol_embeddings"} <= tables
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes