caland 0.5.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.
Files changed (129) hide show
  1. caland-0.5.0/.github/ISSUE_TEMPLATE/bug_report.yml +41 -0
  2. caland-0.5.0/.github/ISSUE_TEMPLATE/config.yml +5 -0
  3. caland-0.5.0/.github/PULL_REQUEST_TEMPLATE.md +13 -0
  4. caland-0.5.0/.github/workflows/ci.yml +74 -0
  5. caland-0.5.0/.github/workflows/docs.yml +54 -0
  6. caland-0.5.0/.github/workflows/release.yml +95 -0
  7. caland-0.5.0/.gitignore +15 -0
  8. caland-0.5.0/.pre-commit-config.yaml +8 -0
  9. caland-0.5.0/.zed/settings.json +36 -0
  10. caland-0.5.0/CHANGELOG.md +343 -0
  11. caland-0.5.0/CLAUDE.md +115 -0
  12. caland-0.5.0/CONTRIBUTING.md +91 -0
  13. caland-0.5.0/LICENSE +202 -0
  14. caland-0.5.0/PKG-INFO +219 -0
  15. caland-0.5.0/README.md +187 -0
  16. caland-0.5.0/SECURITY.md +32 -0
  17. caland-0.5.0/docs/USER_STORIES.md +70 -0
  18. caland-0.5.0/docs/architecture.md +28 -0
  19. caland-0.5.0/docs/browsing.md +103 -0
  20. caland-0.5.0/docs/connecting.md +64 -0
  21. caland-0.5.0/docs/contributing.md +65 -0
  22. caland-0.5.0/docs/img/audit.svg +211 -0
  23. caland-0.5.0/docs/img/auth.svg +209 -0
  24. caland-0.5.0/docs/img/browse.svg +206 -0
  25. caland-0.5.0/docs/img/confirm.svg +211 -0
  26. caland-0.5.0/docs/img/login-empty.svg +200 -0
  27. caland-0.5.0/docs/img/login.svg +199 -0
  28. caland-0.5.0/docs/img/perms.svg +209 -0
  29. caland-0.5.0/docs/img/search.svg +208 -0
  30. caland-0.5.0/docs/index.md +51 -0
  31. caland-0.5.0/docs/installation.md +88 -0
  32. caland-0.5.0/docs/keyboard.md +45 -0
  33. caland-0.5.0/docs/page.md +82 -0
  34. caland-0.5.0/docs/redesign/capture.py +101 -0
  35. caland-0.5.0/docs/security.md +28 -0
  36. caland-0.5.0/docs/stylesheets/extra.css +67 -0
  37. caland-0.5.0/docs/themes.md +24 -0
  38. caland-0.5.0/examples/databricks.yml +23 -0
  39. caland-0.5.0/mise.toml +60 -0
  40. caland-0.5.0/mkdocs.yml +99 -0
  41. caland-0.5.0/pyproject.toml +104 -0
  42. caland-0.5.0/spec/000-what-caland-is.md +92 -0
  43. caland-0.5.0/spec/001-connecting.md +47 -0
  44. caland-0.5.0/spec/002-browsing.md +51 -0
  45. caland-0.5.0/spec/003-secrets.md +74 -0
  46. caland-0.5.0/spec/004-scopes-and-permissions.md +55 -0
  47. caland-0.5.0/spec/005-bulk-and-audit.md +45 -0
  48. caland-0.5.0/spec/006-safety.md +65 -0
  49. caland-0.5.0/spec/007-name-and-release.md +79 -0
  50. caland-0.5.0/spec/008-the-page.md +231 -0
  51. caland-0.5.0/spec/009-who-reads-a-secret.md +71 -0
  52. caland-0.5.0/spec/README.md +73 -0
  53. caland-0.5.0/spec/mock/page.html +581 -0
  54. caland-0.5.0/src/caland/__init__.py +0 -0
  55. caland-0.5.0/src/caland/__main__.py +4 -0
  56. caland-0.5.0/src/caland/app.py +183 -0
  57. caland-0.5.0/src/caland/application/__init__.py +22 -0
  58. caland-0.5.0/src/caland/application/dotenv.py +52 -0
  59. caland-0.5.0/src/caland/application/loading.py +138 -0
  60. caland-0.5.0/src/caland/application/onboarding.py +106 -0
  61. caland-0.5.0/src/caland/application/read_model.py +76 -0
  62. caland-0.5.0/src/caland/application/workspace.py +177 -0
  63. caland-0.5.0/src/caland/domain/__init__.py +55 -0
  64. caland-0.5.0/src/caland/domain/errors.py +12 -0
  65. caland-0.5.0/src/caland/domain/host.py +15 -0
  66. caland-0.5.0/src/caland/domain/models.py +120 -0
  67. caland-0.5.0/src/caland/domain/permissions.py +72 -0
  68. caland-0.5.0/src/caland/domain/ports.py +55 -0
  69. caland-0.5.0/src/caland/domain/secret_store.py +30 -0
  70. caland-0.5.0/src/caland/formerly.py +50 -0
  71. caland-0.5.0/src/caland/infrastructure/__init__.py +23 -0
  72. caland-0.5.0/src/caland/infrastructure/bundle.py +84 -0
  73. caland-0.5.0/src/caland/infrastructure/config.py +13 -0
  74. caland-0.5.0/src/caland/infrastructure/connector.py +47 -0
  75. caland-0.5.0/src/caland/infrastructure/databricks.py +159 -0
  76. caland-0.5.0/src/caland/infrastructure/profiles.py +66 -0
  77. caland-0.5.0/src/caland/infrastructure/settings.py +76 -0
  78. caland-0.5.0/src/caland/interface/__init__.py +0 -0
  79. caland-0.5.0/src/caland/interface/modals.py +932 -0
  80. caland-0.5.0/src/caland/interface/screens/__init__.py +0 -0
  81. caland-0.5.0/src/caland/interface/screens/login.py +245 -0
  82. caland-0.5.0/src/caland/interface/screens/main.py +1107 -0
  83. caland-0.5.0/src/caland/interface/sorting.py +59 -0
  84. caland-0.5.0/src/caland/interface/theme.py +137 -0
  85. caland-0.5.0/src/caland/interface/web/__init__.py +134 -0
  86. caland-0.5.0/src/caland/interface/web/gate.py +107 -0
  87. caland-0.5.0/src/caland/interface/web/opening.py +54 -0
  88. caland-0.5.0/src/caland/interface/web/server.py +328 -0
  89. caland-0.5.0/src/caland/interface/web/static/page.css +110 -0
  90. caland-0.5.0/src/caland/interface/web/static/page.html +94 -0
  91. caland-0.5.0/src/caland/interface/web/static/page.js +425 -0
  92. caland-0.5.0/src/caland/interface/web/views.py +86 -0
  93. caland-0.5.0/src/caland/interface/widgets.py +473 -0
  94. caland-0.5.0/src/caland/styles.tcss +371 -0
  95. caland-0.5.0/tests/__snapshots__/test_snapshots/test_audit_screen.raw +195 -0
  96. caland-0.5.0/tests/__snapshots__/test_snapshots/test_browse_screen.raw +189 -0
  97. caland-0.5.0/tests/__snapshots__/test_snapshots/test_login_empty_state.raw +184 -0
  98. caland-0.5.0/tests/chrome.py +224 -0
  99. caland-0.5.0/tests/conftest.py +24 -0
  100. caland-0.5.0/tests/fakes.py +179 -0
  101. caland-0.5.0/tests/test_bundle.py +121 -0
  102. caland-0.5.0/tests/test_cache.py +35 -0
  103. caland-0.5.0/tests/test_choose.py +47 -0
  104. caland-0.5.0/tests/test_databricks.py +107 -0
  105. caland-0.5.0/tests/test_dotenv.py +33 -0
  106. caland-0.5.0/tests/test_formerly.py +173 -0
  107. caland-0.5.0/tests/test_help.py +41 -0
  108. caland-0.5.0/tests/test_host.py +22 -0
  109. caland-0.5.0/tests/test_loading.py +137 -0
  110. caland-0.5.0/tests/test_models.py +72 -0
  111. caland-0.5.0/tests/test_page_in_a_browser.py +425 -0
  112. caland-0.5.0/tests/test_permissions.py +67 -0
  113. caland-0.5.0/tests/test_profiles.py +76 -0
  114. caland-0.5.0/tests/test_session.py +112 -0
  115. caland-0.5.0/tests/test_settings.py +79 -0
  116. caland-0.5.0/tests/test_snapshots.py +54 -0
  117. caland-0.5.0/tests/test_sorting.py +45 -0
  118. caland-0.5.0/tests/test_ui_browse.py +476 -0
  119. caland-0.5.0/tests/test_ui_login.py +54 -0
  120. caland-0.5.0/tests/test_ui_modals.py +197 -0
  121. caland-0.5.0/tests/test_ui_permissions.py +100 -0
  122. caland-0.5.0/tests/test_ui_scope_filter.py +63 -0
  123. caland-0.5.0/tests/test_ui_small_terminal.py +90 -0
  124. caland-0.5.0/tests/test_ui_tools.py +186 -0
  125. caland-0.5.0/tests/test_web_gate.py +169 -0
  126. caland-0.5.0/tests/test_web_opening.py +81 -0
  127. caland-0.5.0/tests/test_web_run.py +115 -0
  128. caland-0.5.0/tests/test_web_server.py +582 -0
  129. caland-0.5.0/uv.lock +1699 -0
