dbxj 0.1.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.
- dbxj-0.1.0/.github/workflows/ci.yml +23 -0
- dbxj-0.1.0/.github/workflows/release.yml +17 -0
- dbxj-0.1.0/.gitignore +180 -0
- dbxj-0.1.0/.justfile +29 -0
- dbxj-0.1.0/.pre-commit-config.yaml +33 -0
- dbxj-0.1.0/CHANGELOG.md +12 -0
- dbxj-0.1.0/LICENSE +21 -0
- dbxj-0.1.0/PKG-INFO +101 -0
- dbxj-0.1.0/README.md +76 -0
- dbxj-0.1.0/docs/design.md +148 -0
- dbxj-0.1.0/docs/screenshot.svg +142 -0
- dbxj-0.1.0/docs/sessions.md +150 -0
- dbxj-0.1.0/pyproject.toml +57 -0
- dbxj-0.1.0/scripts/create_job_with_task.py +59 -0
- dbxj-0.1.0/scripts/create_jobs.py +19 -0
- dbxj-0.1.0/scripts/delete_jobs.py +15 -0
- dbxj-0.1.0/scripts/generate_sample_data.py +144 -0
- dbxj-0.1.0/scripts/job.py +11 -0
- dbxj-0.1.0/scripts/trigger_job_with_task.py +20 -0
- dbxj-0.1.0/src/dbxj/__init__.py +0 -0
- dbxj-0.1.0/src/dbxj/auth.py +131 -0
- dbxj-0.1.0/src/dbxj/body.py +530 -0
- dbxj-0.1.0/src/dbxj/dialogs.py +100 -0
- dbxj-0.1.0/src/dbxj/jobs.py +89 -0
- dbxj-0.1.0/src/dbxj/main.py +206 -0
- dbxj-0.1.0/src/dbxj/main.tcss +104 -0
- dbxj-0.1.0/src/dbxj/mock_workspace_client.py +203 -0
- dbxj-0.1.0/src/dbxj/sample_data/job_100000000000000.json +112 -0
- dbxj-0.1.0/src/dbxj/sample_data/job_100007919000000.json +112 -0
- dbxj-0.1.0/src/dbxj/sample_data/job_100015838000000.json +64 -0
- dbxj-0.1.0/src/dbxj/sample_data/job_100023757000000.json +90 -0
- dbxj-0.1.0/src/dbxj/sample_data/job_100031676000000.json +222 -0
- dbxj-0.1.0/src/dbxj/sample_data/job_100039595000000.json +24 -0
- dbxj-0.1.0/src/dbxj/sample_data/jobs.json +44 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_500000000000000.json +55 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_500000006363900.json +55 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_500000007613857.json +55 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_500000010463046.json +55 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_500000012320447.json +55 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_500000021057221.json +55 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_500000022726564.json +55 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_500000029348400.json +55 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_500000033531502.json +55 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_500000035086732.json +55 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_500000042336152.json +55 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_500000044098755.json +55 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_500000048300009.json +55 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_500000050235776.json +55 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_500000058538215.json +55 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_500000065934619.json +55 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_500000074305872.json +55 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_500000081730156.json +55 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_500000090522447.json +55 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_500000093602030.json +55 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_500000099538223.json +52 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_500000102534750.json +55 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_500000110308525.json +55 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_500000111816980.json +55 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_500000114356189.json +55 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_500000116138533.json +55 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_500000120413628.json +21 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_500000121904221.json +21 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_500000127804657.json +21 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_500000135946235.json +21 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_500000140333349.json +21 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_500000142021075.json +21 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_500000145023317.json +21 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_500000148675603.json +21 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_500000150290158.json +21 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_500000159118905.json +21 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_500000161442181.json +21 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_500000168044623.json +21 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_500000171263123.json +21 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_500000174626665.json +21 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_500000179866343.json +21 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_500000187823419.json +21 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_500000194167913.json +21 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_500000199873140.json +21 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_500000201175365.json +21 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_500000204761223.json +21 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_500000211355865.json +21 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_500000218642938.json +21 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_500000219850861.json +21 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_500000222186309.json +21 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_500000224860447.json +21 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_500000226058958.json +21 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_500000234309676.json +21 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_500000241009887.json +21 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_500000243507822.json +55 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_500000248153207.json +55 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000000000001.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000000000002.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000000000003.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000006363901.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000006363902.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000006363903.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000007613858.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000007613859.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000007613860.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000010463047.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000010463048.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000010463049.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000012320448.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000012320449.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000012320450.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000021057222.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000021057223.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000021057224.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000022726565.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000022726566.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000022726567.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000029348401.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000029348402.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000029348403.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000033531503.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000033531504.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000033531505.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000035086733.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000035086734.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000035086735.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000042336153.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000042336154.json +4 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000044098756.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000044098757.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000044098758.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000048300010.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000048300011.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000048300012.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000050235777.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000050235778.json +4 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000058538216.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000058538217.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000058538218.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000065934620.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000065934621.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000065934622.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000074305873.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000074305874.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000074305875.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000081730157.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000081730158.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000081730159.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000090522448.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000090522449.json +4 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000093602031.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000093602032.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000093602033.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000099538224.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000099538225.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000102534751.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000102534752.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000102534753.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000110308526.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000110308527.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000110308528.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000111816981.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000111816982.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000111816983.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000114356190.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000114356191.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000114356192.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000116138534.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000116138535.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000116138536.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000121904222.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000127804658.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000140333350.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000142021076.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000145023318.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000148675604.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000150290159.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000159118906.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000161442182.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000168044624.json +4 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000171263124.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000174626666.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000179866344.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000187823420.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000194167914.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000199873141.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000201175366.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000204761224.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000211355866.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000218642939.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000219850862.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000222186310.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000224860448.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000226058959.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000234309677.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000241009888.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000243507823.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000243507824.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000243507825.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000248153208.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000248153209.json +3 -0
- dbxj-0.1.0/src/dbxj/sample_data/run_output_500000248153210.json +3 -0
- dbxj-0.1.0/tests/test_smoke.py +61 -0
- dbxj-0.1.0/uv.lock +1524 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
check:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
strategy:
|
|
12
|
+
matrix:
|
|
13
|
+
python-version: ["3.11", "3.13"]
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
- uses: astral-sh/setup-uv@v6
|
|
17
|
+
with:
|
|
18
|
+
python-version: ${{ matrix.python-version }}
|
|
19
|
+
- run: uv sync --locked
|
|
20
|
+
- run: uv run ruff check
|
|
21
|
+
- run: uv run ruff format --check
|
|
22
|
+
- run: uv run mypy src
|
|
23
|
+
- run: uv run pytest
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags: ["v*"]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
publish:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
environment: pypi
|
|
11
|
+
permissions:
|
|
12
|
+
id-token: write # PyPI trusted publishing
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
- uses: astral-sh/setup-uv@v6
|
|
16
|
+
- run: uv build
|
|
17
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
dbxj-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
### Generated by gibo (https://github.com/simonwhitaker/gibo)
|
|
2
|
+
### https://raw.github.com/github/gitignore/6eeebe6f49678aacd8311ce079842c971b3ebe96/Python.gitignore
|
|
3
|
+
|
|
4
|
+
# Byte-compiled / optimized / DLL files
|
|
5
|
+
__pycache__/
|
|
6
|
+
*.py[cod]
|
|
7
|
+
*$py.class
|
|
8
|
+
|
|
9
|
+
# C extensions
|
|
10
|
+
*.so
|
|
11
|
+
|
|
12
|
+
# Distribution / packaging
|
|
13
|
+
.Python
|
|
14
|
+
build/
|
|
15
|
+
develop-eggs/
|
|
16
|
+
dist/
|
|
17
|
+
downloads/
|
|
18
|
+
eggs/
|
|
19
|
+
.eggs/
|
|
20
|
+
lib/
|
|
21
|
+
lib64/
|
|
22
|
+
parts/
|
|
23
|
+
sdist/
|
|
24
|
+
var/
|
|
25
|
+
wheels/
|
|
26
|
+
share/python-wheels/
|
|
27
|
+
*.egg-info/
|
|
28
|
+
.installed.cfg
|
|
29
|
+
*.egg
|
|
30
|
+
MANIFEST
|
|
31
|
+
|
|
32
|
+
# PyInstaller
|
|
33
|
+
# Usually these files are written by a python script from a template
|
|
34
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
35
|
+
*.manifest
|
|
36
|
+
*.spec
|
|
37
|
+
|
|
38
|
+
# Installer logs
|
|
39
|
+
pip-log.txt
|
|
40
|
+
pip-delete-this-directory.txt
|
|
41
|
+
|
|
42
|
+
# Unit test / coverage reports
|
|
43
|
+
htmlcov/
|
|
44
|
+
.tox/
|
|
45
|
+
.nox/
|
|
46
|
+
.coverage
|
|
47
|
+
.coverage.*
|
|
48
|
+
.cache
|
|
49
|
+
nosetests.xml
|
|
50
|
+
coverage.xml
|
|
51
|
+
*.cover
|
|
52
|
+
*.py,cover
|
|
53
|
+
.hypothesis/
|
|
54
|
+
.pytest_cache/
|
|
55
|
+
cover/
|
|
56
|
+
|
|
57
|
+
# Translations
|
|
58
|
+
*.mo
|
|
59
|
+
*.pot
|
|
60
|
+
|
|
61
|
+
# Django stuff:
|
|
62
|
+
*.log
|
|
63
|
+
local_settings.py
|
|
64
|
+
db.sqlite3
|
|
65
|
+
db.sqlite3-journal
|
|
66
|
+
|
|
67
|
+
# Flask stuff:
|
|
68
|
+
instance/
|
|
69
|
+
.webassets-cache
|
|
70
|
+
|
|
71
|
+
# Scrapy stuff:
|
|
72
|
+
.scrapy
|
|
73
|
+
|
|
74
|
+
# Sphinx documentation
|
|
75
|
+
docs/_build/
|
|
76
|
+
|
|
77
|
+
# PyBuilder
|
|
78
|
+
.pybuilder/
|
|
79
|
+
target/
|
|
80
|
+
|
|
81
|
+
# Jupyter Notebook
|
|
82
|
+
.ipynb_checkpoints
|
|
83
|
+
|
|
84
|
+
# IPython
|
|
85
|
+
profile_default/
|
|
86
|
+
ipython_config.py
|
|
87
|
+
|
|
88
|
+
# pyenv
|
|
89
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
90
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
91
|
+
# .python-version
|
|
92
|
+
|
|
93
|
+
# pipenv
|
|
94
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
95
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
96
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
97
|
+
# install all needed dependencies.
|
|
98
|
+
#Pipfile.lock
|
|
99
|
+
|
|
100
|
+
# UV
|
|
101
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
102
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
103
|
+
# commonly ignored for libraries.
|
|
104
|
+
#uv.lock
|
|
105
|
+
|
|
106
|
+
# poetry
|
|
107
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
108
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
109
|
+
# commonly ignored for libraries.
|
|
110
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
111
|
+
#poetry.lock
|
|
112
|
+
|
|
113
|
+
# pdm
|
|
114
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
115
|
+
#pdm.lock
|
|
116
|
+
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
|
117
|
+
# in version control.
|
|
118
|
+
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
|
|
119
|
+
.pdm.toml
|
|
120
|
+
.pdm-python
|
|
121
|
+
.pdm-build/
|
|
122
|
+
|
|
123
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
124
|
+
__pypackages__/
|
|
125
|
+
|
|
126
|
+
# Celery stuff
|
|
127
|
+
celerybeat-schedule
|
|
128
|
+
celerybeat.pid
|
|
129
|
+
|
|
130
|
+
# SageMath parsed files
|
|
131
|
+
*.sage.py
|
|
132
|
+
|
|
133
|
+
# Environments
|
|
134
|
+
.env
|
|
135
|
+
.venv
|
|
136
|
+
env/
|
|
137
|
+
venv/
|
|
138
|
+
ENV/
|
|
139
|
+
env.bak/
|
|
140
|
+
venv.bak/
|
|
141
|
+
|
|
142
|
+
# Spyder project settings
|
|
143
|
+
.spyderproject
|
|
144
|
+
.spyproject
|
|
145
|
+
|
|
146
|
+
# Rope project settings
|
|
147
|
+
.ropeproject
|
|
148
|
+
|
|
149
|
+
# mkdocs documentation
|
|
150
|
+
/site
|
|
151
|
+
|
|
152
|
+
# mypy
|
|
153
|
+
.mypy_cache/
|
|
154
|
+
.dmypy.json
|
|
155
|
+
dmypy.json
|
|
156
|
+
|
|
157
|
+
# Pyre type checker
|
|
158
|
+
.pyre/
|
|
159
|
+
|
|
160
|
+
# pytype static type analyzer
|
|
161
|
+
.pytype/
|
|
162
|
+
|
|
163
|
+
# Cython debug symbols
|
|
164
|
+
cython_debug/
|
|
165
|
+
|
|
166
|
+
# PyCharm
|
|
167
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
168
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
169
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
170
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
171
|
+
#.idea/
|
|
172
|
+
|
|
173
|
+
# PyPI configuration file
|
|
174
|
+
.pypirc
|
|
175
|
+
|
|
176
|
+
# dev
|
|
177
|
+
tmp
|
|
178
|
+
.python-version
|
|
179
|
+
.DS_Store
|
|
180
|
+
cached
|
dbxj-0.1.0/.justfile
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# just run <profile>, or just run for the default profile
|
|
2
|
+
run profile="":
|
|
3
|
+
uv run dbxj {{ if profile != "" { "--profile " + profile } else { "" } }}
|
|
4
|
+
|
|
5
|
+
run_offline:
|
|
6
|
+
uv run dbxj --offline
|
|
7
|
+
|
|
8
|
+
setup_python:
|
|
9
|
+
uv venv --python 3.11
|
|
10
|
+
uv sync
|
|
11
|
+
|
|
12
|
+
upgrade_and_sync:
|
|
13
|
+
uv upgrade
|
|
14
|
+
uv sync
|
|
15
|
+
|
|
16
|
+
sync: # adds all packages to .venv
|
|
17
|
+
uv sync
|
|
18
|
+
|
|
19
|
+
create_jobs:
|
|
20
|
+
uv run ./scripts/create_jobs.py --count 10
|
|
21
|
+
|
|
22
|
+
create_jobs_50:
|
|
23
|
+
uv run ./scripts/create_jobs.py --count 50
|
|
24
|
+
|
|
25
|
+
delete_jobs:
|
|
26
|
+
uv run ./scripts/delete_jobs.py
|
|
27
|
+
|
|
28
|
+
install:
|
|
29
|
+
uv tool install --reinstall .
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
3
|
+
rev: v5.0.0
|
|
4
|
+
hooks:
|
|
5
|
+
- id: trailing-whitespace
|
|
6
|
+
- id: end-of-file-fixer
|
|
7
|
+
- id: check-docstring-first
|
|
8
|
+
- id: debug-statements
|
|
9
|
+
- id: check-yaml
|
|
10
|
+
- id: check-ast
|
|
11
|
+
- id: check-json
|
|
12
|
+
- id: pretty-format-json
|
|
13
|
+
args: [--autofix]
|
|
14
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
15
|
+
# Ruff version.
|
|
16
|
+
rev: v0.14.0
|
|
17
|
+
hooks:
|
|
18
|
+
# Run the linter.
|
|
19
|
+
- id: ruff
|
|
20
|
+
args: [--fix]
|
|
21
|
+
# Run the formatter.
|
|
22
|
+
- id: ruff-format
|
|
23
|
+
- repo: local
|
|
24
|
+
hooks:
|
|
25
|
+
- id: mypy
|
|
26
|
+
name: mypy
|
|
27
|
+
entry: mypy
|
|
28
|
+
language: system
|
|
29
|
+
types: [python]
|
|
30
|
+
args:
|
|
31
|
+
- --install-types
|
|
32
|
+
- --non-interactive
|
|
33
|
+
exclude: 'scripts/.*'
|
dbxj-0.1.0/CHANGELOG.md
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0
|
|
4
|
+
|
|
5
|
+
First public release.
|
|
6
|
+
|
|
7
|
+
- Browse jobs, their runs, the tasks in a run and task output/error traces
|
|
8
|
+
- `Last 5` column with the results of each job's recent runs
|
|
9
|
+
- Filter jobs by name, trigger a job, open the selection in the browser
|
|
10
|
+
- Profile selection (`--profile` / `DATABRICKS_CONFIG_PROFILE`) and in-app re-login when the
|
|
11
|
+
session has expired
|
|
12
|
+
- `--offline` / `--demo` mode with bundled demo data
|
dbxj-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024-2026 Travis Dent
|
|
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.
|
dbxj-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: dbxj
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A keyboard-driven terminal UI for browsing Databricks jobs, runs and task logs
|
|
5
|
+
Project-URL: Homepage, https://github.com/pugillum/dbxj
|
|
6
|
+
Project-URL: Issues, https://github.com/pugillum/dbxj/issues
|
|
7
|
+
Author: Travis Dent
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: cli,databricks,jobs,textual,tui
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Software Development
|
|
20
|
+
Classifier: Topic :: Utilities
|
|
21
|
+
Requires-Python: >=3.11
|
|
22
|
+
Requires-Dist: databricks-sdk>=0.41.0
|
|
23
|
+
Requires-Dist: textual>=1.0.0
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
|
|
26
|
+
# dbxj
|
|
27
|
+
|
|
28
|
+
A keyboard-driven terminal UI for browsing Databricks jobs, their runs and task logs, in the
|
|
29
|
+
spirit of k9s and lazygit.
|
|
30
|
+
|
|
31
|
+

