# Copyright (c) CloudZero - ALL RIGHTS RESERVED - PROPRIETARY AND CONFIDENTIAL
# Unauthorized copying of this file and/or project, via any medium is strictly prohibited.
# Direct all questions to legal@cloudzero.com
#

[project]
name = "cz-ops-cli"
dynamic = ["version"]
description = "CloudZero Internal Operations command line interface."
readme = "README.md"
requires-python = ">=3.11,<3.14"
license = "LicenseRef-Proprietary"
license-files = ["LICENSE"]
authors = [
    { name = "CloudZero", email = "support@cloudzero.com" },
]
keywords = [
    "CloudZero",
    "czops",
    "ops",
    "cli"
]
classifiers = [
    "Development Status :: 4 - Beta",
    "Intended Audience :: Developers",
    "Natural Language :: English",
    "Programming Language :: Python :: 3.11",
    "Programming Language :: Python :: 3.12",
    "Programming Language :: Python :: 3.13",
    "Operating System :: MacOS",
    "Operating System :: Unix"
]

dependencies = [
    "cloudzero-identifiers>=1.9.2",
    "urllib3>=2.5.0",
    "boto3>=1.40.0",
    "botocore>=1.40.0",
    "pyyaml>=6.0.2",
    "pyaml>=25.6.1",
    "httpx>=0.28.1",
    "cachetools>=6.0.0",
    "mako>=1.3.10",
    "voluptuous>=0.15.2",
    "base58>=2.1.1",
    "tabulate>=0.9.0",
    "faker>=37.5.3",
    "toolz>=1.0.0",
    "numpy>=2.3.2",
    "packaging>=25.0",
    "pydantic>=2.11.7",
    "pydantic-settings>=2.9.1",
    "email-validator>=2.2.0",
    "rich>=14.1.0",
    "python-dateutil>=2.9.0.post0",
    "flask>=3.1.1",
    "flask-cors>=6.0.1",
    "pyjwt[crypto]>=2.10.1",
    "term-image>=0.7.2",
    "polars>=1.32.0",
    "cryptography>=45.0.4",
    "docopt-ng>=0.9.0",
    "tomli-w>=1.0.0",
]

[project.scripts]
czops = "src.main:run"

[project.urls]
Homepage = "https://github.com/Cloudzero/cz-ops-cli"

[build-system]
requires = ["build", "setuptools>=61.0.0", "wheel"]
build-backend = "setuptools.build_meta"

[tool.setuptools]
include-package-data = true

[tool.setuptools.packages.find]
where = ["."]
include = ["src*"]
namespaces = true

[tool.setuptools.package-data]
"src" = ["data/**/*"]

[tool.setuptools.dynamic]
version = {attr = "src.__version__"}

[tool.uv]
extra-index-url = ["https://pypi.cloudzero.com"]

[dependency-groups]
dev = [
    "pytest>=8.4.1",
    "pytest-cov>=6.2.1",
    "pytest-mock>=3.14.1",
    "pytest-env>=1.1.5",
    "s3pypi>=2.0.1",
    "ruff>=0.12.7",
    "coverage>=7.10.1",
    "tomli>=2.2.1",
    "build>=1.2.2.post1",
    "wheel>=0.45.1",
]

[tool.pytest.ini_options]
doctest_optionflags = ["ELLIPSIS", "IGNORE_EXCEPTION_DETAIL"]
env = [
    "NAMESPACE=TEST",
    "AWS_ACCESS_KEY_ID=testing",
    "AWS_SECRET_ACCESS_KEY=testing",
    "AWS_SESSION_TOKEN=testing",
    "AWS_DEFAULT_REGION=us-east-1",
]
markers = ["slow", "unit", "performance", "sns"]
addopts = "--cov-config=pyproject.toml --cov=src --cov=tests --doctest-modules --cov-report term --showlocals -vvv --ignore=tests/integration/"

[tool.ruff]
target-version = "py312"
line-length = 120
exclude = ["tests"]

[tool.ruff.lint]
select = [
    "E",    # pycodestyle: general style errors
    "F",    # pyflakes: undefined names, etc.
    "W",    # warning-like pycodestyle issues
    "I",    # isort: import sorting
    "C90",  # mccabe: complexity checking
    "N",    # pep8-naming: consistent naming
    "UP",   # pyupgrade: use modern syntax
    "B",    # bugbear: likely bugs and anti-patterns
    "A",    # builtins: shadowing builtins
    "S",    # bandit: security issues
    "TID",  # tidy imports: import rules
    "ARG",  # flake8-unused-arguments: dead args
    "SIM",  # flake8-simplify: overly complex patterns
    "RET",  # flake8-return: invalid or missing returns
]

ignore = [
    "E402",     # module level import not at top of file
    "E501",     # line length (use formatter instead, e.g. Black)
    "S101",     # `assert` usage, allowed in tests
    "S110",     # try-except-pass without logging
    "S113",     # probable use of requests without timeout
    "S201",     # flask debug mode
    "S311",     # standard pseudo-random generators unsuitable for crypto
    "S603",     # subprocess without shell equals true
    "S605",     # starting a process with a shell: possible injection
    "S606",     # starting a process without a shell
    "S607",     # starting a process with a partial executable path
    "S702",     # mako templates allow XSS attacks
    "N801",     # class name should use CapWords convention
    "N802",     # function name should be lowercase
    "N806",     # variable in function should be lowercase
    "N818",     # exception name should be named with an Error suffix
    "A",        # all shadowing builtin rules
    "B007",     # loop control variable not used within loop body
    "B008",     # do not use mutable default args (can be false positive in some cases)
    "B033",     # duplicate items in sets
    "C",        # all complexity rules
    "C901",     # function is too complex
    "RET504",   # unnecessary assignment before return (style preference)
    "RET505",   # unnecessary else after return statement (style preference)
    "RET506",   # unnecessary else after raise statement (style preference)
    "SIM102",   # collapsible if statements (readability preference)
    "SIM105",   # use contextlib.suppress instead of try-except-pass
    "SIM115",   # use context manager for opening files
    "SIM116",   # use dictionary instead of if-elif chain (readability preference)
    "ARG",      # all unused argument rules
]

#fixable = ["E", "F", "I", "UP", "TID", "SIM"]
#unfixable = ["B", "S"]  # avoid auto-fixing risky or security-sensitive issues

[tool.ruff.lint.per-file-ignores]
"tests/**/*" = ["S101", "PLR2004", "PLR0913", "S603", "SIM102", "SIM117", "S105", "S106", "S108"]  # allow assert, magic numbers, subprocess, hardcoded passwords/tokens, temp dirs, and style simplifications in tests
"test_*.py" = ["S101", "PLR2004", "PLR0913", "S603", "SIM102", "SIM117", "S105", "S106", "S108"]  # same for root level test files
"scripts/**/*" = ["S602", "S603", "S607", "S301", "S112"]      # allow subprocess, pickle usage, and silent exceptions in scripts

#[tool.ruff.lint.mccabe]
#max-complexity = 18  # increased complexity limit for existing functions

[tool.coverage.run]
source = ["src"]
omit = [
    "tests/*",
    "src/data/*",
    "*/tests/*",
    "*/__pycache__/*",
    "*/migrations/*"
]

[tool.coverage.report]
exclude_lines = [
    "pragma: no cover",
    "def __repr__",
    "raise AssertionError",
    "raise NotImplementedError",
    "if __name__ == .__main__.:",
    "if TYPE_CHECKING:",
    "@abstractmethod"
]
show_missing = true
fail_under = 35
