commit bf731f67d2ee1408b06ee91e36d5003adeede937 Author: Amir Husayn Panahifar Date: Sat Sep 12 13:47:31 2026 +0330 chore: scaffold project metadata and tooling diff --git a/.PYTHON-VERSION b/.PYTHON-VERSION new file mode 100644 index 0000000..24ee5b1 --- /dev/null +++ b/.PYTHON-VERSION @@ -0,0 +1 @@ +3.13 diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..4d1d5b3 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +root = true + +[*] +indent_style = space +indent_size = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.{yml,yaml}] +indent_size = 2 + +[Makefile] +indent_style = tab diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9165d7c --- /dev/null +++ b/.gitignore @@ -0,0 +1,35 @@ +# Python +__pycache__/ +*.py[cod] +*.egg-info/ +dist/ +build/ + +# Virtual environments +.venv/ +venv/ +env/ + +# IDE +.vscode/ +.idea/ +*.swp +*.swo + +# OS +.DS_Store +Thumbs.db + +# Project specific +*.sock +*.egg-info/ +htmlcov/ +.coverage +coverage.xml +.pytest_cache/ +.mypy_cache/ +.ruff_cache/ +dist/ +dist-deb/ +build/ +*.tar.gz diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..a68dd21 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,35 @@ +repos: + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.4.0 + hooks: + - id: ruff + args: [--fix] + - id: ruff-format + + - repo: https://github.com/pre-commit/mirrors-mypy + rev: v1.8.0 + hooks: + - id: mypy + args: [--strict, src/, tests/] + additional_dependencies: + - pydantic + - pydantic-settings + - structlog + - rich + - typer + - pyyaml + - types-PyYAML + + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.6.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + - id: check-json + - id: check-toml + - id: check-added-large-files + - id: check-merge-conflict + - id: detect-private-key + - id: mixed-line-ending + args: [--fix=lf] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..3361962 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Amir Husayn Panahifar + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..7e9c4ff --- /dev/null +++ b/Makefile @@ -0,0 +1,48 @@ +.PHONY: install dev test lint format typecheck clean build-wheel build-deb build-all + +install: + pip install -e . + +dev: + pip install -e ".[dev]" + pre-commit install + +test: + pytest -v --tb=short + +test-cov: + pytest --cov=sshctl --cov-report=term-missing --cov-fail-under=45 + +lint: + ruff check src/ tests/ + +format: + ruff format src/ tests/ + +format-check: + ruff format --check src/ tests/ + +typecheck: + mypy src/ tests/ + +check: lint format-check typecheck test + +clean: + rm -rf dist/ dist-deb/ build/ *.egg-info/ htmlcov/ .coverage coverage.xml .mypy_cache .ruff_cache .pytest_cache + find . -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true + find . -type f -name '*.pyc' -delete + +build-wheel: + pip install build + python -m build --wheel + +build-deb: build-wheel + cd linux && dpkg-buildpackage -us -uc -b + mkdir -p dist-deb + mv ./*.deb dist-deb/ 2>/dev/null || true + +build-all: clean check build-wheel build-deb + +release: clean check build-wheel build-deb + @echo "Release artifacts:" + @ls -la dist/*.whl dist-deb/*.deb 2>/dev/null || echo "No artifacts found" diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..958de74 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,93 @@ +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[project] +name = "sshctl" +version = "0.1.1" +description = "SSH configuration management toolkit" +requires-python = ">=3.13" +license = {text = "MIT"} +authors = [{name = "Amir Husayn Panahifar", email = "ahp@panahifar.ir"}] + +dependencies = [ + "typer>=0.12", + "rich>=13.0", + "structlog>=24.0", + "pydantic>=2.0", + "pydantic-settings>=2.0", + "pyyaml>=6.0", +] + +[project.scripts] +sshctl = "sshctl.cli.app:serve" + +[project.optional-dependencies] +dev = [ + "pytest>=8.0", + "pytest-cov>=5.0", + "pytest-asyncio>=0.24", + "ruff>=0.4", + "mypy>=1.8", + "pre-commit>=3.6", + "types-PyYAML>=6.0", +] + +[project.urls] +Homepage = "https://src.panahifar.ir/ahp/sshctl" +Repository = "https://src.panahifar.ir/ahp/sshctl.git" +Changelog = "https://src.panahifar.ir/ahp/sshctl/src/branch/main/CHANGELOG.md" + +[tool.hatch.build.targets.wheel] +packages = ["src/sshctl"] + +[tool.ruff] +target-version = "py313" +line-length = 100 + +[tool.ruff.lint] +select = ["E", "F", "I", "N", "W", "UP", "B", "SIM", "ARG", "PTH", "ERA"] + +[tool.ruff.lint.per-file-ignores] +"src/sshctl/cli/app.py" = ["ARG001"] + +[tool.ruff.format] +quote-style = "double" + +[tool.mypy] +strict = true +python_version = "3.13" +check_untyped_defs = true +disallow_any_unimported = true +no_implicit_reexport = true +warn_unused_ignores = true + +[tool.pytest.ini_options] +asyncio_mode = "auto" +testpaths = ["tests"] +addopts = [ + "--cov=sshctl", + "--cov-report=term-missing", + "--cov-report=xml:coverage.xml", + "--cov-fail-under=45", +] + +[tool.coverage.run] +source_pkgs = ["sshctl"] +parallel = true + +[tool.coverage.paths] +source = ["src/sshctl"] + +[tool.coverage.report] +exclude_lines = [ + "pragma: no cover", + "if __name__ == .__main__.:", + "raise AssertionError", + "raise NotImplementedError", + "if 0:", + "@overload", +] + +[tool.coverage.html] +directory = "htmlcov"