|
|
32
|
+
|
|
33
|
+
- See every job with the results of its last 5 runs at a glance
|
|
34
|
+
- Drill into a job's runs, a run's tasks and a task's output or error trace
|
|
35
|
+
- Filter jobs by name, trigger a job, open anything in the browser
|
|
36
|
+
- Switch workspaces with `--profile`; re-login happens in-app when your session expires
|
|
37
|
+
|
|
38
|
+
# Install
|
|
39
|
+
|
|
40
|
+
```sh
|
|
41
|
+
uv tool install dbxj # or: pipx install dbxj
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Requirements:
|
|
45
|
+
|
|
46
|
+
- Python 3.11+
|
|
47
|
+
- A profile in `~/.databrickscfg`, e.g. from `databricks auth login --host <workspace-url>`
|
|
48
|
+
- The [Databricks CLI](https://docs.databricks.com/dev-tools/cli/install.html) on your `PATH`, used
|
|
49
|
+
to log in again when your session has expired
|
|
50
|
+
|
|
51
|
+
Try it without a workspace:
|
|
52
|
+
|
|
53
|
+
```sh
|
|
54
|
+
dbxj --demo
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Upgrade with `uv tool upgrade dbxj` and remove with `uv tool uninstall dbxj`.
|
|
58
|
+
|
|
59
|
+
# Usage
|
|
60
|
+
|
|
61
|
+
```sh
|
|
62
|
+
dbxj --profile <profile name> # a profile from ~/.databrickscfg
|
|
63
|
+
dbxj --demo # bundled demo data, no workspace needed (alias: --offline)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
If `--profile` isn't given, `DATABRICKS_CONFIG_PROFILE` or the SDK's default auth is used. On start
|
|
67
|
+
the login is checked; if it's expired a dialog appears and `databricks auth login` opens your browser,
|
|
68
|
+
then the app continues once you've logged in.
|
|
69
|
+
|
|
70
|
+
| Key | Action |
|
|
71
|
+
| --- | --- |
|
|
72
|
+
| `r` | Refresh the current view |
|
|
73
|
+
| `q` | Quit |
|
|
74
|
+
| `?` | Show all key bindings (`esc` closes) |
|
|
75
|
+
| `/` | Filter jobs by name (kept while you drill in) |
|
|
76
|
+
| `t` | Trigger the selected job (asks to confirm) |
|
|
77
|
+
| `o` | Open the selected job, run or task in the browser |
|
|
78
|
+
| `enter` / `esc` | Drill into / back out of jobs, runs and tasks |
|
|
79
|
+
| `j` / `k`, `d` / `u` | Down / up, page down / up |
|
|
80
|
+
|
|
81
|
+
# Development
|
|
82
|
+
|
|
83
|
+
Requires [uv](https://docs.astral.sh/uv/) and optionally [just](https://github.com/casey/just).
|
|
84
|
+
|
|
85
|
+
```sh
|
|
86
|
+
uv sync # create .venv
|
|
87
|
+
uv run pre-commit install
|
|
88
|
+
uv run dbxj --demo # or: just run_offline / just run <profile>
|
|
89
|
+
uv run pytest
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
- In demo mode, data is read from `./cached` if that directory exists, otherwise from
|
|
93
|
+
`src/dbxj/sample_data` (regenerate with `uv run scripts/generate_sample_data.py`)
|
|
94
|
+
- `scripts/` has helpers to create, trigger and delete test jobs in a workspace
|
|
95
|
+
- `docs/design.md` describes how the app is put together
|
|
96
|
+
|
|
97
|
+
Releases are published to PyPI by pushing a `v*` tag.
|
|
98
|
+
|
|
99
|
+
# License
|
|
100
|
+
|
|
101
|
+
MIT
|
dbxj-0.1.0/README.md
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# dbxj
|
|
2
|
+
|
|
3
|
+
A keyboard-driven terminal UI for browsing Databricks jobs, their runs and task logs, in the
|
|
4
|
+
spirit of k9s and lazygit.
|
|
5
|
+
|
|
6
|
+

