obd-tui 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.
Files changed (62) hide show
  1. obd_tui-0.1.0/.github/FUNDING.yml +4 -0
  2. obd_tui-0.1.0/.github/ISSUE_TEMPLATE/bug_report.yml +77 -0
  3. obd_tui-0.1.0/.github/ISSUE_TEMPLATE/config.yml +11 -0
  4. obd_tui-0.1.0/.github/ISSUE_TEMPLATE/feature_request.yml +37 -0
  5. obd_tui-0.1.0/.github/dependabot.yml +70 -0
  6. obd_tui-0.1.0/.github/pull_request_template.md +37 -0
  7. obd_tui-0.1.0/.github/workflows/ci.yml +313 -0
  8. obd_tui-0.1.0/.github/workflows/dependabot-rewrite.yml +94 -0
  9. obd_tui-0.1.0/.gitignore +27 -0
  10. obd_tui-0.1.0/.multicz/state.json +13 -0
  11. obd_tui-0.1.0/.pre-commit-config.yaml +90 -0
  12. obd_tui-0.1.0/CHANGELOG.md +20 -0
  13. obd_tui-0.1.0/CODE_OF_CONDUCT.md +83 -0
  14. obd_tui-0.1.0/CONTRIBUTING.md +62 -0
  15. obd_tui-0.1.0/LICENSE +21 -0
  16. obd_tui-0.1.0/PKG-INFO +127 -0
  17. obd_tui-0.1.0/README.md +96 -0
  18. obd_tui-0.1.0/SECURITY.md +43 -0
  19. obd_tui-0.1.0/docs/architecture.md +68 -0
  20. obd_tui-0.1.0/docs/index.md +53 -0
  21. obd_tui-0.1.0/docs/panels.md +68 -0
  22. obd_tui-0.1.0/docs/stability.md +32 -0
  23. obd_tui-0.1.0/docs/usage.md +82 -0
  24. obd_tui-0.1.0/multicz.toml +69 -0
  25. obd_tui-0.1.0/pyproject.toml +101 -0
  26. obd_tui-0.1.0/scripts/add_license_header.py +135 -0
  27. obd_tui-0.1.0/scripts/rewrite-dependabot-commit.sh +67 -0
  28. obd_tui-0.1.0/src/obd_tui/__init__.py +8 -0
  29. obd_tui-0.1.0/src/obd_tui/__main__.py +11 -0
  30. obd_tui-0.1.0/src/obd_tui/app.py +162 -0
  31. obd_tui-0.1.0/src/obd_tui/cli.py +46 -0
  32. obd_tui-0.1.0/src/obd_tui/models/__init__.py +17 -0
  33. obd_tui-0.1.0/src/obd_tui/models/adapter.py +54 -0
  34. obd_tui-0.1.0/src/obd_tui/models/commands.py +62 -0
  35. obd_tui-0.1.0/src/obd_tui/models/vehicle.py +127 -0
  36. obd_tui-0.1.0/src/obd_tui/services/__init__.py +11 -0
  37. obd_tui-0.1.0/src/obd_tui/services/connection.py +163 -0
  38. obd_tui-0.1.0/src/obd_tui/services/detection.py +85 -0
  39. obd_tui-0.1.0/src/obd_tui/services/polling.py +154 -0
  40. obd_tui-0.1.0/src/obd_tui/services/session.py +107 -0
  41. obd_tui-0.1.0/src/obd_tui/views/__init__.py +9 -0
  42. obd_tui-0.1.0/src/obd_tui/views/format.py +46 -0
  43. obd_tui-0.1.0/src/obd_tui/views/gauges.py +30 -0
  44. obd_tui-0.1.0/src/obd_tui/views/panel.py +98 -0
  45. obd_tui-0.1.0/src/obd_tui/views/panels/__init__.py +46 -0
  46. obd_tui-0.1.0/src/obd_tui/views/panels/air.py +43 -0
  47. obd_tui-0.1.0/src/obd_tui/views/panels/catalog.py +35 -0
  48. obd_tui-0.1.0/src/obd_tui/views/panels/diagnostics.py +36 -0
  49. obd_tui-0.1.0/src/obd_tui/views/panels/egr.py +33 -0
  50. obd_tui-0.1.0/src/obd_tui/views/panels/engine.py +53 -0
  51. obd_tui-0.1.0/src/obd_tui/views/panels/faults.py +35 -0
  52. obd_tui-0.1.0/tests/test_app.py +238 -0
  53. obd_tui-0.1.0/tests/test_cli.py +70 -0
  54. obd_tui-0.1.0/tests/test_connection.py +197 -0
  55. obd_tui-0.1.0/tests/test_detection.py +103 -0
  56. obd_tui-0.1.0/tests/test_models.py +132 -0
  57. obd_tui-0.1.0/tests/test_panels.py +186 -0
  58. obd_tui-0.1.0/tests/test_polling.py +149 -0
  59. obd_tui-0.1.0/tests/test_session.py +181 -0
  60. obd_tui-0.1.0/tests/test_views.py +155 -0
  61. obd_tui-0.1.0/uv.lock +1065 -0
  62. obd_tui-0.1.0/zensical.toml +23 -0