@@ -0,0 +1,41 @@
1
+ name: Bug report
2
+ description: Something isn't working
3
+ labels: [bug]
4
+ body:
5
+ - type: textarea
6
+ id: what
7
+ attributes:
8
+ label: What happened?
9
+ description: What did you do, what did you expect, and what happened instead?
10
+ validations:
11
+ required: true
12
+ - type: textarea
13
+ id: repro
14
+ attributes:
15
+ label: Steps to reproduce
16
+ placeholder: |
17
+ 1. Run `caland`
18
+ 2. ...
19
+ - type: input
20
+ id: version
21
+ attributes:
22
+ label: Caland version
23
+ placeholder: "0.1.0 (uvx caland --version, or `uv tool list`)"
24
+ - type: input
25
+ id: python
26
+ attributes:
27
+ label: Python version
28
+ placeholder: "3.12"
29
+ - type: input
30
+ id: os
31
+ attributes:
32
+ label: OS / terminal
33
+ placeholder: "macOS 15 · iTerm2"
34
+ - type: textarea
35
+ id: logs
36
+ attributes:
37
+ label: Error output
38
+ description: >
39
+ The TUI takes over the screen, so capture stderr with
40
+ `caland 2>err.log` and paste `err.log` here.
41
+ render: text
@@ -0,0 +1,5 @@
1
+ blank_issues_enabled: true
2
+ contact_links:
3
+ - name: Security vulnerability
4
+ url: https://github.com/kostavo-oss/caland/security/advisories/new
5
+ about: Please report security issues privately, not as a public issue.
@@ -0,0 +1,13 @@
1
+ <!-- Thanks for contributing to Caland! -->
2
+
3
+ ## What & why
4
+
5
+ <!-- What does this change, and why? Link any related issue (#123). -->
6
+
7
+ ## Checklist
8
+
9
+ - [ ] `uv run pytest` passes
10
+ - [ ] `uv run ruff check .` and `uv run ruff format .` are clean
11
+ - [ ] `uv run ty check` passes
12
+ - [ ] New behaviour has a test
13
+ - [ ] Business logic stays out of `interface/`; the SDK stays in `infrastructure/`
@@ -0,0 +1,74 @@
1
+ name: ci
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ lint:
10
+ name: lint + types
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+ - uses: astral-sh/setup-uv@v6
15
+ with:
16
+ enable-cache: true
17
+ - run: uv sync
18
+ - name: Lint (ruff)
19
+ run: uv run ruff check .
20
+ - name: Format check (ruff)
21
+ run: uv run ruff format --check .
22
+ - name: Type check (ty)
23
+ run: uv run ty check
24
+
25
+ test:
26
+ name: test (py${{ matrix.python-version }} · ${{ matrix.os }})
27
+ runs-on: ${{ matrix.os }}
28
+ strategy:
29
+ fail-fast: false
30
+ matrix:
31
+ os: [ubuntu-latest]
32
+ python-version: ["3.11", "3.12", "3.13", "3.14"]
33
+ include:
34
+ - os: macos-latest
35
+ python-version: "3.14"
36
+ steps:
37
+ - uses: actions/checkout@v4
38
+ - uses: astral-sh/setup-uv@v6
39
+ with:
40
+ enable-cache: true
41
+ - name: Tests (pytest)
42
+ run: uv run --python ${{ matrix.python-version }} pytest
43
+
44
+ docs:
45
+ name: docs (strict build + screenshots)
46
+ runs-on: ubuntu-latest
47
+ steps:
48
+ - uses: actions/checkout@v4
49
+ - uses: astral-sh/setup-uv@v6
50
+ with:
51
+ enable-cache: true
52
+ # mkdocs lives in the `docs` dependency group, which a plain sync skips
53
+ - run: uv sync --group docs
54
+ - name: Build the docs site (strict — broken links/nav fail)
55
+ run: uv run --group docs mkdocs build --strict
56
+ - name: Screenshot pipeline still runs
57
+ run: uv run --group docs python docs/redesign/capture.py
58
+
59
+ build:
60
+ name: build (wheel + sdist)
61
+ runs-on: ubuntu-latest
62
+ steps:
63
+ - uses: actions/checkout@v4
64
+ - uses: astral-sh/setup-uv@v6
65
+ - run: uv build
66
+ - name: Smoke-test the built wheel on 3.11
67
+ run: |
68
+ uv venv /tmp/smoke -p 3.11
69
+ uv pip install --python /tmp/smoke/bin/python dist/*.whl
70
+ /tmp/smoke/bin/python -c "import caland.app; print('import OK')"
71
+ - uses: actions/upload-artifact@v4
72
+ with:
73
+ name: dist
74
+ path: dist/*
@@ -0,0 +1,54 @@
1
+ name: docs
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ paths:
7
+ - "docs/**"
8
+ - "mkdocs.yml"
9
+ - ".github/workflows/docs.yml"
10
+ pull_request:
11
+ paths:
12
+ - "docs/**"
13
+ - "mkdocs.yml"
14
+ - ".github/workflows/docs.yml"
15
+ workflow_dispatch:
16
+
17
+ # allow the GITHUB_TOKEN to deploy to Pages, and serialise deploys
18
+ permissions:
19
+ contents: read
20
+ pages: write
21
+ id-token: write
22
+
23
+ concurrency:
24
+ group: pages
25
+ cancel-in-progress: false
26
+
27
+ jobs:
28
+ build:
29
+ name: build site
30
+ runs-on: ubuntu-latest
31
+ env:
32
+ NO_MKDOCS_2_WARNING: "1" # we pin mkdocs < 2; suppress the advisory banner
33
+ steps:
34
+ - uses: actions/checkout@v4
35
+ - uses: astral-sh/setup-uv@v6
36
+ with:
37
+ enable-cache: true
38
+ - name: Build (mkdocs)
39
+ run: uv run --group docs mkdocs build --strict
40
+ - uses: actions/upload-pages-artifact@v3
41
+ with:
42
+ path: site
43
+
44
+ deploy:
45
+ name: deploy to Pages
46
+ needs: build
47
+ if: github.event_name != 'pull_request' # build-only on PRs; deploy from main
48
+ runs-on: ubuntu-latest
49
+ environment:
50
+ name: github-pages
51
+ url: ${{ steps.deployment.outputs.page_url }}
52
+ steps:
53
+ - id: deployment
54
+ uses: actions/deploy-pages@v4
@@ -0,0 +1,95 @@
1
+ name: release
2
+
3
+ # Version-driven releases — the package version is the single source of truth.
4
+ # To cut a release:
5
+ # 1. uv version --bump {patch|minor|major} # edits pyproject.toml + uv.lock
6
+ # 2. move the CHANGELOG "[Unreleased]" notes under a "## [x.y.z]" heading
7
+ # 3. open a PR and merge to main
8
+ # On that merge this workflow checks whether the version is already released; if
9
+ # not, it builds the wheel + sdist, publishes to PyPI, and creates the matching
10
+ # `vX.Y.Z` git tag + GitHub release (notes pulled from the CHANGELOG). A push that
11
+ # doesn't change the version is a no-op.
12
+ #
13
+ # One-time setup — configure a PyPI Trusted Publisher (no API token needed) for
14
+ # repo: kostavo-oss/caland · workflow: release.yml · environment: pypi
15
+ # at https://pypi.org/manage/account/publishing/
16
+
17
+ on:
18
+ push:
19
+ branches: [main]
20
+ workflow_dispatch:
21
+
22
+ concurrency:
23
+ group: release
24
+ cancel-in-progress: false
25
+
26
+ jobs:
27
+ check:
28
+ name: detect version bump
29
+ runs-on: ubuntu-latest
30
+ outputs:
31
+ should_release: ${{ steps.decide.outputs.should_release }}
32
+ version: ${{ steps.read.outputs.version }}
33
+ steps:
34
+ - uses: actions/checkout@v4
35
+ with:
36
+ fetch-depth: 0 # need the tags to tell whether this version shipped
37
+ - id: read
38
+ name: Read the project version
39
+ run: |
40
+ v=$(python3 -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
41
+ echo "version=$v" >> "$GITHUB_OUTPUT"
42
+ echo "Project version: $v"
43
+ - id: decide
44
+ name: Already released?
45
+ run: |
46
+ version="${{ steps.read.outputs.version }}"
47
+ released=false
48
+ if git rev-parse "v$version" >/dev/null 2>&1; then
49
+ echo "git tag v$version already exists"; released=true
50
+ fi
51
+ code=$(curl -fsS -o /dev/null -w "%{http_code}" \
52
+ "https://pypi.org/pypi/caland/$version/json" || echo 000)
53
+ if [ "$code" = "200" ]; then
54
+ echo "caland $version is already on PyPI"; released=true
55
+ fi
56
+ if [ "$released" = "true" ]; then
57
+ echo "Nothing to release for $version."
58
+ echo "should_release=false" >> "$GITHUB_OUTPUT"
59
+ else
60
+ echo "Will release $version."
61
+ echo "should_release=true" >> "$GITHUB_OUTPUT"
62
+ fi
63
+
64
+ release:
65
+ name: publish v${{ needs.check.outputs.version }}
66
+ needs: check
67
+ if: needs.check.outputs.should_release == 'true'
68
+ runs-on: ubuntu-latest
69
+ environment: pypi
70
+ permissions:
71
+ contents: write # create the tag + GitHub release
72
+ id-token: write # PyPI Trusted Publishing (OIDC)
73
+ steps:
74
+ - uses: actions/checkout@v4
75
+ - uses: astral-sh/setup-uv@v6
76
+ - name: Build
77
+ run: uv build
78
+ - name: Publish to PyPI
79
+ run: uv publish --trusted-publishing always --check-url https://pypi.org/simple/
80
+ - name: Release notes from CHANGELOG
81
+ run: |
82
+ version="${{ needs.check.outputs.version }}"
83
+ awk -v v="$version" '
84
+ index($0, "## [" v "]") == 1 { f=1; next }
85
+ f && index($0, "## [") == 1 { exit }
86
+ f { print }
87
+ ' CHANGELOG.md > notes.md || true
88
+ - name: Create the tag + GitHub release
89
+ env:
90
+ GH_TOKEN: ${{ github.token }}
91
+ run: |
92
+ version="${{ needs.check.outputs.version }}"
93
+ args=(--title "v$version" --target "$GITHUB_SHA")
94
+ if [ -s notes.md ]; then args+=(--notes-file notes.md); else args+=(--generate-notes); fi
95
+ gh release create "v$version" dist/* "${args[@]}"
@@ -0,0 +1,15 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.pyc
4
+ .textual/
5
+ dist/
6
+ build/
7
+ *.egg-info/
8
+ .DS_Store
9
+ docs/redesign/*.svg.png
10
+
11
+ # generated reference renders (regenerate with docs/redesign/capture.py)
12
+ docs/redesign/*.svg
13
+
14
+ # mkdocs build output
15
+ site/
@@ -0,0 +1,8 @@
1
+ # `uvx pre-commit install` once, and ruff runs on every commit.
2
+ repos:
3
+ - repo: https://github.com/astral-sh/ruff-pre-commit
4
+ rev: v0.15.20 # the ruff uv.lock has, so a commit is checked as ci checks it
5
+ hooks:
6
+ - id: ruff-check
7
+ args: [--fix]
8
+ - id: ruff-format
@@ -0,0 +1,36 @@
1
+ {
2
+ "languages": {
3
+ "Python": {
4
+ "language_servers": ["ty", "ruff"],
5
+ "format_on_save": "on",
6
+ "code_actions_on_format": {
7
+ "source.organizeImports.ruff": true
8
+ },
9
+ "formatter": {
10
+ "language_server": { "name": "ruff" }
11
+ }
12
+ }
13
+ },
14
+ "lsp": {
15
+ "ruff": {
16
+ "binary": {
17
+ "path": ".venv/bin/ruff",
18
+ "arguments": ["server"]
19
+ }
20
+ },
21
+ "ty": {
22
+ "binary": {
23
+ "path": ".venv/bin/ty",
24
+ "arguments": ["server"]
25
+ }
26
+ }
27
+ },
28
+ "terminal": {
29
+ "detect_venv": {
30
+ "on": {
31
+ "directories": [".venv"],
32
+ "activate_script": "default"
33
+ }
34
+ }
35
+ }
36
+ }