clickhouse-charon 1.0.2__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.
- clickhouse_charon-1.0.2/.github/PULL_REQUEST_TEMPLATE.md +16 -0
- clickhouse_charon-1.0.2/.github/workflows/ci.yml +40 -0
- clickhouse_charon-1.0.2/.github/workflows/release.yml +45 -0
- clickhouse_charon-1.0.2/.gitignore +18 -0
- clickhouse_charon-1.0.2/.python-version +1 -0
- clickhouse_charon-1.0.2/CHANGELOG.md +17 -0
- clickhouse_charon-1.0.2/LICENSE +21 -0
- clickhouse_charon-1.0.2/Makefile +24 -0
- clickhouse_charon-1.0.2/PKG-INFO +303 -0
- clickhouse_charon-1.0.2/README.md +276 -0
- clickhouse_charon-1.0.2/charon/__init__.py +3 -0
- clickhouse_charon-1.0.2/charon/cli/__init__.py +0 -0
- clickhouse_charon-1.0.2/charon/cli/app.py +207 -0
- clickhouse_charon-1.0.2/charon/cli/config_cmd.py +184 -0
- clickhouse_charon-1.0.2/charon/cli/copy_cmd.py +173 -0
- clickhouse_charon-1.0.2/charon/cli/diff_cmd.py +167 -0
- clickhouse_charon-1.0.2/charon/cli/list_cmd.py +72 -0
- clickhouse_charon-1.0.2/charon/cli/status_cmd.py +96 -0
- clickhouse_charon-1.0.2/charon/client.py +164 -0
- clickhouse_charon-1.0.2/charon/config.py +98 -0
- clickhouse_charon-1.0.2/charon/copy.py +310 -0
- clickhouse_charon-1.0.2/charon/ddl.py +144 -0
- clickhouse_charon-1.0.2/charon/diff.py +104 -0
- clickhouse_charon-1.0.2/charon/web/__init__.py +0 -0
- clickhouse_charon-1.0.2/charon/web/app.py +344 -0
- clickhouse_charon-1.0.2/charon/web/jobs.py +191 -0
- clickhouse_charon-1.0.2/charon/web/models.py +61 -0
- clickhouse_charon-1.0.2/charon/web/templates/index.html +630 -0
- clickhouse_charon-1.0.2/docs/cli.md +165 -0
- clickhouse_charon-1.0.2/docs/configuration.md +132 -0
- clickhouse_charon-1.0.2/docs/index.md +32 -0
- clickhouse_charon-1.0.2/docs/web.md +92 -0
- clickhouse_charon-1.0.2/mkdocs.yml +38 -0
- clickhouse_charon-1.0.2/pyproject.toml +65 -0
- clickhouse_charon-1.0.2/tests/__init__.py +0 -0
- clickhouse_charon-1.0.2/tests/conftest.py +64 -0
- clickhouse_charon-1.0.2/tests/test_client.py +121 -0
- clickhouse_charon-1.0.2/tests/test_config.py +73 -0
- clickhouse_charon-1.0.2/tests/test_copy.py +132 -0
- clickhouse_charon-1.0.2/tests/test_ddl.py +80 -0
- clickhouse_charon-1.0.2/tests/test_diff.py +68 -0
- clickhouse_charon-1.0.2/tests/test_web.py +144 -0
- clickhouse_charon-1.0.2/uv.lock +1429 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
## Summary
|
|
2
|
+
|
|
3
|
+
<!-- Describe what this PR does and why -->
|
|
4
|
+
|
|
5
|
+
## Changes
|
|
6
|
+
|
|
7
|
+
<!-- List the main changes -->
|
|
8
|
+
|
|
9
|
+
## Checklist
|
|
10
|
+
|
|
11
|
+
- [ ] Tests added or updated for changed behaviour
|
|
12
|
+
- [ ] Documentation updated (README / docs/) if needed
|
|
13
|
+
- [ ] Commit message follows Conventional Commits (`feat:`, `fix:`, `chore:`, `docs:`, `ci:`)
|
|
14
|
+
- [ ] `make lint` passes
|
|
15
|
+
- [ ] `make typecheck` passes
|
|
16
|
+
- [ ] `make test` passes
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: ["**"]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
matrix:
|
|
14
|
+
python-version: ["3.11", "3.12"]
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- name: Set up Python
|
|
20
|
+
uses: actions/setup-python@v5
|
|
21
|
+
with:
|
|
22
|
+
python-version: ${{ matrix.python-version }}
|
|
23
|
+
|
|
24
|
+
- name: Set up uv
|
|
25
|
+
uses: astral-sh/setup-uv@v6
|
|
26
|
+
|
|
27
|
+
- name: Install dependencies
|
|
28
|
+
run: uv sync --all-extras
|
|
29
|
+
|
|
30
|
+
- name: Lint (ruff check)
|
|
31
|
+
run: uv run ruff check .
|
|
32
|
+
|
|
33
|
+
- name: Format check (ruff format)
|
|
34
|
+
run: uv run ruff format --check .
|
|
35
|
+
|
|
36
|
+
- name: Type check (mypy)
|
|
37
|
+
run: uv run mypy charon
|
|
38
|
+
|
|
39
|
+
- name: Run tests
|
|
40
|
+
run: uv run pytest --tb=short
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
release:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
concurrency: release
|
|
11
|
+
permissions:
|
|
12
|
+
id-token: write
|
|
13
|
+
contents: write
|
|
14
|
+
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
with:
|
|
18
|
+
fetch-depth: 0
|
|
19
|
+
token: ${{ secrets.GH_TOKEN }}
|
|
20
|
+
|
|
21
|
+
- name: Set up Python
|
|
22
|
+
uses: actions/setup-python@v5
|
|
23
|
+
with:
|
|
24
|
+
python-version: "3.12"
|
|
25
|
+
|
|
26
|
+
- name: Set up uv
|
|
27
|
+
uses: astral-sh/setup-uv@v6
|
|
28
|
+
|
|
29
|
+
- name: Install dependencies
|
|
30
|
+
run: uv sync --all-extras
|
|
31
|
+
|
|
32
|
+
- name: Python Semantic Release — version bump
|
|
33
|
+
env:
|
|
34
|
+
GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
|
35
|
+
run: uv run python-semantic-release version
|
|
36
|
+
|
|
37
|
+
- name: Python Semantic Release — publish GitHub Release
|
|
38
|
+
env:
|
|
39
|
+
GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
|
40
|
+
run: uv run python-semantic-release publish
|
|
41
|
+
|
|
42
|
+
- name: Publish to PyPI
|
|
43
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
44
|
+
with:
|
|
45
|
+
skip-existing: true
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.1.0] - 2026-05-29
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- Initial release
|
|
14
|
+
- CLI commands: `config`, `status`, `list`, `diff`, `copy`, `web`
|
|
15
|
+
- Web dashboard with diff view, live copy progress (SSE), job history, config editor
|
|
16
|
+
- Partition-aware copy via `INSERT INTO FUNCTION remote()`
|
|
17
|
+
- Conventional Commits + GitHub Actions CI/CD pipeline
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Yahor Yakubovich
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
.PHONY: install lint typecheck test test-cov build clean
|
|
2
|
+
|
|
3
|
+
install:
|
|
4
|
+
uv sync --all-extras
|
|
5
|
+
|
|
6
|
+
lint:
|
|
7
|
+
uv run ruff check .
|
|
8
|
+
uv run ruff format --check .
|
|
9
|
+
|
|
10
|
+
typecheck:
|
|
11
|
+
uv run mypy chcopy
|
|
12
|
+
|
|
13
|
+
test:
|
|
14
|
+
uv run pytest --tb=short
|
|
15
|
+
|
|
16
|
+
test-cov:
|
|
17
|
+
uv run pytest --tb=short --cov=chcopy --cov-report=term-missing
|
|
18
|
+
|
|
19
|
+
build:
|
|
20
|
+
uv build
|
|
21
|
+
|
|
22
|
+
clean:
|
|
23
|
+
rm -rf dist/ .venv/ *.egg-info/ .mypy_cache/ .ruff_cache/ .pytest_cache/
|
|
24
|
+
find . -type d -name __pycache__ -exec rm -rf {} +
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: clickhouse-charon
|
|
3
|
+
Version: 1.0.2
|
|
4
|
+
Summary: CHaron — ClickHouse migration tool with CLI + web dashboard
|
|
5
|
+
License: MIT
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Requires-Python: >=3.11
|
|
8
|
+
Requires-Dist: fastapi>=0.111
|
|
9
|
+
Requires-Dist: jinja2>=3.1
|
|
10
|
+
Requires-Dist: pydantic>=2
|
|
11
|
+
Requires-Dist: python-multipart>=0.0.9
|
|
12
|
+
Requires-Dist: pyyaml>=6
|
|
13
|
+
Requires-Dist: requests>=2.32
|
|
14
|
+
Requires-Dist: rich>=13
|
|
15
|
+
Requires-Dist: typer>=0.12
|
|
16
|
+
Requires-Dist: uvicorn[standard]>=0.29
|
|
17
|
+
Provides-Extra: dev
|
|
18
|
+
Requires-Dist: httpx>=0.27; extra == 'dev'
|
|
19
|
+
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
20
|
+
Requires-Dist: pytest-mock>=3; extra == 'dev'
|
|
21
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
22
|
+
Requires-Dist: python-semantic-release>=9; extra == 'dev'
|
|
23
|
+
Requires-Dist: ruff>=0.4; extra == 'dev'
|
|
24
|
+
Requires-Dist: types-pyyaml>=6; extra == 'dev'
|
|
25
|
+
Requires-Dist: types-requests>=2; extra == 'dev'
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
# charon
|
|
29
|
+
|
|
30
|
+
[](https://pypi.org/project/clickhouse-charon/)
|
|
31
|
+
[](https://github.com/yahoryakubovich/charon/actions/workflows/ci.yml)
|
|
32
|
+
[](https://opensource.org/licenses/MIT)
|
|
33
|
+
|
|
34
|
+
**charon** is a ClickHouse database copier with a CLI and web dashboard. It copies tables between ClickHouse instances partition-by-partition, shows a live diff, and tracks job history.
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Features
|
|
39
|
+
|
|
40
|
+
- Partition-aware copy using `INSERT INTO FUNCTION remote()` — no intermediate files
|
|
41
|
+
- Schema DDL sync: tables, views, materialized views, dictionaries (optional)
|
|
42
|
+
- Live diff: compare source vs. destination row counts per partition
|
|
43
|
+
- Web dashboard with SSE live log streaming
|
|
44
|
+
- Job history persisted to SQLite
|
|
45
|
+
- Configurable retry with exponential backoff
|
|
46
|
+
- Multiple named profiles in `~/.charon/config.yaml`
|
|
47
|
+
- Replicated engine → plain engine conversion option
|
|
48
|
+
- Dry-run mode
|
|
49
|
+
- `ON CLUSTER` clause stripping for single-node destinations
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Installation
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
# Using pipx (recommended for end-users)
|
|
57
|
+
pipx install clickhouse-charon
|
|
58
|
+
|
|
59
|
+
# Using uv tool
|
|
60
|
+
uv tool install clickhouse-charon
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Quick start
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
# Initialize a profile interactively
|
|
69
|
+
charon config init
|
|
70
|
+
|
|
71
|
+
# Check connectivity
|
|
72
|
+
charon status
|
|
73
|
+
|
|
74
|
+
# List tables on the source
|
|
75
|
+
charon list
|
|
76
|
+
|
|
77
|
+
# Show row-count diff between src and dst
|
|
78
|
+
charon diff
|
|
79
|
+
|
|
80
|
+
# Copy everything
|
|
81
|
+
charon copy
|
|
82
|
+
|
|
83
|
+
# Copy a single table
|
|
84
|
+
charon copy --table my_table
|
|
85
|
+
|
|
86
|
+
# Dry run
|
|
87
|
+
charon copy --dry-run
|
|
88
|
+
|
|
89
|
+
# Start the web dashboard
|
|
90
|
+
charon web
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## CLI Reference
|
|
96
|
+
|
|
97
|
+
### `charon config init`
|
|
98
|
+
Interactive wizard that creates or updates a named profile.
|
|
99
|
+
|
|
100
|
+
### `charon config show [--profile NAME]`
|
|
101
|
+
Print the current profile (password masked).
|
|
102
|
+
|
|
103
|
+
### `charon config list`
|
|
104
|
+
List all profile names.
|
|
105
|
+
|
|
106
|
+
### `charon config delete PROFILE`
|
|
107
|
+
Remove a profile.
|
|
108
|
+
|
|
109
|
+
### `charon status [--profile NAME]`
|
|
110
|
+
Ping source and destination; show table count, total rows, total bytes.
|
|
111
|
+
|
|
112
|
+
### `charon list [--src|--dst] [--filter REGEX]`
|
|
113
|
+
List tables on source or destination with engine, row count, and byte size.
|
|
114
|
+
|
|
115
|
+
### `charon diff [--table TABLE] [--json]`
|
|
116
|
+
Compare source and destination. Without `--table`: table-level summary. With `--table`: partition-level detail.
|
|
117
|
+
|
|
118
|
+
### `charon copy [OPTIONS]`
|
|
119
|
+
|
|
120
|
+
| Option | Default | Description |
|
|
121
|
+
|---|---|---|
|
|
122
|
+
| `--table TABLE` | — | Copy a single table |
|
|
123
|
+
| `--tables T1,T2` | — | Copy specific tables |
|
|
124
|
+
| `--dry-run` | false | Show what would be copied |
|
|
125
|
+
| `--no-partitions` | false | Copy whole table at once |
|
|
126
|
+
| `--profile NAME` | default | Profile to use |
|
|
127
|
+
|
|
128
|
+
### `charon web [OPTIONS]`
|
|
129
|
+
|
|
130
|
+
| Option | Default | Description |
|
|
131
|
+
|---|---|---|
|
|
132
|
+
| `--host` | 127.0.0.1 | Bind address |
|
|
133
|
+
| `--port` | 8765 | Port |
|
|
134
|
+
| `--reload` | false | Auto-reload (dev mode) |
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## Configuration reference
|
|
139
|
+
|
|
140
|
+
Config file: `~/.charon/config.yaml` (permissions: 0600)
|
|
141
|
+
|
|
142
|
+
```yaml
|
|
143
|
+
default_profile: prod
|
|
144
|
+
|
|
145
|
+
profiles:
|
|
146
|
+
prod:
|
|
147
|
+
src:
|
|
148
|
+
host: http://clickhouse-src:8123
|
|
149
|
+
user: default
|
|
150
|
+
password: secret
|
|
151
|
+
database: analytics
|
|
152
|
+
timeout: 3600
|
|
153
|
+
dst:
|
|
154
|
+
host: http://clickhouse-dst:8123
|
|
155
|
+
user: default
|
|
156
|
+
password: secret
|
|
157
|
+
database: analytics
|
|
158
|
+
tcp_hostport: clickhouse-dst:9000 # required for remote() INSERT
|
|
159
|
+
copy_by_partitions: true
|
|
160
|
+
copy_views: false
|
|
161
|
+
copy_kafka_tables: false
|
|
162
|
+
copy_dictionaries: false
|
|
163
|
+
convert_replicated_to_merge: false
|
|
164
|
+
skip_tables:
|
|
165
|
+
- huge_log_table
|
|
166
|
+
retry_count: 3
|
|
167
|
+
retry_sleep: 2.0
|
|
168
|
+
retry_max_sleep: 30.0
|
|
169
|
+
insert_settings:
|
|
170
|
+
max_partitions_per_insert_block: 1000
|
|
171
|
+
max_insert_block_size: 1048576
|
|
172
|
+
max_threads: 8
|
|
173
|
+
send_logs_level: warning
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Field descriptions
|
|
177
|
+
|
|
178
|
+
| Field | Type | Default | Description |
|
|
179
|
+
|---|---|---|---|
|
|
180
|
+
| `src.host` | str | required | HTTP URL of source CH (auto-prefixed with `http://`) |
|
|
181
|
+
| `src.user` | str | `default` | Username |
|
|
182
|
+
| `src.password` | str | `""` | Password (never logged) |
|
|
183
|
+
| `src.database` | str | required | Source database name |
|
|
184
|
+
| `src.timeout` | int | 3600 | Request timeout in seconds |
|
|
185
|
+
| `dst.tcp_hostport` | str | None | `host:port` for TCP — required by `remote()` INSERT |
|
|
186
|
+
| `copy_by_partitions` | bool | true | Copy partition-by-partition |
|
|
187
|
+
| `copy_views` | bool | false | Also copy VIEW tables |
|
|
188
|
+
| `copy_kafka_tables` | bool | false | Also copy Kafka engine tables |
|
|
189
|
+
| `copy_dictionaries` | bool | false | Also copy Dictionary tables |
|
|
190
|
+
| `convert_replicated_to_merge` | bool | false | Strip Replicated* prefix in DDL |
|
|
191
|
+
| `skip_tables` | list[str] | `[]` | Tables to skip |
|
|
192
|
+
| `retry_count` | int | 3 | Max retry attempts |
|
|
193
|
+
| `retry_sleep` | float | 2.0 | Base sleep between retries (seconds) |
|
|
194
|
+
| `retry_max_sleep` | float | 30.0 | Cap for exponential backoff |
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## Web dashboard
|
|
199
|
+
|
|
200
|
+
Open [http://localhost:8765](http://localhost:8765) after running `charon web`.
|
|
201
|
+
|
|
202
|
+
Features:
|
|
203
|
+
- Live status cards for src and dst
|
|
204
|
+
- Table comparison grid with color-coded diff
|
|
205
|
+
- One-click copy (all or selected tables)
|
|
206
|
+
- SSE log stream for running jobs
|
|
207
|
+
- Job history (last 20 jobs)
|
|
208
|
+
- Profile config editor
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
## Development setup
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
git clone https://github.com/your-org/charon
|
|
216
|
+
cd charon
|
|
217
|
+
uv sync --all-extras
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### Available make targets
|
|
221
|
+
|
|
222
|
+
```bash
|
|
223
|
+
make install # uv sync --all-extras
|
|
224
|
+
make lint # ruff check + format check
|
|
225
|
+
make typecheck # mypy
|
|
226
|
+
make test # pytest
|
|
227
|
+
make test-cov # pytest with coverage
|
|
228
|
+
make build # uv build
|
|
229
|
+
make clean # remove dist/, .venv/, caches
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## Contributing
|
|
235
|
+
|
|
236
|
+
### Branches
|
|
237
|
+
|
|
238
|
+
| Branch | Purpose |
|
|
239
|
+
|--------|---------|
|
|
240
|
+
| `main` | Protected. Releases happen here. Only via PR. |
|
|
241
|
+
| `feat/<name>` | New feature — branch off `main`, PR back to `main` |
|
|
242
|
+
| `fix/<name>` | Bug fix — same flow |
|
|
243
|
+
| `chore/<name>` | Maintenance, deps, tooling |
|
|
244
|
+
|
|
245
|
+
```bash
|
|
246
|
+
git checkout -b feat/profile-switcher
|
|
247
|
+
# ... work ...
|
|
248
|
+
git push origin feat/profile-switcher
|
|
249
|
+
# open PR → CI runs → merge → automatic release
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### Commit messages — Conventional Commits
|
|
253
|
+
|
|
254
|
+
Format: `<type>(<optional scope>): <description>`
|
|
255
|
+
|
|
256
|
+
| Type | When to use | Version bump |
|
|
257
|
+
|------|-------------|-------------|
|
|
258
|
+
| `feat:` | New feature | `minor` — `0.1.0 → 0.2.0` |
|
|
259
|
+
| `fix:` | Bug fix | `patch` — `0.1.0 → 0.1.1` |
|
|
260
|
+
| `feat!:` or `BREAKING CHANGE:` in body | Breaking API change | `major` — `0.1.0 → 1.0.0` |
|
|
261
|
+
| `docs:` | Documentation only | no bump |
|
|
262
|
+
| `chore:` | Deps, tooling, housekeeping | no bump |
|
|
263
|
+
| `ci:` | CI/CD changes | no bump |
|
|
264
|
+
| `refactor:` | Code restructure, no behaviour change | no bump |
|
|
265
|
+
| `test:` | Adding or fixing tests | no bump |
|
|
266
|
+
| `perf:` | Performance improvement | `patch` |
|
|
267
|
+
|
|
268
|
+
Examples:
|
|
269
|
+
|
|
270
|
+
```
|
|
271
|
+
feat: add profile switcher in web UI
|
|
272
|
+
fix: handle empty partition list on first sync
|
|
273
|
+
feat(web)!: rename /api/config to /api/settings
|
|
274
|
+
docs: add TCP hostport explanation to README
|
|
275
|
+
chore: bump fastapi to 0.115
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
### Release cycle (fully automated)
|
|
279
|
+
|
|
280
|
+
When a PR is merged to `main`, GitHub Actions:
|
|
281
|
+
|
|
282
|
+
1. Runs lint + typecheck + tests
|
|
283
|
+
2. `python-semantic-release` reads all commits since the last tag
|
|
284
|
+
3. Determines the next version (`major` / `minor` / `patch`)
|
|
285
|
+
4. Bumps `version` in `pyproject.toml` and `charon/__init__.py`
|
|
286
|
+
5. Updates `CHANGELOG.md`
|
|
287
|
+
6. Creates a git tag `vX.Y.Z` and a GitHub Release
|
|
288
|
+
7. Publishes the package to PyPI
|
|
289
|
+
|
|
290
|
+
> If there are no `feat:` or `fix:` commits (only `chore:`, `docs:`, etc.) — no release is created.
|
|
291
|
+
|
|
292
|
+
### Required secrets (GitHub → Settings → Secrets)
|
|
293
|
+
|
|
294
|
+
| Secret | Value |
|
|
295
|
+
|--------|-------|
|
|
296
|
+
| `PYPI_TOKEN` | API token from pypi.org |
|
|
297
|
+
| `GH_TOKEN` | GitHub PAT with `contents: write` scope |
|
|
298
|
+
|
|
299
|
+
---
|
|
300
|
+
|
|
301
|
+
## License
|
|
302
|
+
|
|
303
|
+
MIT — see [LICENSE](LICENSE).
|