|
|
7
|
+
|
|
8
|
+
- See every job with the results of its last 5 runs at a glance
|
|
9
|
+
- Drill into a job's runs, a run's tasks and a task's output or error trace
|
|
10
|
+
- Filter jobs by name, trigger a job, open anything in the browser
|
|
11
|
+
- Switch workspaces with `--profile`; re-login happens in-app when your session expires
|
|
12
|
+
|
|
13
|
+
# Install
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
uv tool install dbxj # or: pipx install dbxj
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Requirements:
|
|
20
|
+
|
|
21
|
+
- Python 3.11+
|
|
22
|
+
- A profile in `~/.databrickscfg`, e.g. from `databricks auth login --host <workspace-url>`
|
|
23
|
+
- The [Databricks CLI](https://docs.databricks.com/dev-tools/cli/install.html) on your `PATH`, used
|
|
24
|
+
to log in again when your session has expired
|
|
25
|
+
|
|
26
|
+
Try it without a workspace:
|
|
27
|
+
|
|
28
|
+
```sh
|
|
29
|
+
dbxj --demo
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Upgrade with `uv tool upgrade dbxj` and remove with `uv tool uninstall dbxj`.
|
|
33
|
+
|
|
34
|
+
# Usage
|
|
35
|
+
|
|
36
|
+
```sh
|
|
37
|
+
dbxj --profile <profile name> # a profile from ~/.databrickscfg
|
|
38
|
+
dbxj --demo # bundled demo data, no workspace needed (alias: --offline)
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
If `--profile` isn't given, `DATABRICKS_CONFIG_PROFILE` or the SDK's default auth is used. On start
|
|
42
|
+
the login is checked; if it's expired a dialog appears and `databricks auth login` opens your browser,
|
|
43
|
+
then the app continues once you've logged in.
|
|
44
|
+
|
|
45
|
+
| Key | Action |
|
|
46
|
+
| --- | --- |
|
|
47
|
+
| `r` | Refresh the current view |
|
|
48
|
+
| `q` | Quit |
|
|
49
|
+
| `?` | Show all key bindings (`esc` closes) |
|
|
50
|
+
| `/` | Filter jobs by name (kept while you drill in) |
|
|
51
|
+
| `t` | Trigger the selected job (asks to confirm) |
|
|
52
|
+
| `o` | Open the selected job, run or task in the browser |
|
|
53
|
+
| `enter` / `esc` | Drill into / back out of jobs, runs and tasks |
|
|
54
|
+
| `j` / `k`, `d` / `u` | Down / up, page down / up |
|
|
55
|
+
|
|
56
|
+
# Development
|
|
57
|
+
|
|
58
|
+
Requires [uv](https://docs.astral.sh/uv/) and optionally [just](https://github.com/casey/just).
|
|
59
|
+
|
|
60
|
+
```sh
|
|
61
|
+
uv sync # create .venv
|
|
62
|
+
uv run pre-commit install
|
|
63
|
+
uv run dbxj --demo # or: just run_offline / just run <profile>
|
|
64
|
+
uv run pytest
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
- In demo mode, data is read from `./cached` if that directory exists, otherwise from
|
|
68
|
+
`src/dbxj/sample_data` (regenerate with `uv run scripts/generate_sample_data.py`)
|
|
69
|
+
- `scripts/` has helpers to create, trigger and delete test jobs in a workspace
|
|
70
|
+
- `docs/design.md` describes how the app is put together
|
|
71
|
+
|
|
72
|
+
Releases are published to PyPI by pushing a `v*` tag.
|
|
73
|
+
|
|
74
|
+
# License
|
|
75
|
+
|
|
76
|
+
MIT
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# Design
|
|
2
|
+
|
|
3
|
+
`dbxj` is a terminal UI for browsing Databricks jobs, their runs, the tasks in a run and task output.
|
|
4
|
+
It's built on [Textual](https://textual.textualize.io/) and the
|
|
5
|
+
[Databricks Python SDK](https://github.com/databricks/databricks-sdk-py).
|
|
6
|
+
|
|
7
|
+
## Style: minimal TUI like lazygit and k9s
|
|
8
|
+
|
|
9
|
+
`dbxj` should look and feel like [lazygit](https://github.com/jesseduffield/lazygit) and
|
|
10
|
+
[k9s](https://k9scli.io/), not like a typical Textual app. Every UI change must follow these rules:
|
|
11
|
+
|
|
12
|
+
- **Keyboard only.** Every action is a single key (`t`, `o`, `r`, `/`, ...) with vim-style
|
|
13
|
+
navigation (`j`/`k`, `d`/`u`). There are no buttons, checkboxes or clickable widgets, and nothing
|
|
14
|
+
that needs the mouse.
|
|
15
|
+
- **Plain bordered panels.** Use `solid white` borders on the default background. Put titles and key
|
|
16
|
+
hints in the border (`border_title` / `border_subtitle`, e.g. `y confirm · n cancel`), not in extra
|
|
17
|
+
widgets. Don't add Textual's stock look: no themed dialog boxes, header bars or coloured
|
|
18
|
+
backgrounds.
|
|
19
|
+
- **Popups are overlays.** Dialogs, confirmations and help are a small centred box on a
|
|
20
|
+
`ModalScreen` over the current view (see `dialogs.py`). `esc` always closes them. `y`/`n` answers
|
|
21
|
+
confirmations. Textual's default dimming of the screen behind a modal is turned off
|
|
22
|
+
(`background: transparent`). The box itself is opaque (`background: $background`), so the table
|
|
23
|
+
doesn't show through.
|
|
24
|
+
- **Dense, text-first content.** Prefer tables and compact symbols (`✓ ✗ ● ⊘`) with sparing colour
|
|
25
|
+
(green/red/yellow/grey for state, yellow for keys). Don't use decorative padding or spacing.
|
|
26
|
+
- **One bottom bar.** `StatusBar` (`main.py`) holds the key hints on the left and a brief status
|
|
27
|
+
message on the right, like lazygit. The hints are only the essential keys (`r` refresh, `q` quit,
|
|
28
|
+
`?` help). Every other binding uses `show=False` and is listed in the `?` help overlay. The `^p`
|
|
29
|
+
command palette hint is hidden too, though `ctrl+p` still works.
|
|
30
|
+
- **Feedback is a status message, never an overlay.** `DatabricksTUI.notify` is overridden, so every
|
|
31
|
+
`notify()` call writes one line into the bottom bar instead of showing Textual's toast cards.
|
|
32
|
+
Messages are green / yellow / red for information / warning / error and clear themselves after
|
|
33
|
+
4s, or 8s for errors. Use a popup only when the user has to make a decision.
|
|
34
|
+
- **No scrollbars.** A global `* { scrollbar-size: 0 0; }` in `main.tcss` hides them. In a narrow or
|
|
35
|
+
short window the content is clipped like in k9s and scrolled with keys (`j`/`k`, `d`/`u`, and
|
|
36
|
+
`h`/`l` or arrows to pan wide tables).
|
|
37
|
+
- **Restyle stock Textual widgets rather than accepting their defaults.** Check new widgets against
|
|
38
|
+
these rules. Textual's `DEFAULT_CSS` is overridden by `main.tcss`.
|
|
39
|
+
|
|
40
|
+
## Principles
|
|
41
|
+
|
|
42
|
+
- **`q` / `esc` always work**, including while waiting on something slow (e.g. browser login).
|
|
43
|
+
- **Destructive or costly actions ask first**, e.g. `t` triggering a job shows a y/n popup.
|
|
44
|
+
- **Never block the UI thread**: API calls run in Textual workers (`@work(thread=True)` or
|
|
45
|
+
`asyncio.to_thread`).
|
|
46
|
+
|
|
47
|
+
## Layout
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
src/dbxj/
|
|
51
|
+
main.py entry point, arg parsing, App, MainScreen, SplashScreen, Header
|
|
52
|
+
body.py Body widget: table/filter/details panels and the navigation state machine
|
|
53
|
+
jobs.py JobsAPI: cached job list
|
|
54
|
+
auth.py profiles, connection check, ReauthScreen
|
|
55
|
+
dialogs.py HelpScreen (? overlay), ConfirmScreen (y/n popup)
|
|
56
|
+
mock_workspace_client.py offline client backed by ./cached/*.json or sample_data/*.json
|
|
57
|
+
sample_data/ fake workspace for --offline/--demo (scripts/generate_sample_data.py)
|
|
58
|
+
main.tcss styles
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Startup flow
|
|
62
|
+
|
|
63
|
+
1. `main()` parses `--profile` (default `DATABRICKS_CONFIG_PROFILE`), `--offline` (alias `--demo`)
|
|
64
|
+
and `--version`. An unknown
|
|
65
|
+
profile exits with the list from `~/.databrickscfg` (or `DATABRICKS_CONFIG_FILE`).
|
|
66
|
+
2. `DatabricksTUI` pushes `MainScreen`, then `SplashScreen` on top.
|
|
67
|
+
3. `MainScreen.setup_client` (async worker) calls `auth.connect(profile)` in a thread. It builds a
|
|
68
|
+
`WorkspaceClient` and calls `current_user.me()` / `get_workspace_id()` to prove the login is valid.
|
|
69
|
+
4. On `AuthError` it awaits `ReauthScreen` (see below). If login succeeds it loops back to step 3;
|
|
70
|
+
if the user quits, the app exits.
|
|
71
|
+
5. `Header` and `Body` are initialised. When the first job list loads, `Body` posts
|
|
72
|
+
`JobsInitialised` and the app pops the splash screen.
|
|
73
|
+
|
|
74
|
+
Offline mode skips 3–4 and uses `get_mock_client()`. It reads `./cached` if that directory exists
|
|
75
|
+
(for local development against captured data), otherwise the fake workspace bundled in
|
|
76
|
+
`dbxj/sample_data`, so `dbxj --demo` works after a plain install. `tests/test_smoke.py` drives the
|
|
77
|
+
app against that data.
|
|
78
|
+
|
|
79
|
+
## Re-authentication
|
|
80
|
+
|
|
81
|
+
All profiles are expected to use `auth_type = databricks-cli` (OAuth U2M), so re-auth shells out to
|
|
82
|
+
`databricks auth login --profile <p>`. The CLI opens the browser and blocks until the OAuth callback.
|
|
83
|
+
|
|
84
|
+
`ReauthScreen` (a `ModalScreen[bool]`):
|
|
85
|
+
- starts the login as soon as it mounts, with output captured so it can't corrupt the TUI
|
|
86
|
+
- `q` / `esc` terminates the login process and dismisses with `False`
|
|
87
|
+
- on failure it shows stderr; `enter` / `r` retries
|
|
88
|
+
- any exception from `connect` is treated as an auth failure; the error text is shown so network
|
|
89
|
+
problems can be told apart
|
|
90
|
+
|
|
91
|
+
## Navigation state machine (`body.py`)
|
|
92
|
+
|
|
93
|
+
`BodyState`: `JOBS` → `JOB_RUNS` → `JOB_RUN` (tasks) → `TASK` (logs). `enter` drills in, `esc` goes
|
|
94
|
+
back. Each level clears the shared `DataTablePlus` and reloads it in a worker. `TASK` hides the table
|
|
95
|
+
and shows `DetailsPanel`, a scrollable view of the run output with ANSI colours.
|
|
96
|
+
|
|
97
|
+
`FilterState`: `NONE` → `TYPING` (`/` opens the filter input; the table filters live) → `SELECTING`
|
|
98
|
+
(`enter` moves focus back to the table and saves the text in `Body.job_filter`). Only available in
|
|
99
|
+
`JOBS`. Drilling into a job only hides the filter panel; `esc` back to `JOBS` shows it again and
|
|
100
|
+
reapplies `job_filter`. `esc` in `JOBS` clears the filter.
|
|
101
|
+
|
|
102
|
+
Tables key their rows by job / run id. Going back with `esc` puts the cursor back on the row that
|
|
103
|
+
was drilled into (`current_job_key`, `current_run_key`, `current_task_run_id`), via the loaders'
|
|
104
|
+
`select_key` argument.
|
|
105
|
+
|
|
106
|
+
## Actions
|
|
107
|
+
|
|
108
|
+
Bound on `MainScreen` and delegated to `Body`:
|
|
109
|
+
|
|
110
|
+
- `r` → `Body.refresh_view`: reloads the current view (jobs with the cache cleared and the filter
|
|
111
|
+
kept, runs, tasks, or the task logs), keeping the cursor on the same row.
|
|
112
|
+
- `t` → `Body.trigger_job`: jobs view only. It shows a `ConfirmScreen`, then `jobs.run_now` in a
|
|
113
|
+
worker. It doesn't wait on the returned `Wait`; the new run id comes from `.bind()["run_id"]`. A
|
|
114
|
+
status message in the bottom bar reports the result, and the job's `Last 5` cell is refetched.
|
|
115
|
+
- `o` → `Body.open_in_browser` → `App.open_url`. Jobs use `{host}/jobs/{id}?o={workspace_id}`. Runs
|
|
116
|
+
and tasks use the API's `run_page_url`, with a fallback of `{host}/jobs/{job}/runs/{run}?o=...`.
|
|
117
|
+
- `?` → `HelpScreen`, a static key list in `dialogs.HELP_SECTIONS`. Most navigation keys are
|
|
118
|
+
handled in `Body.on_key` rather than as `Binding`s, so they can't be generated.
|
|
119
|
+
|
|
120
|
+
Single-letter screen bindings don't fire while typing in the filter, because `Input` consumes
|
|
121
|
+
printable keys.
|
|
122
|
+
|
|
123
|
+
## Last 5 runs column
|
|
124
|
+
|
|
125
|
+
The jobs table has a `Last 5` column, ordered oldest → newest: `✓` success, `✗` failed, `●` running/
|
|
126
|
+
queued, `⊘` cancelled/skipped, `-` no run, `?` fetch failed, `…` loading.
|
|
127
|
+
|
|
128
|
+
The public Jobs API can't return recent runs for many jobs in one call. `jobs.list` has no runs
|
|
129
|
+
option and `jobs.list_runs` takes a single `job_id`. The portal uses an internal GraphQL route
|
|
130
|
+
(`/graphql/LakeflowListAssetsQuery`) that only accepts browser session cookies: bearer tokens get a
|
|
131
|
+
303 to the login page (tested 2026-11-14), so we can't use it. Instead `Body.load_recent_runs` makes one
|
|
132
|
+
`list_runs(job_id, limit=5)` per job on an 8-thread pool. The table renders at once and cells fill in
|
|
133
|
+
as results arrive. The SDK iterator follows `next_page_token`, so `JobsAPI.get_recent_runs` caps the
|
|
134
|
+
results with `islice`. The column has a fixed width (`2 * RECENT_RUNS - 1`): `update_cell` doesn't resize a column, so
|
|
135
|
+
sizing it from the `…` placeholders cut cells off at 3 symbols. Results (including failures) are cached in `JobsAPI.recent_runs`, so filtering
|
|
136
|
+
doesn't refetch. `r` clears the cache.
|
|
137
|
+
|
|
138
|
+
## Packaging
|
|
139
|
+
|
|
140
|
+
- The package and command are both `dbxj` (`[project.scripts] dbxj = "dbxj.main:main"`), installed
|
|
141
|
+
with `uv tool install .`.
|
|
142
|
+
- Runtime deps are only `databricks-sdk` and `textual`; tooling lives in the `dev` group.
|
|
143
|
+
- `just run [profile]`, `just run_offline`, `just install`.
|
|
144
|
+
|
|
145
|
+
## Known gaps
|
|
146
|
+
|
|
147
|
+
- Offline mode needs a `./cached/` directory of JSON fixtures that isn't in the repo.
|
|
148
|
+
- Run/task lists are capped at 25 and not paginated.
|