registro-db 1.0.0__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.
- registro_db-1.0.0/.editorconfig +15 -0
- registro_db-1.0.0/.github/ISSUE_TEMPLATE//342/234/250-feature-request.md +23 -0
- registro_db-1.0.0/.github/ISSUE_TEMPLATE//360/237/220/233-bug-report.md +30 -0
- registro_db-1.0.0/.github/ISSUE_TEMPLATE//360/237/233/240-task---improvement.md +20 -0
- registro_db-1.0.0/.github/PULL_REQUEST_TEMPLATE/PULL_REQUEST_TEMPLATE.md +30 -0
- registro_db-1.0.0/.github/workflows/ci.yml +77 -0
- registro_db-1.0.0/.github/workflows/release.yml +104 -0
- registro_db-1.0.0/.gitignore +119 -0
- registro_db-1.0.0/.python-version +1 -0
- registro_db-1.0.0/CHANGELOG.md +14 -0
- registro_db-1.0.0/CONTRIBUTING.md +252 -0
- registro_db-1.0.0/CONTRIBUTORS.md +11 -0
- registro_db-1.0.0/LICENSE +21 -0
- registro_db-1.0.0/PKG-INFO +270 -0
- registro_db-1.0.0/README.md +234 -0
- registro_db-1.0.0/SECURITY.md +17 -0
- registro_db-1.0.0/config.example.toml +48 -0
- registro_db-1.0.0/docker/Dockerfile +49 -0
- registro_db-1.0.0/docker/docker-compose.yml +35 -0
- registro_db-1.0.0/docker/seeds/mysql/01-demo.sql +10 -0
- registro_db-1.0.0/docker/seeds/postgres/01-demo.sql +18 -0
- registro_db-1.0.0/mypy.ini +44 -0
- registro_db-1.0.0/pyproject.toml +110 -0
- registro_db-1.0.0/resources/registro.svg +30 -0
- registro_db-1.0.0/ruff.toml +37 -0
- registro_db-1.0.0/scripts/check.sh +10 -0
- registro_db-1.0.0/scripts/dev.sh +9 -0
- registro_db-1.0.0/scripts/fmt.sh +7 -0
- registro_db-1.0.0/scripts/lint.sh +13 -0
- registro_db-1.0.0/scripts/test.sh +4 -0
- registro_db-1.0.0/src/registro_adapters/__init__.py +17 -0
- registro_db-1.0.0/src/registro_adapters/_introspect.py +124 -0
- registro_db-1.0.0/src/registro_adapters/_params.py +49 -0
- registro_db-1.0.0/src/registro_adapters/_typeinfer.py +111 -0
- registro_db-1.0.0/src/registro_adapters/base.py +267 -0
- registro_db-1.0.0/src/registro_adapters/duckdb_adapter.py +268 -0
- registro_db-1.0.0/src/registro_adapters/introspect/__init__.py +5 -0
- registro_db-1.0.0/src/registro_adapters/introspect/duckdb.py +263 -0
- registro_db-1.0.0/src/registro_adapters/introspect/mysql.py +196 -0
- registro_db-1.0.0/src/registro_adapters/introspect/postgres.py +251 -0
- registro_db-1.0.0/src/registro_adapters/introspect/sqlite.py +176 -0
- registro_db-1.0.0/src/registro_adapters/mysql_adapter.py +437 -0
- registro_db-1.0.0/src/registro_adapters/postgres_adapter.py +331 -0
- registro_db-1.0.0/src/registro_adapters/py.typed +0 -0
- registro_db-1.0.0/src/registro_adapters/registry.py +102 -0
- registro_db-1.0.0/src/registro_adapters/sqlite_adapter.py +276 -0
- registro_db-1.0.0/src/registro_cli/__init__.py +5 -0
- registro_db-1.0.0/src/registro_cli/__main__.py +4 -0
- registro_db-1.0.0/src/registro_cli/main.py +363 -0
- registro_db-1.0.0/src/registro_cli/py.typed +0 -0
- registro_db-1.0.0/src/registro_core/__init__.py +19 -0
- registro_db-1.0.0/src/registro_core/config.py +170 -0
- registro_db-1.0.0/src/registro_core/errors.py +65 -0
- registro_db-1.0.0/src/registro_core/events.py +275 -0
- registro_db-1.0.0/src/registro_core/export.py +32 -0
- registro_db-1.0.0/src/registro_core/logging.py +203 -0
- registro_db-1.0.0/src/registro_core/models/__init__.py +33 -0
- registro_db-1.0.0/src/registro_core/models/connection.py +185 -0
- registro_db-1.0.0/src/registro_core/models/query.py +66 -0
- registro_db-1.0.0/src/registro_core/models/result.py +53 -0
- registro_db-1.0.0/src/registro_core/models/schema.py +83 -0
- registro_db-1.0.0/src/registro_core/protocols/__init__.py +11 -0
- registro_db-1.0.0/src/registro_core/protocols/adapter.py +85 -0
- registro_db-1.0.0/src/registro_core/protocols/plugin.py +28 -0
- registro_db-1.0.0/src/registro_core/py.typed +0 -0
- registro_db-1.0.0/src/registro_core/version.py +17 -0
- registro_db-1.0.0/src/registro_engine/__init__.py +16 -0
- registro_db-1.0.0/src/registro_engine/executor.py +271 -0
- registro_db-1.0.0/src/registro_engine/history.py +165 -0
- registro_db-1.0.0/src/registro_engine/introspector.py +97 -0
- registro_db-1.0.0/src/registro_engine/pool.py +220 -0
- registro_db-1.0.0/src/registro_engine/py.typed +0 -0
- registro_db-1.0.0/src/registro_engine/transaction.py +21 -0
- registro_db-1.0.0/src/registro_plugins/__init__.py +16 -0
- registro_db-1.0.0/src/registro_plugins/api.py +110 -0
- registro_db-1.0.0/src/registro_plugins/context.py +75 -0
- registro_db-1.0.0/src/registro_plugins/loader.py +196 -0
- registro_db-1.0.0/src/registro_plugins/py.typed +0 -0
- registro_db-1.0.0/src/registro_plugins/registry.py +80 -0
- registro_db-1.0.0/src/registro_tui/__init__.py +5 -0
- registro_db-1.0.0/src/registro_tui/app.py +696 -0
- registro_db-1.0.0/src/registro_tui/builtin_commands.py +120 -0
- registro_db-1.0.0/src/registro_tui/events.py +11 -0
- registro_db-1.0.0/src/registro_tui/keymaps/__init__.py +20 -0
- registro_db-1.0.0/src/registro_tui/keymaps/_labels.py +39 -0
- registro_db-1.0.0/src/registro_tui/keymaps/default.py +62 -0
- registro_db-1.0.0/src/registro_tui/keymaps/vim.py +20 -0
- registro_db-1.0.0/src/registro_tui/py.typed +0 -0
- registro_db-1.0.0/src/registro_tui/registro.tcss +308 -0
- registro_db-1.0.0/src/registro_tui/screens/__init__.py +8 -0
- registro_db-1.0.0/src/registro_tui/screens/_base.py +76 -0
- registro_db-1.0.0/src/registro_tui/screens/command_palette.py +101 -0
- registro_db-1.0.0/src/registro_tui/screens/connection.py +114 -0
- registro_db-1.0.0/src/registro_tui/screens/help.py +102 -0
- registro_db-1.0.0/src/registro_tui/screens/history.py +81 -0
- registro_db-1.0.0/src/registro_tui/screens/main.py +44 -0
- registro_db-1.0.0/src/registro_tui/screens/row_limit.py +68 -0
- registro_db-1.0.0/src/registro_tui/session.py +161 -0
- registro_db-1.0.0/src/registro_tui/state.py +37 -0
- registro_db-1.0.0/src/registro_tui/themes/__init__.py +8 -0
- registro_db-1.0.0/src/registro_tui/themes/_base.py +18 -0
- registro_db-1.0.0/src/registro_tui/themes/monokai.py +16 -0
- registro_db-1.0.0/src/registro_tui/themes/nord.py +15 -0
- registro_db-1.0.0/src/registro_tui/widgets/__init__.py +10 -0
- registro_db-1.0.0/src/registro_tui/widgets/action_bar.py +48 -0
- registro_db-1.0.0/src/registro_tui/widgets/query_tabs.py +44 -0
- registro_db-1.0.0/src/registro_tui/widgets/results_table.py +191 -0
- registro_db-1.0.0/src/registro_tui/widgets/schema_tree.py +153 -0
- registro_db-1.0.0/src/registro_tui/widgets/sql_editor.py +17 -0
- registro_db-1.0.0/src/registro_tui/widgets/status_bar.py +164 -0
- registro_db-1.0.0/tests/__init__.py +0 -0
- registro_db-1.0.0/tests/adapters/__init__.py +0 -0
- registro_db-1.0.0/tests/adapters/_conformance.py +386 -0
- registro_db-1.0.0/tests/adapters/conftest.py +78 -0
- registro_db-1.0.0/tests/adapters/test_bulk_introspection.py +94 -0
- registro_db-1.0.0/tests/adapters/test_cancellation_inflight.py +100 -0
- registro_db-1.0.0/tests/adapters/test_conformance_duckdb.py +29 -0
- registro_db-1.0.0/tests/adapters/test_conformance_sqlite.py +25 -0
- registro_db-1.0.0/tests/adapters/test_dsn_params.py +56 -0
- registro_db-1.0.0/tests/adapters/test_duckdb_adapter.py +119 -0
- registro_db-1.0.0/tests/adapters/test_introspect.py +157 -0
- registro_db-1.0.0/tests/adapters/test_mysql_adapter.py +98 -0
- registro_db-1.0.0/tests/adapters/test_mysql_adapter_unit.py +258 -0
- registro_db-1.0.0/tests/adapters/test_postgres_adapter.py +65 -0
- registro_db-1.0.0/tests/adapters/test_postgres_adapter_unit.py +192 -0
- registro_db-1.0.0/tests/adapters/test_registry.py +92 -0
- registro_db-1.0.0/tests/adapters/test_sqlite_adapter.py +163 -0
- registro_db-1.0.0/tests/adapters/test_typeinfer.py +107 -0
- registro_db-1.0.0/tests/cli/__init__.py +0 -0
- registro_db-1.0.0/tests/cli/test_main.py +244 -0
- registro_db-1.0.0/tests/conftest.py +29 -0
- registro_db-1.0.0/tests/core/__init__.py +0 -0
- registro_db-1.0.0/tests/core/test_config.py +118 -0
- registro_db-1.0.0/tests/core/test_errors.py +58 -0
- registro_db-1.0.0/tests/core/test_events.py +239 -0
- registro_db-1.0.0/tests/core/test_export.py +33 -0
- registro_db-1.0.0/tests/core/test_logging.py +78 -0
- registro_db-1.0.0/tests/core/test_models.py +139 -0
- registro_db-1.0.0/tests/engine/__init__.py +0 -0
- registro_db-1.0.0/tests/engine/test_executor.py +417 -0
- registro_db-1.0.0/tests/engine/test_history.py +93 -0
- registro_db-1.0.0/tests/engine/test_introspector.py +208 -0
- registro_db-1.0.0/tests/engine/test_pool.py +254 -0
- registro_db-1.0.0/tests/engine/test_transaction.py +60 -0
- registro_db-1.0.0/tests/fixtures/__init__.py +0 -0
- registro_db-1.0.0/tests/fixtures/sample_plugin.py +39 -0
- registro_db-1.0.0/tests/plugins/__init__.py +0 -0
- registro_db-1.0.0/tests/plugins/test_api.py +81 -0
- registro_db-1.0.0/tests/plugins/test_context.py +123 -0
- registro_db-1.0.0/tests/plugins/test_loader.py +227 -0
- registro_db-1.0.0/tests/plugins/test_loader_discovery.py +92 -0
- registro_db-1.0.0/tests/plugins/test_panels.py +142 -0
- registro_db-1.0.0/tests/plugins/test_registry.py +53 -0
- registro_db-1.0.0/tests/scripts/test-integration.sh +41 -0
- registro_db-1.0.0/tests/scripts/test.sh +7 -0
- registro_db-1.0.0/tests/tui/__init__.py +0 -0
- registro_db-1.0.0/tests/tui/test_app_smoke.py +283 -0
- registro_db-1.0.0/tests/tui/test_builtin_commands.py +37 -0
- registro_db-1.0.0/tests/tui/test_connection_modal.py +187 -0
- registro_db-1.0.0/tests/tui/test_help_modal.py +94 -0
- registro_db-1.0.0/tests/tui/test_history_panel.py +127 -0
- registro_db-1.0.0/tests/tui/test_keymap_conflicts.py +265 -0
- registro_db-1.0.0/tests/tui/test_markup_escaping.py +262 -0
- registro_db-1.0.0/tests/tui/test_pool_in_app.py +126 -0
- registro_db-1.0.0/tests/tui/test_results_pane.py +154 -0
- registro_db-1.0.0/tests/tui/test_row_limit.py +81 -0
- registro_db-1.0.0/tests/tui/test_schema_tree.py +153 -0
- registro_db-1.0.0/tests/tui/test_session_helpers.py +147 -0
- registro_db-1.0.0/tests/tui/test_vim_keymap.py +43 -0
- registro_db-1.0.0/tests/tui/test_widgets.py +196 -0
- registro_db-1.0.0/uv.lock +1312 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
root = true
|
|
2
|
+
|
|
3
|
+
[*]
|
|
4
|
+
charset = utf-8
|
|
5
|
+
end_of_line = lf
|
|
6
|
+
indent_style = space
|
|
7
|
+
indent_size = 4
|
|
8
|
+
insert_final_newline = true
|
|
9
|
+
trim_trailing_whitespace = true
|
|
10
|
+
|
|
11
|
+
[*.{yml,yaml,toml,json}]
|
|
12
|
+
indent_size = 2
|
|
13
|
+
|
|
14
|
+
[*.md]
|
|
15
|
+
trim_trailing_whitespace = false
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: '✨ Feature Request'
|
|
3
|
+
about: Suggest an idea or a new functionality
|
|
4
|
+
title: '[FEATURE]: '
|
|
5
|
+
labels: enhancement
|
|
6
|
+
assignees: ''
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
### Is your feature request related to a problem?
|
|
10
|
+
|
|
11
|
+
A clear and concise description of what the problem is (e.g. I'm always frustrated when...).
|
|
12
|
+
|
|
13
|
+
### Describe the solution you'd like
|
|
14
|
+
|
|
15
|
+
A clear and concise description of what you want to happen.
|
|
16
|
+
|
|
17
|
+
### Describe alternatives you've considered
|
|
18
|
+
|
|
19
|
+
A clear and concise description of any alternative solutions or features you've considered.
|
|
20
|
+
|
|
21
|
+
### Additional context
|
|
22
|
+
|
|
23
|
+
Add any other context or screenshots about the feature request here.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: "\U0001F41B Bug Report"
|
|
3
|
+
about: Report an error or unexpected behavior
|
|
4
|
+
title: '[BUG]: '
|
|
5
|
+
labels: bug
|
|
6
|
+
assignees: ''
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
### Bug Description
|
|
10
|
+
|
|
11
|
+
A clear and concise description of what the bug is.
|
|
12
|
+
|
|
13
|
+
### Steps to Reproduce
|
|
14
|
+
|
|
15
|
+
1. Go to '...'
|
|
16
|
+
2. Click on '....'
|
|
17
|
+
3. Scroll down to '....'
|
|
18
|
+
4. See error
|
|
19
|
+
|
|
20
|
+
### Expected Behavior
|
|
21
|
+
|
|
22
|
+
A clear and concise description of what you expected to happen.
|
|
23
|
+
|
|
24
|
+
### Screenshots / Logs
|
|
25
|
+
|
|
26
|
+
If applicable, add screenshots or paste console logs to help explain your problem.
|
|
27
|
+
|
|
28
|
+
### Environment
|
|
29
|
+
|
|
30
|
+
- **Version:**
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: "\U0001F6E0 Task / Improvement"
|
|
3
|
+
about: Maintenance, refactoring, or small improvements
|
|
4
|
+
title: '[TASK]: '
|
|
5
|
+
labels: maintenance
|
|
6
|
+
assignees: ''
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
### Description
|
|
10
|
+
|
|
11
|
+
A clear and concise description of what needs to be done.
|
|
12
|
+
|
|
13
|
+
### Motivation
|
|
14
|
+
|
|
15
|
+
Why is this task necessary or beneficial for the project?
|
|
16
|
+
|
|
17
|
+
### Checklist
|
|
18
|
+
|
|
19
|
+
- [ ] Task 1
|
|
20
|
+
- [ ] Task 2
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
## Description
|
|
2
|
+
|
|
3
|
+
<!-- Provide a brief summary of the changes and the motivation behind them. -->
|
|
4
|
+
|
|
5
|
+
## Related Issue
|
|
6
|
+
|
|
7
|
+
<!-- Link the issue here using the syntax: Fixes #123 or Related to #123 -->
|
|
8
|
+
|
|
9
|
+
## Type of Change
|
|
10
|
+
|
|
11
|
+
- [ ] 🐛 Bug fix (non-breaking change which fixes an issue)
|
|
12
|
+
- [ ] ✨ New feature (non-breaking change which adds functionality)
|
|
13
|
+
- [ ] 💥 Breaking change (fix or feature that would cause existing functionality to not work as expected)
|
|
14
|
+
- [ ] 🛠 Maintenance / Refactoring
|
|
15
|
+
|
|
16
|
+
## How Has This Been Tested?
|
|
17
|
+
|
|
18
|
+
<!-- Describe the tests that you ran to verify your changes. -->
|
|
19
|
+
|
|
20
|
+
- [ ] Manual testing (please describe)
|
|
21
|
+
- [ ] Unit tests added/updated
|
|
22
|
+
- [ ] Integration tests added/updated
|
|
23
|
+
|
|
24
|
+
## Checklist
|
|
25
|
+
|
|
26
|
+
- [ ] My code follows the style guidelines of this project
|
|
27
|
+
- [ ] I have performed a self-review of my code
|
|
28
|
+
- [ ] I have commented my code, particularly in hard-to-understand areas
|
|
29
|
+
- [ ] I have made corresponding changes to the documentation
|
|
30
|
+
- [ ] My changes generate no new warnings
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
paths-ignore:
|
|
7
|
+
- '**.md'
|
|
8
|
+
- 'docs/**'
|
|
9
|
+
pull_request:
|
|
10
|
+
paths-ignore:
|
|
11
|
+
- '**.md'
|
|
12
|
+
- 'docs/**'
|
|
13
|
+
workflow_dispatch:
|
|
14
|
+
workflow_call:
|
|
15
|
+
|
|
16
|
+
concurrency:
|
|
17
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
18
|
+
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
|
19
|
+
|
|
20
|
+
permissions:
|
|
21
|
+
contents: read
|
|
22
|
+
|
|
23
|
+
jobs:
|
|
24
|
+
lint:
|
|
25
|
+
runs-on: ubuntu-24.04
|
|
26
|
+
steps:
|
|
27
|
+
- uses: actions/checkout@v6
|
|
28
|
+
|
|
29
|
+
- name: Install uv
|
|
30
|
+
uses: astral-sh/setup-uv@v8.1.0
|
|
31
|
+
with:
|
|
32
|
+
enable-cache: true
|
|
33
|
+
cache-suffix: "lint"
|
|
34
|
+
python-version: "3.13"
|
|
35
|
+
|
|
36
|
+
- name: Sync workspace
|
|
37
|
+
run: uv sync --all-extras --dev --frozen
|
|
38
|
+
|
|
39
|
+
- name: Lint (ruff format + check + mypy)
|
|
40
|
+
run: ./scripts/lint.sh
|
|
41
|
+
|
|
42
|
+
test:
|
|
43
|
+
runs-on: ${{ matrix.os }}
|
|
44
|
+
defaults:
|
|
45
|
+
run:
|
|
46
|
+
shell: bash
|
|
47
|
+
strategy:
|
|
48
|
+
matrix:
|
|
49
|
+
os: [ubuntu-24.04, macos-15]
|
|
50
|
+
fail-fast: false
|
|
51
|
+
steps:
|
|
52
|
+
- uses: actions/checkout@v6
|
|
53
|
+
|
|
54
|
+
- name: Install uv
|
|
55
|
+
uses: astral-sh/setup-uv@v8.1.0
|
|
56
|
+
with:
|
|
57
|
+
enable-cache: true
|
|
58
|
+
cache-suffix: "test"
|
|
59
|
+
python-version: "3.13"
|
|
60
|
+
|
|
61
|
+
- name: Sync workspace
|
|
62
|
+
run: uv sync --all-extras --dev --frozen
|
|
63
|
+
|
|
64
|
+
- name: Test (pytest + coverage gate)
|
|
65
|
+
run: ./tests/scripts/test.sh
|
|
66
|
+
env:
|
|
67
|
+
REGISTRO_TEST_MARKERS: ${{ matrix.os == 'ubuntu-24.04' && 'integration or not integration' || 'not integration' }}
|
|
68
|
+
|
|
69
|
+
dependency-review:
|
|
70
|
+
name: Dependency Review
|
|
71
|
+
runs-on: ubuntu-24.04
|
|
72
|
+
if: github.event_name == 'pull_request'
|
|
73
|
+
steps:
|
|
74
|
+
- name: Checkout repository
|
|
75
|
+
uses: actions/checkout@v6
|
|
76
|
+
- name: Dependency Review
|
|
77
|
+
uses: actions/dependency-review-action@v4
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
validate:
|
|
14
|
+
runs-on: ubuntu-24.04
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v6
|
|
17
|
+
- name: Install uv
|
|
18
|
+
uses: astral-sh/setup-uv@v8.1.0
|
|
19
|
+
with:
|
|
20
|
+
enable-cache: true
|
|
21
|
+
python-version: "3.13"
|
|
22
|
+
- name: Validate tag version against pyproject.toml
|
|
23
|
+
run: |
|
|
24
|
+
if [[ "$GITHUB_REF" == refs/tags/v* ]]; then
|
|
25
|
+
TAG_VERSION="${GITHUB_REF#refs/tags/v}"
|
|
26
|
+
PY_VERSION=$(uv run python -c 'import tomllib; print(tomllib.load(open("pyproject.toml", "rb"))["project"]["version"])')
|
|
27
|
+
echo "Tag version: $TAG_VERSION | pyproject.toml version: $PY_VERSION"
|
|
28
|
+
if [[ "$TAG_VERSION" != "$PY_VERSION" ]]; then
|
|
29
|
+
echo "::error::Tag version ($TAG_VERSION) does not match pyproject.toml version ($PY_VERSION)"
|
|
30
|
+
exit 1
|
|
31
|
+
fi
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
checks:
|
|
35
|
+
uses: ./.github/workflows/ci.yml
|
|
36
|
+
|
|
37
|
+
release:
|
|
38
|
+
needs: [validate, checks]
|
|
39
|
+
runs-on: ubuntu-24.04
|
|
40
|
+
environment: release
|
|
41
|
+
permissions:
|
|
42
|
+
contents: write
|
|
43
|
+
id-token: write
|
|
44
|
+
steps:
|
|
45
|
+
- uses: actions/checkout@v6
|
|
46
|
+
|
|
47
|
+
- name: Install uv
|
|
48
|
+
uses: astral-sh/setup-uv@v8.1.0
|
|
49
|
+
with:
|
|
50
|
+
enable-cache: true
|
|
51
|
+
python-version: "3.13"
|
|
52
|
+
|
|
53
|
+
- name: Sync workspace
|
|
54
|
+
run: uv sync --all-extras --dev --frozen
|
|
55
|
+
|
|
56
|
+
- name: Build packages
|
|
57
|
+
run: uv build --out-dir dist
|
|
58
|
+
|
|
59
|
+
- name: Generate Checksums
|
|
60
|
+
run: |
|
|
61
|
+
(cd dist && sha256sum *.tar.gz *.whl) > SHA256SUMS
|
|
62
|
+
|
|
63
|
+
- name: Extract Changelog Entry
|
|
64
|
+
id: extract_changelog
|
|
65
|
+
env:
|
|
66
|
+
GITHUB_REF_NAME: ${{ github.ref_name }}
|
|
67
|
+
run: |
|
|
68
|
+
uv run python -c '
|
|
69
|
+
import os, re, uuid
|
|
70
|
+
version = os.environ["GITHUB_REF_NAME"].lstrip("v")
|
|
71
|
+
with open("CHANGELOG.md") as f:
|
|
72
|
+
content = f.read()
|
|
73
|
+
pattern = rf"(?m)^##\s+\[?{re.escape(version)}\]?(.*?)(?=^##\s+\[?|\Z)"
|
|
74
|
+
match = re.search(pattern, content, re.DOTALL)
|
|
75
|
+
notes = ""
|
|
76
|
+
if match:
|
|
77
|
+
raw_notes = match.group(1).strip()
|
|
78
|
+
lines = raw_notes.splitlines()
|
|
79
|
+
if lines and (re.search(r"\d{4}-\d{1,2}-\d{1,2}", lines[0]) or not lines[0].strip()):
|
|
80
|
+
notes = "\n".join(lines[1:]).strip()
|
|
81
|
+
else:
|
|
82
|
+
notes = raw_notes
|
|
83
|
+
if not notes:
|
|
84
|
+
notes = "No changelog entry found for this release."
|
|
85
|
+
github_output = os.environ.get("GITHUB_OUTPUT")
|
|
86
|
+
if github_output:
|
|
87
|
+
delimiter = f"EOF_{uuid.uuid4().hex}"
|
|
88
|
+
with open(github_output, "a") as f:
|
|
89
|
+
f.write(f"body<<{delimiter}\n{notes}\n{delimiter}\n")
|
|
90
|
+
'
|
|
91
|
+
|
|
92
|
+
- name: Create GitHub Release
|
|
93
|
+
uses: softprops/action-gh-release@v3
|
|
94
|
+
with:
|
|
95
|
+
name: Registro ${{ github.ref_name }}
|
|
96
|
+
body: ${{ steps.extract_changelog.outputs.body }}
|
|
97
|
+
files: |
|
|
98
|
+
dist/*.tar.gz
|
|
99
|
+
dist/*.whl
|
|
100
|
+
SHA256SUMS
|
|
101
|
+
generate_release_notes: false
|
|
102
|
+
|
|
103
|
+
- name: Publish package distributions to PyPI
|
|
104
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# Python bytecode / runtime
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
.Python
|
|
7
|
+
|
|
8
|
+
# Distribution / Packaging
|
|
9
|
+
build/
|
|
10
|
+
develop-eggs/
|
|
11
|
+
dist/
|
|
12
|
+
downloads/
|
|
13
|
+
eggs/
|
|
14
|
+
.eggs/
|
|
15
|
+
lib/
|
|
16
|
+
lib64/
|
|
17
|
+
parts/
|
|
18
|
+
sdist/
|
|
19
|
+
var/
|
|
20
|
+
wheels/
|
|
21
|
+
share/python-wheels/
|
|
22
|
+
*.egg-info/
|
|
23
|
+
.installed.cfg
|
|
24
|
+
*.egg
|
|
25
|
+
MANIFEST
|
|
26
|
+
|
|
27
|
+
# Virtual Environments and uv
|
|
28
|
+
.venv/
|
|
29
|
+
venv/
|
|
30
|
+
env/
|
|
31
|
+
.uv/
|
|
32
|
+
.uv-cache/
|
|
33
|
+
pip-wheel-metadata/
|
|
34
|
+
|
|
35
|
+
# Linting / Type-checking / Testing
|
|
36
|
+
.mypy_cache/
|
|
37
|
+
.ruff_cache/
|
|
38
|
+
.pytest_cache/
|
|
39
|
+
.hypothesis/
|
|
40
|
+
.nox/
|
|
41
|
+
.tox/
|
|
42
|
+
.dmypy.json
|
|
43
|
+
dmypy.json
|
|
44
|
+
.pre-commit-cache/
|
|
45
|
+
|
|
46
|
+
# Coverage
|
|
47
|
+
.coverage
|
|
48
|
+
.coverage.*
|
|
49
|
+
htmlcov/
|
|
50
|
+
coverage.xml
|
|
51
|
+
*.cover
|
|
52
|
+
|
|
53
|
+
# Jupyter / Notebooks
|
|
54
|
+
.ipynb_checkpoints/
|
|
55
|
+
|
|
56
|
+
# Environment / Secrets
|
|
57
|
+
.env
|
|
58
|
+
.env.*
|
|
59
|
+
!.env.example
|
|
60
|
+
!.env.template
|
|
61
|
+
config.toml
|
|
62
|
+
!config.example.toml
|
|
63
|
+
|
|
64
|
+
# Docker (local overrides)
|
|
65
|
+
docker-compose.override.yml
|
|
66
|
+
|
|
67
|
+
# Registro (project-specific runtime & storage)
|
|
68
|
+
*.db
|
|
69
|
+
*.sqlite
|
|
70
|
+
*.sqlite3
|
|
71
|
+
*.duckdb
|
|
72
|
+
*.log
|
|
73
|
+
registro.log
|
|
74
|
+
.registro/
|
|
75
|
+
scratch/
|
|
76
|
+
|
|
77
|
+
# IDEs and Editors
|
|
78
|
+
.vscode/
|
|
79
|
+
.idea/
|
|
80
|
+
.zed/
|
|
81
|
+
*.swp
|
|
82
|
+
*.swo
|
|
83
|
+
*~
|
|
84
|
+
*.bak
|
|
85
|
+
*.sublime-project
|
|
86
|
+
*.sublime-workspace
|
|
87
|
+
|
|
88
|
+
# OS / System
|
|
89
|
+
.DS_Store
|
|
90
|
+
.DS_Store?
|
|
91
|
+
._*
|
|
92
|
+
.Spotlight-V100
|
|
93
|
+
.Trashes
|
|
94
|
+
ehthumbs.db
|
|
95
|
+
Thumbs.db
|
|
96
|
+
desktop.ini
|
|
97
|
+
|
|
98
|
+
# AI Tools & Agents
|
|
99
|
+
.openai/
|
|
100
|
+
.chatgpt/
|
|
101
|
+
gpt-config.json
|
|
102
|
+
openai.config.json
|
|
103
|
+
.anthropic/
|
|
104
|
+
.claude/
|
|
105
|
+
claude.config.json
|
|
106
|
+
claude-settings.json
|
|
107
|
+
.prompts/
|
|
108
|
+
.prompt-cache/
|
|
109
|
+
.ai/
|
|
110
|
+
.gemini/
|
|
111
|
+
.antigravity/
|
|
112
|
+
.antigravitycli/
|
|
113
|
+
.copilot/
|
|
114
|
+
.codex/
|
|
115
|
+
.cagent/
|
|
116
|
+
.cursor/
|
|
117
|
+
.windsurf/
|
|
118
|
+
.cline/
|
|
119
|
+
CLAUDE.md
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.13
|
|
@@ -0,0 +1,14 @@
|
|
|
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
|
+
## [1.0.0] - 2026-09-23
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- First implementation of Registro.
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
# Contributing to Registro 📟
|
|
2
|
+
|
|
3
|
+
Thank you for your interest in contributing to **Registro**! We welcome contributions, bug reports, feature requests, and security improvements from the community.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 🛠️ Development Setup
|
|
8
|
+
|
|
9
|
+
Registro uses [uv](https://github.com/astral-sh/uv) for fast, modern Python package management and workflow orchestration.
|
|
10
|
+
|
|
11
|
+
### Prerequisites
|
|
12
|
+
|
|
13
|
+
- **Python 3.13 or newer**
|
|
14
|
+
- **uv** (Install via `curl -LsSf https://astral.sh/uv/install.sh | sh` or your package manager)
|
|
15
|
+
|
|
16
|
+
### Quick Start
|
|
17
|
+
|
|
18
|
+
1. **Fork and Clone the Repository**
|
|
19
|
+
```bash
|
|
20
|
+
git clone https://github.com/YOUR_USERNAME/registro.git
|
|
21
|
+
cd registro
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
2. **Synchronize Dependencies**
|
|
25
|
+
Initialize the virtual environment and install all packages (including development packages and all database extras):
|
|
26
|
+
```bash
|
|
27
|
+
uv sync --all-extras --dev
|
|
28
|
+
```
|
|
29
|
+
*Alternatively, run `./scripts/dev.sh` to initialize the environment.*
|
|
30
|
+
|
|
31
|
+
3. **Activate the Environment**
|
|
32
|
+
```bash
|
|
33
|
+
source .venv/bin/activate
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## ⚙️ Coding Standards & Tools
|
|
39
|
+
|
|
40
|
+
We use strict quality gates to maintain codebase health. All of these run automatically on CI, but you should run them locally before submitting changes.
|
|
41
|
+
|
|
42
|
+
### Developer Scripts
|
|
43
|
+
|
|
44
|
+
We provide dedicated helper scripts under `scripts/` to streamline local workflows:
|
|
45
|
+
|
|
46
|
+
| Script | Purpose |
|
|
47
|
+
| :--- | :--- |
|
|
48
|
+
| `./scripts/dev.sh` | Synchronizes virtual environment with all extras and dev dependencies |
|
|
49
|
+
| `./scripts/fmt.sh` | Formats code with Ruff and applies safe linter autofixes |
|
|
50
|
+
| `./scripts/lint.sh` | Runs Ruff format check, Ruff lint check, and MyPy strict type check |
|
|
51
|
+
| `./scripts/test.sh` | Runs the offline test suite with coverage reporting |
|
|
52
|
+
| `./scripts/check.sh` | The pre-PR quality gate (runs `./scripts/lint.sh` and `./scripts/test.sh`) |
|
|
53
|
+
|
|
54
|
+
### Manual Linting & Formatting
|
|
55
|
+
|
|
56
|
+
We use **Ruff** for formatting and linting, and **MyPy** for strict static type checking:
|
|
57
|
+
|
|
58
|
+
- **Check formatting:**
|
|
59
|
+
```bash
|
|
60
|
+
uv run ruff format --check .
|
|
61
|
+
```
|
|
62
|
+
- **Format code:**
|
|
63
|
+
```bash
|
|
64
|
+
uv run ruff format .
|
|
65
|
+
# Or run ./scripts/fmt.sh
|
|
66
|
+
```
|
|
67
|
+
- **Lint code:**
|
|
68
|
+
```bash
|
|
69
|
+
uv run ruff check .
|
|
70
|
+
```
|
|
71
|
+
- **Strict type checking:**
|
|
72
|
+
```bash
|
|
73
|
+
uv run mypy --config-file=mypy.ini src
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## 🧪 Testing
|
|
79
|
+
|
|
80
|
+
We use **Pytest** for running our test suite with strict coverage requirements.
|
|
81
|
+
|
|
82
|
+
- **Run unit tests (offline suite):**
|
|
83
|
+
```bash
|
|
84
|
+
uv run pytest -m "not integration" --cov=src
|
|
85
|
+
# Or simply:
|
|
86
|
+
./scripts/test.sh
|
|
87
|
+
```
|
|
88
|
+
- **Run integration tests (requires Docker daemon for PostgreSQL and MySQL):**
|
|
89
|
+
```bash
|
|
90
|
+
./tests/scripts/test-integration.sh
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Coverage Quality Gate
|
|
94
|
+
|
|
95
|
+
Our test suite enforces an **84% minimum test coverage gate** (`fail_under = 84` in `pyproject.toml`). The offline suite run must satisfy this gate on its own without relying on server-backed integration tests.
|
|
96
|
+
|
|
97
|
+
### The Ultimate Quality Gate
|
|
98
|
+
|
|
99
|
+
Before submitting any Pull Request, ensure that formatting, linting, type-checking, and test coverage all pass cleanly by running:
|
|
100
|
+
```bash
|
|
101
|
+
./scripts/check.sh
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## 🔌 Writing Plugins
|
|
107
|
+
|
|
108
|
+
Registro features an extensible, plugin-first architecture. You can contribute new plugins or create external plugins to add dockable panels, custom commands, and event listeners.
|
|
109
|
+
|
|
110
|
+
### Plugin Protocol
|
|
111
|
+
|
|
112
|
+
A Registro plugin implements the structural `Plugin` protocol (`registro_core.protocols.plugin.Plugin`):
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
from registro_core.protocols.plugin import PluginManifest
|
|
116
|
+
from registro_plugins.context import PluginContext
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class MyPlugin:
|
|
120
|
+
manifest = PluginManifest(
|
|
121
|
+
id="my_plugin",
|
|
122
|
+
name="My Plugin",
|
|
123
|
+
version="1.0.0",
|
|
124
|
+
description="A custom Registro plugin",
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
def on_load(self, ctx: PluginContext) -> None:
|
|
128
|
+
"""Called when the plugin is loaded."""
|
|
129
|
+
...
|
|
130
|
+
|
|
131
|
+
def on_unload(self, ctx: PluginContext) -> None:
|
|
132
|
+
"""Called when the plugin is unloaded or during app shutdown."""
|
|
133
|
+
...
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Packaging & Discovery
|
|
137
|
+
|
|
138
|
+
Plugins are discovered dynamically via Python entry points in the `registro.plugins` group. Add the following to your `pyproject.toml`:
|
|
139
|
+
|
|
140
|
+
```toml
|
|
141
|
+
[project.entry-points."registro.plugins"]
|
|
142
|
+
my_plugin = "my_package.plugin:MyPlugin"
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
You can verify that Registro discovers your plugin by running:
|
|
146
|
+
```bash
|
|
147
|
+
registro plugins list
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Subscribing to Event Bus Hooks
|
|
151
|
+
|
|
152
|
+
Plugins can subscribe to asynchronous lifecycle events on the `EventBus`. Key events defined in [events.py](src/registro_core/events.py) include:
|
|
153
|
+
|
|
154
|
+
- `ConnectionOpened` / `ConnectionClosed`
|
|
155
|
+
- `QueryStarted` / `QueryProgress` / `QueryCompleted` / `QueryFailed` / `QueryCancelled`
|
|
156
|
+
- `SchemaRefreshed`
|
|
157
|
+
- `PluginLoaded` / `PluginUnloaded`
|
|
158
|
+
|
|
159
|
+
The `@hook` decorator tags a method with the target event name. In `on_load`, subscribe your handler to the event class via `ctx.subscribe`:
|
|
160
|
+
|
|
161
|
+
```python
|
|
162
|
+
from registro_core.events import QueryStarted
|
|
163
|
+
from registro_plugins.api import hook, hook_event, is_hook
|
|
164
|
+
from registro_plugins.context import PluginContext
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
class QueryLoggerPlugin:
|
|
168
|
+
manifest = PluginManifest(id="query_logger", name="Query Logger")
|
|
169
|
+
|
|
170
|
+
@hook("query.started")
|
|
171
|
+
async def handle_query_started(self, ev: QueryStarted) -> None:
|
|
172
|
+
# Asynchronously handle query start
|
|
173
|
+
pass
|
|
174
|
+
|
|
175
|
+
def on_load(self, ctx: PluginContext) -> None:
|
|
176
|
+
for name in dir(self):
|
|
177
|
+
fn = getattr(self, name)
|
|
178
|
+
if is_hook(fn) and hook_event(fn) == "query.started":
|
|
179
|
+
ctx.subscribe(QueryStarted, fn)
|
|
180
|
+
|
|
181
|
+
def on_unload(self, ctx: PluginContext) -> None:
|
|
182
|
+
pass
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Registering Commands and Panels
|
|
186
|
+
|
|
187
|
+
Plugins can contribute actions to the fuzzy command palette (`Ctrl+P`) and dockable panels to the main UI:
|
|
188
|
+
|
|
189
|
+
```python
|
|
190
|
+
from textual.widgets import Static
|
|
191
|
+
from registro_plugins.api import Command, Panel
|
|
192
|
+
from registro_plugins.context import PluginContext
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
class AnalyticsPlugin:
|
|
196
|
+
manifest = PluginManifest(id="analytics", name="Analytics")
|
|
197
|
+
|
|
198
|
+
def on_load(self, ctx: PluginContext) -> None:
|
|
199
|
+
# Register a command for the command palette (Ctrl+P)
|
|
200
|
+
async def run_diagnostics(context: PluginContext) -> None:
|
|
201
|
+
context.logger.info("Diagnostics started")
|
|
202
|
+
|
|
203
|
+
ctx.register_command(
|
|
204
|
+
Command(
|
|
205
|
+
id="analytics.diagnostics",
|
|
206
|
+
title="Run Database Diagnostics",
|
|
207
|
+
handler=run_diagnostics,
|
|
208
|
+
category="Analytics",
|
|
209
|
+
)
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
# Register a dockable panel ("left", "right", or "bottom")
|
|
213
|
+
def create_panel(_context: PluginContext) -> Static:
|
|
214
|
+
return Static("★ Analytics Rail Content", id="analytics-panel-content")
|
|
215
|
+
|
|
216
|
+
ctx.register_panel(
|
|
217
|
+
Panel(
|
|
218
|
+
id="analytics.sidebar",
|
|
219
|
+
title="Analytics",
|
|
220
|
+
placement="right",
|
|
221
|
+
factory=create_panel,
|
|
222
|
+
initial_size=30,
|
|
223
|
+
)
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
def on_unload(self, ctx: PluginContext) -> None:
|
|
227
|
+
pass
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### Context & Host Services
|
|
231
|
+
|
|
232
|
+
Each plugin receives an isolated `PluginContext` providing:
|
|
233
|
+
- `ctx.subscribe(event_class, handler)`: Register an async event handler.
|
|
234
|
+
- `ctx.publish(event)`: Publish custom events across the event bus.
|
|
235
|
+
- `ctx.register_command(command)`: Add an action to the fuzzy command palette.
|
|
236
|
+
- `ctx.register_panel(panel)`: Mount a sidebar or bottom panel into the TUI grid.
|
|
237
|
+
- `ctx.get_service(name)`: Access host services (such as `"introspector"`, `"history"`, or `"config"`).
|
|
238
|
+
- `ctx.logger`: Context-bound structured logger (`logger.bind(plugin_id=...)`).
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## 📥 Submitting a Pull Request
|
|
243
|
+
|
|
244
|
+
1. Create a logical feature branch: `git checkout -b feature/my-cool-feature`.
|
|
245
|
+
2. Write tests covering your implementation.
|
|
246
|
+
3. Verify that `./scripts/check.sh` passes successfully (formatting, linting, type checks, and 84% coverage gate).
|
|
247
|
+
4. Commit your changes with clear, descriptive commit messages.
|
|
248
|
+
5. Push to your fork and open a Pull Request.
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
Happy coding! 📟
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Contributors 👥
|
|
2
|
+
|
|
3
|
+
A huge thank you to everyone who has contributed to **Registro**!
|
|
4
|
+
|
|
5
|
+
## ✨ Lead Maintainer
|
|
6
|
+
|
|
7
|
+
- **Salvatore Corvaglia** ([@salvatorecorvaglia](https://github.com/salvatorecorvaglia))
|
|
8
|
+
|
|
9
|
+
## 🌟 Contributors
|
|
10
|
+
|
|
11
|
+
_Want to contribute? Check out [CONTRIBUTING.md](./CONTRIBUTING.md) to get started!_
|