@@ -0,0 +1,4 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2026 Chris <goabonga@pm.me>
3
+
4
+ github: [goabonga]
@@ -0,0 +1,77 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2026 Chris <goabonga@pm.me>
3
+
4
+ name: Bug Report
5
+ description: Report a bug or unexpected behavior
6
+ labels: ["bug"]
7
+ body:
8
+ - type: markdown
9
+ attributes:
10
+ value: |
11
+ Thanks for reporting a bug! Please fill in the details below.
12
+
13
+ - type: input
14
+ id: version
15
+ attributes:
16
+ label: obd-tui version
17
+ description: "Output of `pip show obd-tui` or `obd-tui --version`."
18
+ validations:
19
+ required: true
20
+
21
+ - type: textarea
22
+ id: description
23
+ attributes:
24
+ label: Description
25
+ description: A clear description of the bug.
26
+ validations:
27
+ required: true
28
+
29
+ - type: textarea
30
+ id: steps
31
+ attributes:
32
+ label: Steps to Reproduce
33
+ description: How can we reproduce this issue? Include the exact command line.
34
+ validations:
35
+ required: true
36
+
37
+ - type: textarea
38
+ id: expected
39
+ attributes:
40
+ label: Expected Behavior
41
+ validations:
42
+ required: true
43
+
44
+ - type: textarea
45
+ id: actual
46
+ attributes:
47
+ label: Actual Behavior
48
+ validations:
49
+ required: true
50
+
51
+ - type: dropdown
52
+ id: python-version
53
+ attributes:
54
+ label: Python Version
55
+ options:
56
+ - "3.11"
57
+ - "3.12"
58
+ - "3.13"
59
+ validations:
60
+ required: true
61
+
62
+ - type: dropdown
63
+ id: os
64
+ attributes:
65
+ label: Operating System
66
+ options:
67
+ - Linux
68
+ - macOS
69
+ - Windows
70
+ validations:
71
+ required: true
72
+
73
+ - type: textarea
74
+ id: logs
75
+ attributes:
76
+ label: Logs / Traceback
77
+ render: shell
@@ -0,0 +1,11 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2026 Chris <goabonga@pm.me>
3
+
4
+ blank_issues_enabled: false
5
+ contact_links:
6
+ - name: Code of Conduct
7
+ url: https://github.com/goabonga/obd-tui/blob/main/CODE_OF_CONDUCT.md
8
+ about: Please read our Code of Conduct before contributing
9
+ - name: Security advisories
10
+ url: https://github.com/goabonga/obd-tui/security/advisories/new
11
+ about: Report a vulnerability privately (do not open a public issue)
@@ -0,0 +1,37 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2026 Chris <goabonga@pm.me>
3
+
4
+ name: Feature Request
5
+ description: Suggest a new feature or enhancement
6
+ labels: ["enhancement"]
7
+ body:
8
+ - type: markdown
9
+ attributes:
10
+ value: |
11
+ Thanks for suggesting a feature! Please describe your idea below.
12
+
13
+ - type: textarea
14
+ id: problem
15
+ attributes:
16
+ label: Problem
17
+ description: What problem does this feature solve? Is it related to a frustration?
18
+ validations:
19
+ required: true
20
+
21
+ - type: textarea
22
+ id: solution
23
+ attributes:
24
+ label: Proposed Solution
25
+ description: Describe the solution you'd like.
26
+ validations:
27
+ required: true
28
+
29
+ - type: textarea
30
+ id: alternatives
31
+ attributes:
32
+ label: Alternatives Considered
33
+
34
+ - type: textarea
35
+ id: context
36
+ attributes:
37
+ label: Additional Context
@@ -0,0 +1,70 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2026 Chris <goabonga@pm.me>
3
+
4
+ # Dependabot v2 configuration.
5
+
6
+ version: 2
7
+
8
+ updates:
9
+ # Python dependencies via uv.
10
+ - package-ecosystem: "uv"
11
+ directory: "/"
12
+ schedule:
13
+ interval: "weekly"
14
+ day: "monday"
15
+ time: "06:00"
16
+ timezone: "Europe/Paris"
17
+ open-pull-requests-limit: 5
18
+ ignore:
19
+ # `hatchling` lives in [build-system].requires; Dependabot's uv
20
+ # FileUpdater cannot write that table. Bump it by hand; pip-audit and
21
+ # osv-scanner still flag any CVEs.
22
+ - dependency-name: "hatchling"
23
+ groups:
24
+ # Bundle routine dev tooling bumps into a single weekly PR. The
25
+ # signed-rewrite script keys "dev-tools group" -> chore(deps).
26
+ dev-tools:
27
+ applies-to: version-updates
28
+ patterns:
29
+ - "ruff"
30
+ - "mypy"
31
+ - "pytest*"
32
+ - "pre-commit"
33
+ - "types-*"
34
+ - "zensical"
35
+ # Runtime deps ship in the package -> fixes; the signed rewrite confirms
36
+ # this from [dependency-groups].dev membership. Dev tooling stays chore.
37
+ commit-message:
38
+ prefix: "fix(deps)"
39
+ prefix-development: "chore(deps)"
40
+ labels:
41
+ - "dependencies"
42
+ - "python"
43
+
44
+ # Pinned versions of GitHub Actions in `.github/workflows/*.yml`.
45
+ - package-ecosystem: "github-actions"
46
+ directory: "/"
47
+ schedule:
48
+ interval: "weekly"
49
+ day: "monday"
50
+ time: "06:00"
51
+ timezone: "Europe/Paris"
52
+ open-pull-requests-limit: 5
53
+ groups:
54
+ ci-actions:
55
+ applies-to: version-updates
56
+ patterns:
57
+ - "actions/*"
58
+ - "astral-sh/*"
59
+ - "google/*"
60
+ - "pypa/*"
61
+ - "codecov/*"
62
+ - "compilerla/*"
63
+ - "pre-commit/*"
64
+ - "docker/*"
65
+ - "softprops/*"
66
+ commit-message:
67
+ prefix: "ci"
68
+ labels:
69
+ - "dependencies"
70
+ - "github-actions"
@@ -0,0 +1,37 @@
1
+ ## Description
2
+
3
+ <!-- Describe what this PR does and why. -->
4
+
5
+ ## Type
6
+
7
+ <!-- Check the one that applies: -->
8
+
9
+ - [ ] `feat` - New feature
10
+ - [ ] `fix` - Bug fix
11
+ - [ ] `docs` - Documentation
12
+ - [ ] `refactor` - Code refactoring
13
+ - [ ] `test` - Adding or updating tests
14
+ - [ ] `chore` - Maintenance
15
+ - [ ] `ci` - CI / release pipeline
16
+
17
+ ## Changes
18
+
19
+ <!-- List the main changes introduced by this PR: -->
20
+
21
+ -
22
+
23
+ ## Related Issues
24
+
25
+ <!-- Link related issues: Closes #123, Fixes #456 -->
26
+
27
+ ## Checklist
28
+
29
+ - [ ] Commits follow [Conventional Commits](https://www.conventionalcommits.org/)
30
+ - [ ] Branch is up to date with `main`
31
+ - [ ] `uv sync` succeeds
32
+ - [ ] `uv run ruff check src tests` and `uv run ruff format --check src tests` are clean
33
+ - [ ] `uv run mypy src` is clean (if the project uses mypy)
34
+ - [ ] `uv run pytest` passes (if the project has tests)
35
+ - [ ] `uv tool run multicz validate --strict` passes
36
+ - [ ] SPDX license headers are present (`python scripts/add_license_header.py --path . --types py,toml --check`)
37
+ - [ ] No `Co-Authored-By` trailer in commit messages
@@ -0,0 +1,313 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2026 Chris <goabonga@pm.me>
3
+
4
+ name: ci
5
+
6
+ on:
7
+ pull_request:
8
+ branches: [main]
9
+ push:
10
+ branches: [main]
11
+
12
+ concurrency:
13
+ group: ${{ github.workflow }}-${{ github.ref }}
14
+ cancel-in-progress: ${{ github.event_name == 'pull_request' }}
15
+
16
+ permissions:
17
+ contents: read
18
+
19
+ jobs:
20
+ lint:
21
+ name: lint
22
+ runs-on: ubuntu-latest
23
+ steps:
24
+ - uses: actions/checkout@v6
25
+ - uses: astral-sh/setup-uv@v7
26
+ with:
27
+ enable-cache: true
28
+ cache-suffix: ${{ github.job }}
29
+ python-version: "3.11"
30
+ - run: uv sync --frozen --only-group dev
31
+ - run: uv run --no-sync ruff check --output-format=github src tests
32
+ - run: uv run --no-sync ruff format --check src tests
33
+
34
+ type-check:
35
+ name: type-check
36
+ runs-on: ubuntu-latest
37
+ steps:
38
+ - uses: actions/checkout@v6
39
+ - uses: astral-sh/setup-uv@v7
40
+ with:
41
+ enable-cache: true
42
+ cache-suffix: ${{ github.job }}
43
+ python-version: "3.11"
44
+ - run: uv sync --frozen
45
+ - run: uv run --no-sync mypy src
46
+
47
+ license-headers:
48
+ name: license-headers
49
+ runs-on: ubuntu-latest
50
+ steps:
51
+ - uses: actions/checkout@v6
52
+ - uses: astral-sh/setup-uv@v7
53
+ with:
54
+ enable-cache: true
55
+ cache-suffix: ${{ github.job }}
56
+ python-version: "3.11"
57
+ - name: Check SPDX headers
58
+ run: |
59
+ uv run --no-project python scripts/add_license_header.py --path src --types py --check
60
+ uv run --no-project python scripts/add_license_header.py --path tests --types py --check
61
+ uv run --no-project python scripts/add_license_header.py --path scripts --types py,sh --check
62
+ uv run --no-project python scripts/add_license_header.py --path .github --types yml --check
63
+ uv run --no-project python scripts/add_license_header.py --path . --types toml --check
64
+
65
+ test:
66
+ name: test
67
+ runs-on: ubuntu-latest
68
+ strategy:
69
+ fail-fast: false
70
+ matrix:
71
+ python-version: ["3.11", "3.12", "3.13"]
72
+ steps:
73
+ - uses: actions/checkout@v6
74
+ - uses: astral-sh/setup-uv@v7
75
+ with:
76
+ enable-cache: true
77
+ cache-suffix: ${{ github.job }}-${{ matrix.python-version }}
78
+ python-version: ${{ matrix.python-version }}
79
+ - run: uv sync --frozen
80
+ - name: Run tests with coverage
81
+ run: |
82
+ uv run --no-sync pytest \
83
+ --cov=obd_tui \
84
+ --cov-report=term-missing \
85
+ --cov-report=xml:coverage.xml \
86
+ --junitxml=junit.xml \
87
+ -o junit_family=legacy
88
+ - name: Upload coverage to Codecov
89
+ if: matrix.python-version == '3.11'
90
+ uses: codecov/codecov-action@v6
91
+ with:
92
+ files: coverage.xml
93
+ token: ${{ secrets.CODECOV_TOKEN }}
94
+ fail_ci_if_error: false
95
+ disable_search: true
96
+ - name: Upload test results to Codecov
97
+ if: ${{ !cancelled() && matrix.python-version == '3.11' }}
98
+ uses: codecov/codecov-action@v6
99
+ with:
100
+ files: junit.xml
101
+ report_type: test_results
102
+ token: ${{ secrets.CODECOV_TOKEN }}
103
+ fail_ci_if_error: false
104
+ disable_search: true
105
+
106
+ bandit:
107
+ name: bandit
108
+ runs-on: ubuntu-latest
109
+ steps:
110
+ - uses: actions/checkout@v6
111
+ - uses: astral-sh/setup-uv@v7
112
+ with:
113
+ enable-cache: true
114
+ cache-suffix: ${{ github.job }}
115
+ python-version: "3.11"
116
+ - run: uvx --from 'bandit[toml]' bandit -c pyproject.toml --recursive src
117
+
118
+ pip-audit:
119
+ name: pip-audit
120
+ runs-on: ubuntu-latest
121
+ steps:
122
+ - uses: actions/checkout@v6
123
+ - uses: astral-sh/setup-uv@v7
124
+ with:
125
+ enable-cache: true
126
+ cache-suffix: ${{ github.job }}
127
+ python-version: "3.11"
128
+ - run: uv sync --frozen
129
+ - run: uvx pip-audit --skip-editable --path .venv
130
+
131
+ osv-scanner:
132
+ name: osv-scanner
133
+ runs-on: ubuntu-latest
134
+ steps:
135
+ - uses: actions/checkout@v6
136
+ - name: Install OSV-Scanner
137
+ run: |
138
+ mkdir -p "$HOME/.local/bin"
139
+ curl -sSfL \
140
+ "https://github.com/google/osv-scanner/releases/latest/download/osv-scanner_linux_amd64" \
141
+ -o "$HOME/.local/bin/osv-scanner"
142
+ chmod +x "$HOME/.local/bin/osv-scanner"
143
+ echo "$HOME/.local/bin" >> "$GITHUB_PATH"
144
+ - run: osv-scanner scan source --lockfile=uv.lock
145
+
146
+ release:
147
+ name: release
148
+ needs: [lint, type-check, license-headers, test, bandit, pip-audit, osv-scanner]
149
+ if: github.event_name == 'push' && github.ref == 'refs/heads/main'
150
+ runs-on: ubuntu-latest
151
+ permissions:
152
+ contents: write
153
+ outputs:
154
+ no_bumps: ${{ steps.bump.outputs.no_bumps }}
155
+ version: ${{ steps.bump.outputs.version }}
156
+ steps:
157
+ - uses: actions/checkout@v6
158
+ with:
159
+ fetch-depth: 0
160
+ persist-credentials: true
161
+ # No python-version pin: multicz needs Python >=3.12 and
162
+ # `uv tool install multicz` fails under 3.11.
163
+ - uses: astral-sh/setup-uv@v7
164
+ with:
165
+ enable-cache: true
166
+ cache-suffix: ${{ github.job }}
167
+ - name: Install multicz
168
+ run: uv tool install multicz
169
+ - name: Configure git author
170
+ run: |
171
+ NAME="${{ github.event.head_commit.author.name }}"
172
+ EMAIL="${{ github.event.head_commit.author.email }}"
173
+ git config user.name "${NAME:-github-actions[bot]}"
174
+ git config user.email "${EMAIL:-41898282+github-actions[bot]@users.noreply.github.com}"
175
+ - name: Detect GPG availability
176
+ id: gpg
177
+ env:
178
+ GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
179
+ GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
180
+ run: |
181
+ if [ -n "$GPG_PRIVATE_KEY" ] && [ -n "$GPG_PASSPHRASE" ]; then
182
+ echo "available=true" >> "$GITHUB_OUTPUT"
183
+ else
184
+ echo "available=false" >> "$GITHUB_OUTPUT"
185
+ fi
186
+ - name: Import and unlock GPG key
187
+ if: steps.gpg.outputs.available == 'true'
188
+ env:
189
+ GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
190
+ GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
191
+ run: |
192
+ echo "$GPG_PRIVATE_KEY" | gpg --batch --import
193
+ echo test | gpg --batch --yes --passphrase "$GPG_PASSPHRASE" \
194
+ --pinentry-mode loopback --sign > /dev/null
195
+ KEY_ID=$(gpg --list-secret-keys --with-colons | awk -F: '/^sec/{print $5; exit}')
196
+ git config user.signingkey "$KEY_ID"
197
+ - name: Plan release
198
+ run: uv tool run multicz plan --summary "$GITHUB_STEP_SUMMARY"
199
+ - name: Bump, commit, tag, push (signed)
200
+ id: bump_signed
201
+ if: steps.gpg.outputs.available == 'true'
202
+ run: |
203
+ set -euo pipefail
204
+ PLAN=$(uv tool run multicz bump --commit --tag --push --sign --output json)
205
+ BUMPED=$(echo "$PLAN" | jq -r '.bumps | keys | join(" ")')
206
+ if [ -z "$BUMPED" ]; then
207
+ echo "no_bumps=true" >> "$GITHUB_OUTPUT"
208
+ else
209
+ echo "no_bumps=false" >> "$GITHUB_OUTPUT"
210
+ echo "version=$(uv tool run multicz get obd-tui)" >> "$GITHUB_OUTPUT"
211
+ fi
212
+ - name: Bump, commit, tag, push
213
+ id: bump_unsigned
214
+ if: steps.gpg.outputs.available != 'true'
215
+ run: |
216
+ set -euo pipefail
217
+ PLAN=$(uv tool run multicz bump --commit --tag --push --output json)
218
+ BUMPED=$(echo "$PLAN" | jq -r '.bumps | keys | join(" ")')
219
+ if [ -z "$BUMPED" ]; then
220
+ echo "no_bumps=true" >> "$GITHUB_OUTPUT"
221
+ else
222
+ echo "no_bumps=false" >> "$GITHUB_OUTPUT"
223
+ echo "version=$(uv tool run multicz get obd-tui)" >> "$GITHUB_OUTPUT"
224
+ fi
225
+ - name: Resolve release outputs
226
+ id: bump
227
+ run: |
228
+ if [ "${{ steps.gpg.outputs.available }}" = "true" ]; then
229
+ echo 'no_bumps=${{ steps.bump_signed.outputs.no_bumps }}' >> "$GITHUB_OUTPUT"
230
+ echo 'version=${{ steps.bump_signed.outputs.version }}' >> "$GITHUB_OUTPUT"
231
+ else
232
+ echo 'no_bumps=${{ steps.bump_unsigned.outputs.no_bumps }}' >> "$GITHUB_OUTPUT"
233
+ echo 'version=${{ steps.bump_unsigned.outputs.version }}' >> "$GITHUB_OUTPUT"
234
+ fi
235
+
236
+ pypi-publish:
237
+ name: pypi-publish
238
+ needs: [release]
239
+ if: >-
240
+ github.event_name == 'push' && github.ref == 'refs/heads/main'
241
+ && needs.release.result == 'success' && needs.release.outputs.no_bumps == 'false'
242
+ runs-on: ubuntu-latest
243
+ environment:
244
+ name: pypi
245
+ url: https://pypi.org/project/obd-tui/
246
+ permissions:
247
+ contents: write
248
+ id-token: write # Trusted Publishing (OIDC) - do NOT add a token/password
249
+ steps:
250
+ - uses: actions/checkout@v6
251
+ with:
252
+ fetch-depth: 0
253
+ ref: main
254
+ - uses: astral-sh/setup-uv@v7
255
+ with:
256
+ enable-cache: true
257
+ cache-suffix: ${{ github.job }}
258
+ - name: Install multicz
259
+ run: uv tool install multicz
260
+ - name: Build wheel and sdist
261
+ run: uv build --out-dir dist && ls -l dist
262
+ - name: Publish to PyPI
263
+ uses: pypa/gh-action-pypi-publish@release/v1
264
+ with:
265
+ packages-dir: dist
266
+ skip-existing: true
267
+ - name: Create GitHub release
268
+ env:
269
+ GH_TOKEN: ${{ github.token }}
270
+ run: |
271
+ set -euo pipefail
272
+ version="${{ needs.release.outputs.version }}"
273
+ # Same tag multicz created (tag_format = "v{version}").
274
+ tag="v${version}"
275
+ notes=$(uv tool run multicz release-notes --tag "$tag")
276
+ gh release create "$tag" --title "$tag" --notes "$notes" dist/*
277
+
278
+ docs:
279
+ name: docs
280
+ needs: [release]
281
+ if: >-
282
+ github.event_name == 'push'
283
+ && github.ref == 'refs/heads/main'
284
+ && needs.release.result == 'success'
285
+ && needs.release.outputs.no_bumps == 'false'
286
+ runs-on: ubuntu-latest
287
+ environment:
288
+ name: github-pages
289
+ url: ${{ steps.deployment.outputs.page_url }}
290
+ permissions:
291
+ contents: read
292
+ pages: write
293
+ id-token: write
294
+ steps:
295
+ - uses: actions/checkout@v6
296
+ with:
297
+ ref: main
298
+ - uses: actions/configure-pages@v6
299
+ with:
300
+ enablement: true
301
+ - uses: astral-sh/setup-uv@v7
302
+ with:
303
+ enable-cache: true
304
+ cache-suffix: ${{ github.job }}
305
+ - name: Install Zensical (doc group)
306
+ run: uv sync --frozen --only-group doc
307
+ - name: Build site
308
+ run: uv run zensical build --clean
309
+ - uses: actions/upload-pages-artifact@v5
310
+ with:
311
+ path: site
312
+ - id: deployment
313
+ uses: actions/deploy-pages@v5
@@ -0,0 +1,94 @@
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (c) 2026 Chris <goabonga@pm.me>
3
+
4
+ name: dependabot-rewrite
5
+
6
+ # `pull_request_target` so the workflow can read GPG_PRIVATE_KEY /
7
+ # GPG_PASSPHRASE (regular `pull_request` runs without secrets on bot events).
8
+ # The workflow body is loaded from `main`, not the PR branch, so no untrusted
9
+ # code runs. The actor gate restricts it to genuine Dependabot PRs.
10
+
11
+ on:
12
+ pull_request_target:
13
+ types: [opened, synchronize, reopened]
14
+
15
+ permissions:
16
+ contents: write
17
+ pull-requests: write
18
+
19
+ jobs:
20
+ rewrite:
21
+ name: rewrite dependabot commits
22
+ if: github.actor == 'dependabot[bot]'
23
+ runs-on: ubuntu-latest
24
+ concurrency:
25
+ group: dependabot-rewrite-${{ github.event.pull_request.number }}
26
+ cancel-in-progress: true
27
+ steps:
28
+ - name: Check GPG availability
29
+ id: gate
30
+ env:
31
+ GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
32
+ GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
33
+ run: |
34
+ if [ -z "$GPG_PRIVATE_KEY" ] || [ -z "$GPG_PASSPHRASE" ]; then
35
+ {
36
+ echo "## Dependabot rewrite: skipped"
37
+ echo ""
38
+ echo "GPG_PRIVATE_KEY and/or GPG_PASSPHRASE are not configured. "
39
+ echo "The Dependabot PR ships with bot-authored, web-signed commits."
40
+ } >> "$GITHUB_STEP_SUMMARY"
41
+ echo "::warning::GPG secrets unset; leaving Dependabot commits as-is"
42
+ echo "skip=true" >> "$GITHUB_OUTPUT"
43
+ fi
44
+
45
+ - name: Checkout PR head branch
46
+ if: steps.gate.outputs.skip != 'true'
47
+ uses: actions/checkout@v6
48
+ with:
49
+ ref: ${{ github.event.pull_request.head.ref }}
50
+ fetch-depth: 0
51
+ persist-credentials: true
52
+
53
+ - name: Configure GPG and git identity from the imported key
54
+ if: steps.gate.outputs.skip != 'true'
55
+ env:
56
+ GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
57
+ GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
58
+ run: |
59
+ set -euo pipefail
60
+ echo "$GPG_PRIVATE_KEY" | gpg --batch --import
61
+ echo test | gpg --batch --yes --passphrase "$GPG_PASSPHRASE" \
62
+ --pinentry-mode loopback --sign > /dev/null
63
+ KEY_ID=$(gpg --list-secret-keys --with-colons | awk -F: '/^sec/{print $5; exit}')
64
+ UID_LINE=$(gpg --list-keys --with-colons "$KEY_ID" | awk -F: '/^uid/{print $10; exit}')
65
+ NAME="${UID_LINE%% <*}"
66
+ EMAIL=$(printf '%s' "$UID_LINE" | sed -nE 's/.*<([^>]+)>.*/\1/p')
67
+ git config user.name "$NAME"
68
+ git config user.email "$EMAIL"
69
+ git config user.signingkey "$KEY_ID"
70
+ git config commit.gpgsign true
71
+
72
+ - name: Rewrite commits with scope-aware messages
73
+ if: steps.gate.outputs.skip != 'true'
74
+ run: |
75
+ set -euo pipefail
76
+ BASE_SHA=$(git merge-base "origin/${{ github.event.pull_request.base.ref }}" HEAD)
77
+ git rebase "$BASE_SHA" --exec "scripts/rewrite-dependabot-commit.sh"
78
+
79
+ - name: Force-push rewritten branch
80
+ if: steps.gate.outputs.skip != 'true'
81
+ run: |
82
+ git push --force-with-lease origin "HEAD:${{ github.event.pull_request.head.ref }}"
83
+
84
+ - name: Write step summary
85
+ if: steps.gate.outputs.skip != 'true' && success()
86
+ run: |
87
+ {
88
+ echo "## Dependabot rewrite: applied"
89
+ echo ""
90
+ echo "PR branch \`${{ github.event.pull_request.head.ref }}\` rewritten:"
91
+ echo ""
92
+ echo "- Conventional-commits scope inferred from changed files / dev-group membership"
93
+ echo "- Authored and GPG-signed as \`$(git config user.name) <$(git config user.email)>\`"
94
+ } >> "$GITHUB_STEP_SUMMARY"
@@ -0,0 +1,27 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ build/
6
+ dist/
7
+
8
+ # Environments
9
+ .venv/
10
+ venv/
11
+
12
+ # Tooling caches
13
+ .mypy_cache/
14
+ .pytest_cache/
15
+ .ruff_cache/
16
+ .coverage
17
+ coverage.xml
18
+ junit.xml
19
+
20
+ # Zensical
21
+ site/
22
+
23
+ # Editors / OS
24
+ .idea/
25
+ .vscode/
26
+ *.swp
27
+ .DS_Store
@@ -0,0 +1,13 @@
1
+ {
2
+ "version": 1,
3
+ "git_head": "5399485d9f56d25cf1b1e8382073ac4875f8075e",
4
+ "git_head_short": "5399485",
5
+ "timestamp": "2026-08-13T13:00:26Z",
6
+ "components": {
7
+ "obd-tui": {
8
+ "version": "0.1.0",
9
+ "tag": "v0.1.0",
10
+ "tag_sha": null
11
+ }
12
+ }
13
+ }