clauster 0.2.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.
- clauster-0.2.0/.bestpractices.json +136 -0
- clauster-0.2.0/.coderabbit.yaml +17 -0
- clauster-0.2.0/.dockerignore +29 -0
- clauster-0.2.0/.github/CODEOWNERS +9 -0
- clauster-0.2.0/.github/settings.yml +143 -0
- clauster-0.2.0/.github/workflows/ci.yml +167 -0
- clauster-0.2.0/.github/workflows/lint.yml +55 -0
- clauster-0.2.0/.github/workflows/pr-title.yml +41 -0
- clauster-0.2.0/.github/workflows/release-please.yml +60 -0
- clauster-0.2.0/.github/workflows/release.yml +125 -0
- clauster-0.2.0/.github/workflows/scorecard.yml +45 -0
- clauster-0.2.0/.github/workflows/security.yml +208 -0
- clauster-0.2.0/.gitignore +31 -0
- clauster-0.2.0/.markdownlint-cli2.yaml +48 -0
- clauster-0.2.0/.pre-commit-config.yaml +45 -0
- clauster-0.2.0/.release-please-manifest.json +3 -0
- clauster-0.2.0/.yamllint.yaml +33 -0
- clauster-0.2.0/CHANGELOG.md +60 -0
- clauster-0.2.0/CONTRIBUTING.md +46 -0
- clauster-0.2.0/Dockerfile +70 -0
- clauster-0.2.0/LICENSE +202 -0
- clauster-0.2.0/PKG-INFO +280 -0
- clauster-0.2.0/README.md +237 -0
- clauster-0.2.0/SECURITY.md +34 -0
- clauster-0.2.0/THIRD_PARTY_NOTICES.md +112 -0
- clauster-0.2.0/UPGRADING.md +53 -0
- clauster-0.2.0/clauster.spec +48 -0
- clauster-0.2.0/clauster.yml.example +75 -0
- clauster-0.2.0/codecov.yml +20 -0
- clauster-0.2.0/docker/entrypoint.sh +20 -0
- clauster-0.2.0/docs/screenshots/dashboard-dark.png +0 -0
- clauster-0.2.0/docs/screenshots/dashboard-light.png +0 -0
- clauster-0.2.0/docs/screenshots/login-dark.png +0 -0
- clauster-0.2.0/docs/screenshots/new-project-clone.png +0 -0
- clauster-0.2.0/package-lock.json +1348 -0
- clauster-0.2.0/package.json +13 -0
- clauster-0.2.0/pyproject.toml +135 -0
- clauster-0.2.0/release-please-config.json +35 -0
- clauster-0.2.0/renovate.json +94 -0
- clauster-0.2.0/scripts/build-binary.sh +17 -0
- clauster-0.2.0/scripts/lint-docs.sh +26 -0
- clauster-0.2.0/src/clauster/__init__.py +3 -0
- clauster-0.2.0/src/clauster/__main__.py +342 -0
- clauster-0.2.0/src/clauster/app.py +746 -0
- clauster-0.2.0/src/clauster/auth.py +311 -0
- clauster-0.2.0/src/clauster/bridge_log.py +80 -0
- clauster-0.2.0/src/clauster/claude_cli.py +36 -0
- clauster-0.2.0/src/clauster/claude_md.py +178 -0
- clauster-0.2.0/src/clauster/clone_jobs.py +121 -0
- clauster-0.2.0/src/clauster/config.py +313 -0
- clauster-0.2.0/src/clauster/discovery.py +75 -0
- clauster-0.2.0/src/clauster/environments.py +284 -0
- clauster-0.2.0/src/clauster/hooks/__init__.py +5 -0
- clauster-0.2.0/src/clauster/hooks/resume_recap.py +211 -0
- clauster-0.2.0/src/clauster/inspector.py +68 -0
- clauster-0.2.0/src/clauster/logstream.py +44 -0
- clauster-0.2.0/src/clauster/models.py +148 -0
- clauster-0.2.0/src/clauster/ops.py +470 -0
- clauster-0.2.0/src/clauster/pointers.py +63 -0
- clauster-0.2.0/src/clauster/procutil.py +141 -0
- clauster-0.2.0/src/clauster/provisioning.py +362 -0
- clauster-0.2.0/src/clauster/pty_keeper.py +205 -0
- clauster-0.2.0/src/clauster/recap.py +112 -0
- clauster-0.2.0/src/clauster/redact.py +64 -0
- clauster-0.2.0/src/clauster/runner.py +1039 -0
- clauster-0.2.0/src/clauster/state.py +93 -0
- clauster-0.2.0/src/clauster/static/alpine.LICENSE +21 -0
- clauster-0.2.0/src/clauster/static/alpine.min.js +5 -0
- clauster-0.2.0/src/clauster/static/clauster.css +340 -0
- clauster-0.2.0/src/clauster/static/favicon.svg +14 -0
- clauster-0.2.0/src/clauster/static/vendor/iconoir/LICENSE +21 -0
- clauster-0.2.0/src/clauster/static/vendor/iconoir/README.md +45 -0
- clauster-0.2.0/src/clauster/static/vendor/tabler/LICENSE +21 -0
- clauster-0.2.0/src/clauster/static/vendor/tabler/README.md +30 -0
- clauster-0.2.0/src/clauster/static/vendor/tabler/css/tabler.min.css +9 -0
- clauster-0.2.0/src/clauster/static/vendor/tabler/js/tabler.min.js +13 -0
- clauster-0.2.0/src/clauster/static/vendor/versions.txt +11 -0
- clauster-0.2.0/src/clauster/templates/_iconoir_sprite.html +82 -0
- clauster-0.2.0/src/clauster/templates/_project_card.html +166 -0
- clauster-0.2.0/src/clauster/templates/dashboard.html +760 -0
- clauster-0.2.0/src/clauster/templates/login.html +77 -0
- clauster-0.2.0/src/clauster/trust.py +117 -0
- clauster-0.2.0/src/clauster/usage.py +212 -0
- clauster-0.2.0/tests/E2E_CHECKLIST.md +118 -0
- clauster-0.2.0/tests/conftest.py +69 -0
- clauster-0.2.0/tests/fixtures/bridge-logs/test1-bridge-debug.log +8 -0
- clauster-0.2.0/tests/fixtures/fake_claude/claude +178 -0
- clauster-0.2.0/tests/fixtures/fake_claude/claude.cmd +5 -0
- clauster-0.2.0/tests/fixtures/fake_git/git +75 -0
- clauster-0.2.0/tests/fixtures/fake_git/git.cmd +5 -0
- clauster-0.2.0/tests/fixtures/pointers/dockerize2.bridge-pointer.json +1 -0
- clauster-0.2.0/tests/fixtures/pointers/test1.bridge-pointer.json +1 -0
- clauster-0.2.0/tests/fixtures/pointers/test2.bridge-pointer.json +1 -0
- clauster-0.2.0/tests/fixtures/transcripts/test1-session.jsonl +10 -0
- clauster-0.2.0/tests/test_app.py +36 -0
- clauster-0.2.0/tests/test_app_auth.py +364 -0
- clauster-0.2.0/tests/test_app_instances.py +80 -0
- clauster-0.2.0/tests/test_app_routes.py +439 -0
- clauster-0.2.0/tests/test_auth.py +288 -0
- clauster-0.2.0/tests/test_bridge_log.py +49 -0
- clauster-0.2.0/tests/test_claude_md.py +234 -0
- clauster-0.2.0/tests/test_clone_jobs.py +79 -0
- clauster-0.2.0/tests/test_config.py +159 -0
- clauster-0.2.0/tests/test_discovery.py +57 -0
- clauster-0.2.0/tests/test_environments.py +408 -0
- clauster-0.2.0/tests/test_fixtures.py +42 -0
- clauster-0.2.0/tests/test_inspector.py +51 -0
- clauster-0.2.0/tests/test_logstream.py +68 -0
- clauster-0.2.0/tests/test_logtail.py +51 -0
- clauster-0.2.0/tests/test_main.py +186 -0
- clauster-0.2.0/tests/test_ops.py +402 -0
- clauster-0.2.0/tests/test_pointers.py +48 -0
- clauster-0.2.0/tests/test_procutil.py +156 -0
- clauster-0.2.0/tests/test_provisioning.py +484 -0
- clauster-0.2.0/tests/test_pty_keeper.py +230 -0
- clauster-0.2.0/tests/test_recap.py +340 -0
- clauster-0.2.0/tests/test_redact.py +53 -0
- clauster-0.2.0/tests/test_runner.py +528 -0
- clauster-0.2.0/tests/test_runner_pty.py +266 -0
- clauster-0.2.0/tests/test_runner_recap.py +97 -0
- clauster-0.2.0/tests/test_spawn_controls.py +238 -0
- clauster-0.2.0/tests/test_state.py +67 -0
- clauster-0.2.0/tests/test_trust.py +128 -0
- clauster-0.2.0/tests/test_urls.py +59 -0
- clauster-0.2.0/tests/test_usage.py +284 -0
- clauster-0.2.0/uv.lock +1459 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
{
|
|
2
|
+
"description_good_status": "Met",
|
|
3
|
+
"description_good_justification": "Clauster's purpose and capabilities are described in the README and the GitHub repository description.",
|
|
4
|
+
"interact_status": "Met",
|
|
5
|
+
"interact_justification": "CONTRIBUTING.md explains how to interact and contribute; GitHub Issues are open. https://github.com/schubydoo/clauster/blob/main/CONTRIBUTING.md",
|
|
6
|
+
"contribution_status": "Met",
|
|
7
|
+
"contribution_justification": "The contribution process (branch + PR, CI gates) is documented. https://github.com/schubydoo/clauster/blob/main/CONTRIBUTING.md",
|
|
8
|
+
"contribution_requirements_status": "Met",
|
|
9
|
+
"contribution_requirements_justification": "CONTRIBUTING.md states the requirements: pytest with a 96% coverage gate, ruff/pyright/doc-lint, and Conventional Commit PR titles. https://github.com/schubydoo/clauster/blob/main/CONTRIBUTING.md",
|
|
10
|
+
"floss_license_status": "Met",
|
|
11
|
+
"floss_license_justification": "Released under the Apache License 2.0. https://github.com/schubydoo/clauster/blob/main/LICENSE",
|
|
12
|
+
"floss_license_osi_status": "Met",
|
|
13
|
+
"floss_license_osi_justification": "Apache-2.0 is an OSI-approved open source license.",
|
|
14
|
+
"license_location_status": "Met",
|
|
15
|
+
"license_location_justification": "License is in the standard top-level LICENSE file. https://github.com/schubydoo/clauster/blob/main/LICENSE",
|
|
16
|
+
"documentation_basics_status": "Met",
|
|
17
|
+
"documentation_basics_justification": "README documents installation, configuration (clauster.yml), and usage; CONTRIBUTING, SECURITY, UPGRADING, and THIRD_PARTY_NOTICES are also provided.",
|
|
18
|
+
"documentation_interface_status": "Met",
|
|
19
|
+
"documentation_interface_justification": "The README documents the external interface: the CLI subcommands (clauster run/doctor/backup/...) and the clauster.yml configuration keys; the web UI is self-describing.",
|
|
20
|
+
"sites_https_status": "Met",
|
|
21
|
+
"sites_https_justification": "The project homepage/repository (GitHub) and distribution channels (PyPI, GHCR) are all served over HTTPS.",
|
|
22
|
+
"discussion_status": "Met",
|
|
23
|
+
"discussion_justification": "GitHub Issues is enabled and used for project discussion. https://github.com/schubydoo/clauster/issues",
|
|
24
|
+
"english_status": "Met",
|
|
25
|
+
"english_justification": "All documentation and code comments are in English.",
|
|
26
|
+
"maintained_status": "Met",
|
|
27
|
+
"maintained_justification": "The project is actively maintained (frequent commits, PRs, and reviews).",
|
|
28
|
+
"repo_public_status": "Met",
|
|
29
|
+
"repo_public_justification": "Public git repository hosted on GitHub. https://github.com/schubydoo/clauster",
|
|
30
|
+
"repo_track_status": "Met",
|
|
31
|
+
"repo_track_justification": "Git tracks every change to the source, with full history.",
|
|
32
|
+
"repo_interim_status": "Met",
|
|
33
|
+
"repo_interim_justification": "Interim changes between releases are visible as commits/branches/PRs in git.",
|
|
34
|
+
"repo_distributed_status": "Met",
|
|
35
|
+
"repo_distributed_justification": "Git is a distributed version control system.",
|
|
36
|
+
"version_unique_status": "Met",
|
|
37
|
+
"version_unique_justification": "Each release has a unique Semantic Version identifier maintained in pyproject.toml and src/clauster/__init__.py, which makes every build uniquely identifiable. Release tagging is automated via release-please (.release-please-manifest.json / release-please-config.json) and produces a vX.Y.Z git tag when a release is published.",
|
|
38
|
+
"version_semver_status": "Met",
|
|
39
|
+
"version_semver_justification": "The project uses Semantic Versioning (MAJOR.MINOR.PATCH).",
|
|
40
|
+
"version_tags_status": "Unmet",
|
|
41
|
+
"version_tags_justification": "No release has been cut yet, so no semantic-version git tags exist. Tagging is configured and automated via release-please and will produce vX.Y.Z tags once the first release is published.",
|
|
42
|
+
"release_notes_status": "N/A",
|
|
43
|
+
"release_notes_justification": "No release has been published, so there are no release notes to provide. Releases are published through the release-please workflow, which generates human-readable, grouped release notes (a CHANGELOG entry and a tagged GitHub Release) from the Conventional Commit history.",
|
|
44
|
+
"release_notes_vulns_status": "N/A",
|
|
45
|
+
"release_notes_vulns_justification": "No release has shipped a security fix and no publicly-known vulnerability has required disclosure. When a release fixes one, release-please records it in the generated release notes (Bug Fixes section).",
|
|
46
|
+
"report_process_status": "Met",
|
|
47
|
+
"report_process_justification": "Bugs are reported via GitHub Issues; security issues via SECURITY.md. https://github.com/schubydoo/clauster/issues",
|
|
48
|
+
"report_tracker_status": "Met",
|
|
49
|
+
"report_tracker_justification": "GitHub Issues serves as the bug tracker. https://github.com/schubydoo/clauster/issues",
|
|
50
|
+
"report_responses_status": "Met",
|
|
51
|
+
"report_responses_justification": "The project is new and has received no external bug reports yet (the only open issue is the auto-generated Renovate dependency dashboard). The maintainer monitors the GitHub issue tracker; there is no backlog of unanswered reports.",
|
|
52
|
+
"enhancement_responses_status": "Met",
|
|
53
|
+
"enhancement_responses_justification": "No enhancement requests have been received yet (the only open issue is the auto-generated Renovate dependency dashboard). The maintainer monitors the GitHub issue tracker and responds to requests as they arrive.",
|
|
54
|
+
"report_archive_status": "Met",
|
|
55
|
+
"report_archive_justification": "GitHub Issues provides a public, persistent archive of reports and responses. https://github.com/schubydoo/clauster/issues",
|
|
56
|
+
"vulnerability_report_process_status": "Met",
|
|
57
|
+
"vulnerability_report_process_justification": "SECURITY.md documents the vulnerability reporting process. https://github.com/schubydoo/clauster/blob/main/SECURITY.md",
|
|
58
|
+
"vulnerability_report_private_status": "Met",
|
|
59
|
+
"vulnerability_report_private_justification": "Vulnerabilities are reported privately via GitHub private security advisories. https://github.com/schubydoo/clauster/security/advisories/new",
|
|
60
|
+
"vulnerability_report_response_status": "Met",
|
|
61
|
+
"vulnerability_report_response_justification": "SECURITY.md commits to an initial response within a few days.",
|
|
62
|
+
"build_status": "Met",
|
|
63
|
+
"build_justification": "Built from source with a standard PEP 517 build (uv + hatchling); installable via pip/uv as a wheel/sdist.",
|
|
64
|
+
"build_common_tools_status": "Met",
|
|
65
|
+
"build_common_tools_justification": "Built with common, widely available FLOSS tools (uv, hatchling, CPython).",
|
|
66
|
+
"build_floss_tools_status": "Met",
|
|
67
|
+
"build_floss_tools_justification": "All build tooling is FLOSS.",
|
|
68
|
+
"test_status": "Met",
|
|
69
|
+
"test_justification": "A pytest test suite covers the codebase (400+ tests).",
|
|
70
|
+
"test_invocation_status": "Met",
|
|
71
|
+
"test_invocation_justification": "Running the tests is documented in CONTRIBUTING.md (`uv run pytest`). https://github.com/schubydoo/clauster/blob/main/CONTRIBUTING.md",
|
|
72
|
+
"test_most_status": "Met",
|
|
73
|
+
"test_most_justification": "The suite covers ~96% of statements, enforced by a CI coverage gate.",
|
|
74
|
+
"test_continuous_integration_status": "Met",
|
|
75
|
+
"test_continuous_integration_justification": "GitHub Actions runs the suite on every PR across a Linux/macOS/Windows x Python 3.11-3.14 matrix.",
|
|
76
|
+
"test_policy_status": "Met",
|
|
77
|
+
"test_policy_justification": "CONTRIBUTING requires tests for new functionality, backed by the merge-blocking 96% coverage gate.",
|
|
78
|
+
"tests_are_added_status": "Met",
|
|
79
|
+
"tests_are_added_justification": "New functionality is added with tests; the coverage gate blocks merges that would drop coverage.",
|
|
80
|
+
"tests_documented_added_status": "Met",
|
|
81
|
+
"tests_documented_added_justification": "The test-and-coverage policy is documented in CONTRIBUTING.md.",
|
|
82
|
+
"warnings_status": "Met",
|
|
83
|
+
"warnings_justification": "ruff (broad ruleset including bugbear and flake8-bandit) and pyright run in CI.",
|
|
84
|
+
"warnings_fixed_status": "Met",
|
|
85
|
+
"warnings_fixed_justification": "CI fails on any ruff or pyright finding; warnings are not tolerated on main.",
|
|
86
|
+
"warnings_strict_status": "Met",
|
|
87
|
+
"warnings_strict_justification": "Lint and type checks are treated as errors (CI-blocking).",
|
|
88
|
+
"know_secure_design_status": "Met",
|
|
89
|
+
"know_secure_design_justification": "The design applies least-privilege, fail-closed liveness, loopback-by-default networking, SSRF guards on clone, and per-project trust gating (documented in SECURITY.md).",
|
|
90
|
+
"know_common_errors_status": "Met",
|
|
91
|
+
"know_common_errors_justification": "The code guards against common errors (input validation, SSRF protection, no shell=True/argv lists, output redaction); ruff-S (bandit) and CodeQL enforce this in CI.",
|
|
92
|
+
"crypto_published_status": "Met",
|
|
93
|
+
"crypto_published_justification": "Cryptography is limited to argon2id password hashing (argon2-cffi) and Python's `secrets` module; both are standard, published mechanisms.",
|
|
94
|
+
"crypto_call_status": "Met",
|
|
95
|
+
"crypto_call_justification": "The project calls the argon2-cffi library and Python's stdlib `secrets`; it implements no home-grown cryptography.",
|
|
96
|
+
"crypto_floss_status": "Met",
|
|
97
|
+
"crypto_floss_justification": "argon2-cffi and the Python standard library are FLOSS.",
|
|
98
|
+
"crypto_keylength_status": "Met",
|
|
99
|
+
"crypto_keylength_justification": "argon2id is used with the library's secure default cost parameters; generated secrets/tokens are 256-bit (`secrets.token_bytes(32)`).",
|
|
100
|
+
"crypto_working_status": "Met",
|
|
101
|
+
"crypto_working_justification": "No broken or risky algorithms are used for security; argon2id for passwords, no MD5/SHA-1 for security purposes.",
|
|
102
|
+
"crypto_weaknesses_status": "Met",
|
|
103
|
+
"crypto_weaknesses_justification": "No known-weak cryptographic algorithms are used.",
|
|
104
|
+
"crypto_pfs_status": "N/A",
|
|
105
|
+
"crypto_pfs_justification": "Clauster performs no network key agreement itself; TLS (and forward secrecy) is provided by the operator's reverse proxy. It binds to loopback by default.",
|
|
106
|
+
"crypto_password_storage_status": "Met",
|
|
107
|
+
"crypto_password_storage_justification": "Login passwords are stored only as argon2id hashes (salted, memory-hard KDF) via argon2-cffi; plaintext is never stored. Verification is constant-time.",
|
|
108
|
+
"crypto_random_status": "Met",
|
|
109
|
+
"crypto_random_justification": "Cryptographically secure randomness uses Python's `secrets` module (token_bytes/token_hex).",
|
|
110
|
+
"delivery_mitm_status": "Met",
|
|
111
|
+
"delivery_mitm_justification": "The software is delivered over HTTPS (GitHub, PyPI, GHCR) and its dependencies are version/hash pinned, preventing MITM during retrieval.",
|
|
112
|
+
"delivery_unsigned_status": "Met",
|
|
113
|
+
"delivery_unsigned_justification": "Installation instructions use HTTPS package managers (pip/uv, docker pull); users are never directed to download unsigned code over HTTP.",
|
|
114
|
+
"vulnerabilities_fixed_60_days_status": "Met",
|
|
115
|
+
"vulnerabilities_fixed_60_days_justification": "Renovate and GitHub security alerts surface dependency vulnerabilities, which are patched promptly (well within 60 days).",
|
|
116
|
+
"vulnerabilities_critical_fixed_status": "Met",
|
|
117
|
+
"vulnerabilities_critical_fixed_justification": "No known unpatched vulnerabilities exist (OpenSSF Scorecard Vulnerabilities check scores 10/10).",
|
|
118
|
+
"no_leaked_credentials_status": "Met",
|
|
119
|
+
"no_leaked_credentials_justification": "No credentials are stored in the repository; gitleaks and GitHub secret scanning run in CI, sensitive config is gitignored, and session URLs/tokens are redacted in logs.",
|
|
120
|
+
"static_analysis_status": "Met",
|
|
121
|
+
"static_analysis_justification": "Static analysis runs in CI: ruff (including flake8-bandit `S` security rules), pyright, CodeQL, and Trivy.",
|
|
122
|
+
"static_analysis_common_vulnerabilities_status": "Met",
|
|
123
|
+
"static_analysis_common_vulnerabilities_justification": "CodeQL and ruff-S specifically target common vulnerability patterns.",
|
|
124
|
+
"static_analysis_fixed_status": "Met",
|
|
125
|
+
"static_analysis_fixed_justification": "Static-analysis findings are merge-blocking and fixed before merge.",
|
|
126
|
+
"static_analysis_often_status": "Met",
|
|
127
|
+
"static_analysis_often_justification": "Static analysis runs on every PR and push (CodeQL also on a weekly schedule).",
|
|
128
|
+
"dynamic_analysis_status": "Unmet",
|
|
129
|
+
"dynamic_analysis_justification": "No dynamic analysis or fuzzing is currently run; the project relies on static analysis plus a high-coverage test suite. Python is memory-safe.",
|
|
130
|
+
"dynamic_analysis_unsafe_status": "N/A",
|
|
131
|
+
"dynamic_analysis_unsafe_justification": "The project is written in Python, a memory-safe language; memory/behavior sanitizers for memory-unsafe languages do not apply.",
|
|
132
|
+
"dynamic_analysis_enable_assertions_status": "Met",
|
|
133
|
+
"dynamic_analysis_enable_assertions_justification": "The pytest suite exercises the code with assertions enabled throughout.",
|
|
134
|
+
"dynamic_analysis_fixed_status": "N/A",
|
|
135
|
+
"dynamic_analysis_fixed_justification": "No dynamic analysis is performed (see dynamic_analysis), so there are no such findings to fix; static analysis and tests cover this."
|
|
136
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json
|
|
2
|
+
#
|
|
3
|
+
# Docstrings: our gate is ruff's pydocstyle (D) rules, enforced on EVERY PR by the
|
|
4
|
+
# "ruff + pyright" CI job (.github/workflows/lint.yml, `on: pull_request`). It
|
|
5
|
+
# requires docstrings on all public modules/classes/functions (pep257 convention;
|
|
6
|
+
# D105/D107 and tests/** are intentionally exempt — see pyproject.toml).
|
|
7
|
+
#
|
|
8
|
+
# CodeRabbit's separate docstring-coverage pre-merge check computes a raw
|
|
9
|
+
# percentage that counts inner/nested helpers (preexec closures, signal handlers,
|
|
10
|
+
# test fixtures) which our gate deliberately exempts, so it reports a misleadingly
|
|
11
|
+
# low number against its 80% default. Defer to ruff D as the single source of
|
|
12
|
+
# truth and turn the redundant check off rather than run two disagreeing gates.
|
|
13
|
+
language: "en-US"
|
|
14
|
+
reviews:
|
|
15
|
+
pre_merge_checks:
|
|
16
|
+
docstrings:
|
|
17
|
+
mode: "off"
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Keep the build context (and final image) lean: only pyproject/uv.lock/README/src
|
|
2
|
+
# feed the build. Everything else is noise or must never ship.
|
|
3
|
+
.git
|
|
4
|
+
.github
|
|
5
|
+
.venv
|
|
6
|
+
**/__pycache__
|
|
7
|
+
**/*.py[cod]
|
|
8
|
+
.pytest_cache
|
|
9
|
+
.ruff_cache
|
|
10
|
+
.mypy_cache
|
|
11
|
+
htmlcov
|
|
12
|
+
.coverage*
|
|
13
|
+
tests
|
|
14
|
+
dist
|
|
15
|
+
build
|
|
16
|
+
*.spec
|
|
17
|
+
scripts
|
|
18
|
+
|
|
19
|
+
# Local config + internal notes (mirror .gitignore; never bake into an image)
|
|
20
|
+
clauster.yml
|
|
21
|
+
NOTES.md
|
|
22
|
+
HANDOFF.md
|
|
23
|
+
DEBUG_NOTES.md
|
|
24
|
+
PLAN_*.md
|
|
25
|
+
*_spec*.md
|
|
26
|
+
v4-panel-findings.md
|
|
27
|
+
.claude
|
|
28
|
+
.gitnexus
|
|
29
|
+
.logfire
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Code owners for Clauster — https://docs.github.com/articles/about-code-owners
|
|
2
|
+
#
|
|
3
|
+
# GitHub uses this to auto-request review from the owner on matching PRs.
|
|
4
|
+
# Branch protection does not (yet) *require* code-owner review — as a solo
|
|
5
|
+
# project there's no second reviewer to approve — but declaring ownership here
|
|
6
|
+
# is good hygiene and makes required code-owner review a one-line switch if the
|
|
7
|
+
# maintainer set grows.
|
|
8
|
+
|
|
9
|
+
* @schubydoo
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# Probot Settings (https://github.com/repository-settings/app)
|
|
2
|
+
# Install the GitHub App on this repo to make settings declarative.
|
|
3
|
+
# Note: the App requires `Administration: Read & write` permission on the repo
|
|
4
|
+
# to apply branch protection rules.
|
|
5
|
+
#
|
|
6
|
+
# Borrowed from the schubydoo python+uv family (dump1090-exporter / dockerize2)
|
|
7
|
+
# and adapted for clauster. Two known gotchas are preserved verbatim below:
|
|
8
|
+
# - enforce_admins / restrictions MUST be `null`, never `false`/`[]` (#923)
|
|
9
|
+
# - every bot-applied label MUST be declared or Probot deletes it (#386)
|
|
10
|
+
|
|
11
|
+
repository:
|
|
12
|
+
name: clauster
|
|
13
|
+
description: >-
|
|
14
|
+
Your homelab's Claude Code launchpad. Self-hosted web UI that spawns &
|
|
15
|
+
manages remote-control bridges on a remote host from any browser or phone —
|
|
16
|
+
start/stop, spawn & permission modes, CLAUDE.md editor, git clone, live log
|
|
17
|
+
tail, cost tracking. Loopback by default; password/reverse-proxy auth. No
|
|
18
|
+
telemetry.
|
|
19
|
+
# Topics: GitHub slugs disallow spaces — a leading space after "comma+space"
|
|
20
|
+
# gets silently dropped. Folded scalar + bare commas => exactly `a,b,c`.
|
|
21
|
+
topics: >-
|
|
22
|
+
claude,claude-code,remote-control,self-hosted,homelab,fastapi,nas,dispatcher,python
|
|
23
|
+
homepage: https://github.com/schubydoo/clauster
|
|
24
|
+
has_issues: true
|
|
25
|
+
has_projects: false
|
|
26
|
+
has_wiki: false
|
|
27
|
+
has_downloads: true
|
|
28
|
+
default_branch: main
|
|
29
|
+
allow_squash_merge: true
|
|
30
|
+
allow_merge_commit: false
|
|
31
|
+
allow_rebase_merge: true
|
|
32
|
+
allow_auto_merge: true
|
|
33
|
+
delete_branch_on_merge: true
|
|
34
|
+
allow_update_branch: true
|
|
35
|
+
# Web-UI squash defaults. Both default to PR_TITLE / PR_BODY out of the box;
|
|
36
|
+
# we override:
|
|
37
|
+
# - squash_merge_commit_title: COMMIT_OR_PR_TITLE — use the single commit's
|
|
38
|
+
# subject when a PR has only one commit, fall back to PR title otherwise.
|
|
39
|
+
# Avoids the "(#NN)" suffix duplication.
|
|
40
|
+
# - squash_merge_commit_message: COMMIT_MESSAGES — concatenate the branch's
|
|
41
|
+
# commit messages into the squash body, preserving hand-crafted commit
|
|
42
|
+
# bodies (rationale + Co-Authored-By trailers) instead of the PR desc.
|
|
43
|
+
# merge_commit_* settings deliberately omitted — allow_merge_commit is false,
|
|
44
|
+
# so those settings are inert.
|
|
45
|
+
squash_merge_commit_title: COMMIT_OR_PR_TITLE
|
|
46
|
+
squash_merge_commit_message: COMMIT_MESSAGES
|
|
47
|
+
|
|
48
|
+
# Labels managed declaratively by Probot. Every label any of our bots applies
|
|
49
|
+
# MUST be listed here — Probot Settings has a long-running issue
|
|
50
|
+
# (repository-settings/app#386) where it deletes labels that aren't declared,
|
|
51
|
+
# and the family has watched it strip Renovate's `dependencies` and
|
|
52
|
+
# release-please's `autorelease: *` labels off PRs in real time. Standard
|
|
53
|
+
# GitHub-created labels (bug, enhancement, documentation, etc.) are left alone.
|
|
54
|
+
labels:
|
|
55
|
+
# ---- Renovate ----
|
|
56
|
+
- name: dependencies
|
|
57
|
+
color: ededed
|
|
58
|
+
description: Renovate-opened dependency update PRs (and the dashboard issue)
|
|
59
|
+
- name: security
|
|
60
|
+
color: d93f0b
|
|
61
|
+
description: Security-related — Renovate vulnerability alerts use this
|
|
62
|
+
- name: vendored-assets
|
|
63
|
+
color: ededed
|
|
64
|
+
description: Renovate heads-up to re-vendor self-hosted front-end assets (manual dist update)
|
|
65
|
+
|
|
66
|
+
# ---- Release Please ----
|
|
67
|
+
# release-please-action applies these to its release PRs / tagged release
|
|
68
|
+
# commits. Without them declared, Probot strips them mid-release-flow.
|
|
69
|
+
- name: "autorelease: pending"
|
|
70
|
+
color: ededed
|
|
71
|
+
description: Release Please — release PR is open, awaiting merge
|
|
72
|
+
- name: "autorelease: tagged"
|
|
73
|
+
color: 0e8a16
|
|
74
|
+
description: Release Please — release PR is merged and the tag has been pushed
|
|
75
|
+
- name: "autorelease: snapshot"
|
|
76
|
+
color: 5319e7
|
|
77
|
+
description: Release Please — snapshot/prerelease (rare)
|
|
78
|
+
|
|
79
|
+
branches:
|
|
80
|
+
- name: main
|
|
81
|
+
protection:
|
|
82
|
+
required_pull_request_reviews:
|
|
83
|
+
# Solo maintainer: 0 required approvals, but stale reviews are dismissed
|
|
84
|
+
# and conversations must resolve. Raise this once there are co-maintainers.
|
|
85
|
+
required_approving_review_count: 0
|
|
86
|
+
dismiss_stale_reviews: true
|
|
87
|
+
require_code_owner_reviews: false
|
|
88
|
+
required_status_checks:
|
|
89
|
+
# Context names must match the check-run names GitHub records exactly —
|
|
90
|
+
# that's each job's `name:` field, NOT "workflow / job". A check must
|
|
91
|
+
# also have run on the repo at least once before it can be required.
|
|
92
|
+
#
|
|
93
|
+
# GOTCHA (observed on clauster's first push): when this file ships in a
|
|
94
|
+
# repo's FIRST push, none of these contexts have reported yet, and the
|
|
95
|
+
# Settings App's branch-protection PUT is rejected wholesale — labels /
|
|
96
|
+
# description / merge settings still apply (different API calls), but
|
|
97
|
+
# protection silently does NOT. Fix: once every listed check has run
|
|
98
|
+
# once, re-push this file (a no-op edit is enough) to re-apply.
|
|
99
|
+
#
|
|
100
|
+
# Aggregator pattern (mirrors the family):
|
|
101
|
+
# - `ci required checks passed` — ci.yml aggregator (needs: tests, build)
|
|
102
|
+
# - `security required checks passed` — security.yml aggregator
|
|
103
|
+
# (needs: changes, codeql, gitleaks,
|
|
104
|
+
# trivy-fs, trivy-image, zizmor,
|
|
105
|
+
# dependency-review)
|
|
106
|
+
# - `conventional PR title` — pr-title.yml (gates the squash subject
|
|
107
|
+
# that Release Please parses)
|
|
108
|
+
# - `lint` — lint.yml (ruff + pyright + the
|
|
109
|
+
# Markdown/YAML doc-lint; we gate it,
|
|
110
|
+
# the family leaves lint non-required).
|
|
111
|
+
# Direct job name, so renaming that job
|
|
112
|
+
# means updating this list too.
|
|
113
|
+
#
|
|
114
|
+
# `OSSF Scorecard` is intentionally NOT required: scorecard.yml only
|
|
115
|
+
# triggers on push + schedule + branch_protection_rule, so requiring it
|
|
116
|
+
# would leave every PR perpetually pending.
|
|
117
|
+
strict: true
|
|
118
|
+
contexts:
|
|
119
|
+
- "ci required checks passed"
|
|
120
|
+
- "security required checks passed"
|
|
121
|
+
- "conventional PR title"
|
|
122
|
+
- "lint"
|
|
123
|
+
# `true` enforces branch protection for admins too — OpenSSF Scorecard's
|
|
124
|
+
# Branch-Protection check rewards this. NB the Settings App rejects `false`
|
|
125
|
+
# here silently (repository-settings/app#923): use `null` to disable,
|
|
126
|
+
# `true` to enforce. Trade-off: this removes the admin-merge bypass we used
|
|
127
|
+
# for the job-rename in #66, so a future required-context change can't be
|
|
128
|
+
# force-merged the same way — revert to `null` if you need that escape hatch.
|
|
129
|
+
#
|
|
130
|
+
# ⚠️ CONFIRMED (A/B test, 2026-06-02): the Settings App does NOT apply this
|
|
131
|
+
# field on an already-protected branch. It reconciles other sections on push
|
|
132
|
+
# (security log: settings[bot] `repo.change_merge_setting`, probot/14.3.2) but
|
|
133
|
+
# leaves enforce_admins untouched — #70's null→true never took. Set it directly:
|
|
134
|
+
# gh api --method POST repos/schubydoo/clauster/branches/main/protection/enforce_admins
|
|
135
|
+
# (DELETE to disable) and re-GET to verify. Keep this `true` so the desired
|
|
136
|
+
# state is still declared, but don't trust the App to apply it.
|
|
137
|
+
enforce_admins: true
|
|
138
|
+
required_linear_history: true
|
|
139
|
+
allow_force_pushes: false
|
|
140
|
+
allow_deletions: false
|
|
141
|
+
required_conversation_resolution: true
|
|
142
|
+
# Same `null`-vs-empty-array gotcha; explicit `null` for safety.
|
|
143
|
+
restrictions: null
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
concurrency:
|
|
12
|
+
group: ci-${{ github.workflow }}-${{ github.head_ref || github.ref }}
|
|
13
|
+
cancel-in-progress: true
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
# --------------------------------------------------------------------
|
|
17
|
+
# Unit tests across the supported Python versions (Linux only — clauster
|
|
18
|
+
# targets a Linux/NAS host; psutil/proc + bridge management aren't
|
|
19
|
+
# exercised on macOS/Windows). The 96% coverage gate lives in
|
|
20
|
+
# pyproject [tool.pytest.ini_options] addopts (--cov-fail-under=96), so
|
|
21
|
+
# a bare `pytest` enforces it on every cell.
|
|
22
|
+
# --------------------------------------------------------------------
|
|
23
|
+
tests:
|
|
24
|
+
name: tests (${{ matrix.os }}, ${{ matrix.python-version }})
|
|
25
|
+
runs-on: ${{ matrix.os }}
|
|
26
|
+
timeout-minutes: 15
|
|
27
|
+
# Ubuntu, macOS, and Windows are all required (spec target: tri-platform).
|
|
28
|
+
# macOS + Windows were brought green via a .cmd entrypoint for the test stubs
|
|
29
|
+
# plus cross-platform guards; they now block merges like Linux, so a future
|
|
30
|
+
# regression on any platform can't silently rot the way these 29 once did.
|
|
31
|
+
strategy:
|
|
32
|
+
fail-fast: false
|
|
33
|
+
# Spec target is Ubuntu + macOS + Windows (conductor_spec_v3 §"Ubuntu,
|
|
34
|
+
# macOS, Windows" + 4-runner PyInstaller matrix; psutil was chosen for
|
|
35
|
+
# cross-platform liveness). Full Python sweep on every OS (3 × 4 = 12
|
|
36
|
+
# cells) so a version-specific portability bug can't hide on an untested
|
|
37
|
+
# OS×version combo. Public repo → Actions minutes are free; cells run in
|
|
38
|
+
# parallel. Only Linux enforces the coverage gate (see the pytest step).
|
|
39
|
+
matrix:
|
|
40
|
+
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
41
|
+
python-version: ["3.11", "3.12", "3.13", "3.14"]
|
|
42
|
+
steps:
|
|
43
|
+
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
|
44
|
+
with:
|
|
45
|
+
persist-credentials: false
|
|
46
|
+
- name: Set up uv
|
|
47
|
+
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
|
|
48
|
+
with:
|
|
49
|
+
enable-cache: true
|
|
50
|
+
# Matrix values flow through env vars (not inlined into `run:`) so zizmor's
|
|
51
|
+
# template-injection analysis doesn't flag the shell blocks. The matrix is
|
|
52
|
+
# hard-coded above, not derived from any PR-controlled input. `shell: bash`
|
|
53
|
+
# keeps one script across all three runners (Git Bash ships on Windows).
|
|
54
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
55
|
+
shell: bash
|
|
56
|
+
env:
|
|
57
|
+
PYTHON_VERSION: ${{ matrix.python-version }}
|
|
58
|
+
run: uv python install "$PYTHON_VERSION"
|
|
59
|
+
- name: Install
|
|
60
|
+
shell: bash
|
|
61
|
+
env:
|
|
62
|
+
PYTHON_VERSION: ${{ matrix.python-version }}
|
|
63
|
+
run: uv sync --extra dev --python "$PYTHON_VERSION"
|
|
64
|
+
# Linux enforces the 96% coverage gate (pyproject addopts). macOS + Windows
|
|
65
|
+
# still REPORT coverage (term-missing, for visibility into platform-specific
|
|
66
|
+
# gaps) but don't gate on it — `--cov-fail-under=0` overrides the addopts
|
|
67
|
+
# threshold — since branches are inherently platform-specific (Linux jiffies
|
|
68
|
+
# procStart, Windows O_BINARY/WNOHANG guards) and no single OS reaches 96%.
|
|
69
|
+
- name: pytest
|
|
70
|
+
shell: bash
|
|
71
|
+
env:
|
|
72
|
+
PYTHON_VERSION: ${{ matrix.python-version }}
|
|
73
|
+
RUNNER_OS: ${{ runner.os }}
|
|
74
|
+
run: |
|
|
75
|
+
if [ "$RUNNER_OS" = "Linux" ]; then
|
|
76
|
+
uv run --python "$PYTHON_VERSION" pytest -q \
|
|
77
|
+
--cov-report=xml --junitxml=junit.xml -o junit_family=legacy
|
|
78
|
+
else
|
|
79
|
+
uv run --python "$PYTHON_VERSION" pytest -q --cov-fail-under=0
|
|
80
|
+
fi
|
|
81
|
+
# Upload coverage + test results to Codecov from a single Linux cell (the
|
|
82
|
+
# gate platform). Linux is where the 96% number is measured; macOS/Windows
|
|
83
|
+
# coverage is platform-partial by design (see the pytest step), so uploading
|
|
84
|
+
# them would only muddy the trend. Token-based upload (CODECOV_TOKEN secret)
|
|
85
|
+
# → no `id-token` permission needed. Both uploads are non-blocking: Codecov
|
|
86
|
+
# isn't a required check and upload flakiness must never fail CI
|
|
87
|
+
# (fail_ci_if_error: false). Both use `!cancelled()` so a FAILED pytest run
|
|
88
|
+
# still uploads — pytest writes coverage.xml/junit.xml before the gate check,
|
|
89
|
+
# so a coverage drop shows up in the Codecov trend precisely when it matters,
|
|
90
|
+
# and failed tests still feed Codecov's flaky-test analytics. Test results go
|
|
91
|
+
# through codecov-action with `report_type: test_results` (the standalone
|
|
92
|
+
# test-results-action is deprecated), so we pin a single action SHA.
|
|
93
|
+
- name: Upload coverage to Codecov
|
|
94
|
+
if: ${{ !cancelled() && runner.os == 'Linux' && matrix.python-version == '3.13' }}
|
|
95
|
+
uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1
|
|
96
|
+
with:
|
|
97
|
+
token: ${{ secrets.CODECOV_TOKEN }}
|
|
98
|
+
files: coverage.xml
|
|
99
|
+
fail_ci_if_error: false
|
|
100
|
+
- name: Upload test results to Codecov
|
|
101
|
+
if: ${{ !cancelled() && runner.os == 'Linux' && matrix.python-version == '3.13' }}
|
|
102
|
+
uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1
|
|
103
|
+
with:
|
|
104
|
+
token: ${{ secrets.CODECOV_TOKEN }}
|
|
105
|
+
files: junit.xml
|
|
106
|
+
report_type: test_results
|
|
107
|
+
fail_ci_if_error: false
|
|
108
|
+
|
|
109
|
+
# --------------------------------------------------------------------
|
|
110
|
+
# Build sdist + wheel. Cheap; runs on every push and PR so we always
|
|
111
|
+
# know the package is shippable.
|
|
112
|
+
# --------------------------------------------------------------------
|
|
113
|
+
build:
|
|
114
|
+
name: build sdist + wheel
|
|
115
|
+
runs-on: ubuntu-latest
|
|
116
|
+
timeout-minutes: 5
|
|
117
|
+
steps:
|
|
118
|
+
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
|
119
|
+
with:
|
|
120
|
+
persist-credentials: false
|
|
121
|
+
- name: Set up uv
|
|
122
|
+
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
|
|
123
|
+
with:
|
|
124
|
+
enable-cache: true
|
|
125
|
+
- name: Set up Python 3.13
|
|
126
|
+
run: uv python install 3.13
|
|
127
|
+
- name: Build
|
|
128
|
+
run: uv build
|
|
129
|
+
- name: Upload dist artefacts
|
|
130
|
+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
131
|
+
with:
|
|
132
|
+
name: dist
|
|
133
|
+
path: dist/
|
|
134
|
+
retention-days: 7
|
|
135
|
+
if-no-files-found: error
|
|
136
|
+
|
|
137
|
+
# --------------------------------------------------------------------
|
|
138
|
+
# Required-checks aggregator. ONE check for branch protection to gate
|
|
139
|
+
# on, regardless of how many underlying jobs exist. `if: always()` so it
|
|
140
|
+
# runs even when a need failed; jq fails the aggregator iff any listed
|
|
141
|
+
# job is not success-or-skipped. Add a new ci.yml job by appending to
|
|
142
|
+
# `needs:` — branch protection lists only this one name.
|
|
143
|
+
# --------------------------------------------------------------------
|
|
144
|
+
ci-required-checks-passed:
|
|
145
|
+
name: ci required checks passed
|
|
146
|
+
if: always()
|
|
147
|
+
needs:
|
|
148
|
+
- tests
|
|
149
|
+
- build
|
|
150
|
+
runs-on: ubuntu-latest
|
|
151
|
+
timeout-minutes: 2
|
|
152
|
+
steps:
|
|
153
|
+
- name: Check required jobs passed
|
|
154
|
+
env:
|
|
155
|
+
NEEDS_JSON: ${{ toJSON(needs) }}
|
|
156
|
+
run: |
|
|
157
|
+
failing=$(echo "$NEEDS_JSON" | jq -r '
|
|
158
|
+
to_entries[]
|
|
159
|
+
| select(.value.result != "success" and .value.result != "skipped")
|
|
160
|
+
| "\(.key): \(.value.result)"
|
|
161
|
+
')
|
|
162
|
+
if [ -n "$failing" ]; then
|
|
163
|
+
echo "Failed or cancelled jobs:"
|
|
164
|
+
echo "$failing"
|
|
165
|
+
exit 1
|
|
166
|
+
fi
|
|
167
|
+
echo "All required jobs passed (or were intentionally skipped)."
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
name: Lint
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
concurrency:
|
|
12
|
+
group: lint-${{ github.workflow }}-${{ github.head_ref || github.ref }}
|
|
13
|
+
cancel-in-progress: true
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
lint:
|
|
17
|
+
# This job name is a REQUIRED status check — it must match the context list
|
|
18
|
+
# in .github/settings.yml exactly (renaming means updating that list too).
|
|
19
|
+
# Runs ruff (check + format), pyright, and the Markdown + YAML doc-lint
|
|
20
|
+
# (scripts/lint-docs.sh).
|
|
21
|
+
name: lint
|
|
22
|
+
runs-on: ubuntu-latest
|
|
23
|
+
timeout-minutes: 5
|
|
24
|
+
steps:
|
|
25
|
+
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
|
26
|
+
with:
|
|
27
|
+
persist-credentials: false
|
|
28
|
+
- name: Set up uv
|
|
29
|
+
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
|
|
30
|
+
with:
|
|
31
|
+
enable-cache: true
|
|
32
|
+
- name: Set up Python 3.13
|
|
33
|
+
run: uv python install 3.13
|
|
34
|
+
- name: Install
|
|
35
|
+
run: uv sync --extra dev --python 3.13
|
|
36
|
+
- name: ruff check
|
|
37
|
+
run: uv run ruff check .
|
|
38
|
+
- name: ruff format --check
|
|
39
|
+
run: uv run ruff format --check .
|
|
40
|
+
- name: pyright
|
|
41
|
+
run: uv run pyright src/clauster
|
|
42
|
+
# Node for markdownlint-cli2: pin the runtime (ubuntu-latest's bundled Node
|
|
43
|
+
# drifts across image refreshes) and install the version-pinned, integrity-
|
|
44
|
+
# checked package from the lockfile via `npm ci`. yamllint comes from
|
|
45
|
+
# `uv sync --extra dev` above.
|
|
46
|
+
- name: Set up Node 20
|
|
47
|
+
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
|
48
|
+
with:
|
|
49
|
+
node-version: "20"
|
|
50
|
+
cache: npm
|
|
51
|
+
- name: Install Node deps
|
|
52
|
+
run: npm ci
|
|
53
|
+
# Markdown + YAML lint — the same script developers run locally.
|
|
54
|
+
- name: markdown + yaml lint
|
|
55
|
+
run: bash scripts/lint-docs.sh
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
name: PR Title
|
|
2
|
+
|
|
3
|
+
# Validate that the pull-request title follows Conventional Commits. Because the
|
|
4
|
+
# repo squash-merges, the PR title becomes the squashed commit subject — which
|
|
5
|
+
# release-please parses to decide version bumps and CHANGELOG sections.
|
|
6
|
+
|
|
7
|
+
on:
|
|
8
|
+
pull_request:
|
|
9
|
+
types: [opened, edited, reopened, synchronize]
|
|
10
|
+
|
|
11
|
+
permissions:
|
|
12
|
+
contents: read
|
|
13
|
+
|
|
14
|
+
concurrency:
|
|
15
|
+
group: pr-title-${{ github.event.pull_request.number }}
|
|
16
|
+
cancel-in-progress: true
|
|
17
|
+
|
|
18
|
+
jobs:
|
|
19
|
+
validate:
|
|
20
|
+
name: conventional PR title
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
timeout-minutes: 5
|
|
23
|
+
permissions:
|
|
24
|
+
pull-requests: read
|
|
25
|
+
steps:
|
|
26
|
+
- uses: amannn/action-semantic-pull-request@48f256284bd46cdaab1048c3721360e808335d50 # v6.1.1
|
|
27
|
+
env:
|
|
28
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
29
|
+
with:
|
|
30
|
+
types: |
|
|
31
|
+
feat
|
|
32
|
+
fix
|
|
33
|
+
perf
|
|
34
|
+
revert
|
|
35
|
+
docs
|
|
36
|
+
chore
|
|
37
|
+
ci
|
|
38
|
+
build
|
|
39
|
+
test
|
|
40
|
+
refactor
|
|
41
|
+
style
|