dnsight 0.1.1__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.
@@ -0,0 +1,29 @@
1
+ name: Setup Environment
2
+ description: Installs just, uv and python
3
+
4
+ inputs:
5
+ python-version:
6
+ description: Python version to use
7
+ required: false
8
+ default: '3.14'
9
+ install-deps:
10
+ description: Whether to run just install-ci
11
+ required: false
12
+ default: 'true'
13
+
14
+ runs:
15
+ using: composite
16
+ steps:
17
+ - name: Install just
18
+ uses: extractions/setup-just@f8a3cce218d9f83db3a2ecd90e41ac3de6cdfd9b # v3
19
+
20
+ - name: Install uv
21
+ uses: astral-sh/setup-uv@5a095e7a2014a4212f075830d4f7277575a9d098 # v7.3.1
22
+ with:
23
+ enable-cache: true
24
+ python-version: ${{ inputs.python-version }}
25
+
26
+ - name: Install dependencies
27
+ if: inputs.install-deps == 'true'
28
+ shell: bash
29
+ run: just install-ci
@@ -0,0 +1,17 @@
1
+ version: 2
2
+
3
+ updates:
4
+ # Keep GitHub Actions SHA pins up to date
5
+ - package-ecosystem: github-actions
6
+ directory: /
7
+ schedule:
8
+ interval: weekly
9
+ groups:
10
+ actions:
11
+ patterns: ['*'] # group all action updates into one PR
12
+
13
+ # Keep Python dependencies up to date
14
+ - package-ecosystem: uv
15
+ directory: /
16
+ schedule:
17
+ interval: weekly
@@ -0,0 +1,63 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ # Linting and type checking - only run on latest python v
11
+ check:
12
+ name: Check
13
+ runs-on: ubuntu-latest
14
+ permissions:
15
+ contents: read # checkout
16
+ steps:
17
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
18
+ with:
19
+ fetch-depth: 0
20
+
21
+ - uses: ./.github/actions/setup
22
+
23
+ - name: Run Checks
24
+ shell: bash
25
+ run: just check
26
+
27
+ # Tests - matrix across Python versions
28
+ test:
29
+ name: Test (Python ${{ matrix.python-version }})
30
+ runs-on: ubuntu-latest
31
+ permissions:
32
+ contents: read # checkout
33
+ strategy:
34
+ fail-fast: false # run all versions even if one fails
35
+ matrix:
36
+ python-version: ['3.11', '3.12', '3.13', '3.14']
37
+ steps:
38
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
39
+ with:
40
+ fetch-depth: 0
41
+
42
+ - uses: ./.github/actions/setup
43
+ with:
44
+ python-version: ${{ matrix.python-version }}
45
+
46
+ - name: Run tests
47
+ run: just test
48
+
49
+ - name: Upload coverage
50
+ if: github.event_name == 'push' && !cancelled() && matrix.python-version ==
51
+ '3.14'
52
+ uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5.5.2
53
+ with:
54
+ files: coverage.xml
55
+ token: ${{ secrets.CODECOV_TOKEN }}
56
+
57
+ - name: Upload test results to Codecov
58
+ if: github.event_name == 'push' && !cancelled() && matrix.python-version ==
59
+ '3.14'
60
+ uses: codecov/test-results-action@0fa95f0e1eeaafde2c782583b36b28ad0d8c77d3 # v1.2.1
61
+ with:
62
+ files: junit.xml
63
+ token: ${{ secrets.CODECOV_TOKEN }}
@@ -0,0 +1,25 @@
1
+ name: Publish
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ jobs:
8
+ publish:
9
+ runs-on: ubuntu-latest
10
+ environment: pypi
11
+ permissions:
12
+ contents: read # required for checkout to clone the repo
13
+ id-token: write # required for PyPI trusted publishing
14
+ steps:
15
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
16
+ with:
17
+ fetch-depth: 0 # required for hatch-vcs to resolve version from tag
18
+
19
+ - uses: ./.github/actions/setup
20
+
21
+ - name: Build
22
+ run: just build
23
+
24
+ - name: Publish to PyPI
25
+ run: uv publish
@@ -0,0 +1,48 @@
1
+ ### OS / editor
2
+ .DS_Store
3
+
4
+ ### Python bytecode
5
+ __pycache__/
6
+ *.py[codz]
7
+ *$py.class
8
+
9
+ ### Packaging / build artifacts
10
+ build/
11
+ dist/
12
+ *.egg-info/
13
+ *.egg
14
+ .Python
15
+
16
+ ### Virtual environments
17
+ .venv/
18
+ venv/
19
+ env/
20
+ ENV/
21
+
22
+ ### Tests / coverage
23
+ .pytest_cache/
24
+ .coverage
25
+ .coverage.*
26
+ htmlcov/
27
+ coverage.xml
28
+ cover/
29
+ junit.xml
30
+
31
+ ### Type checking / linting
32
+ .mypy_cache/
33
+ .dmypy.json
34
+ dmypy.json
35
+ .ruff_cache/
36
+
37
+ ### Local env / secrets
38
+ .env
39
+ .envrc
40
+ .pypirc
41
+
42
+ ### Cursor
43
+ .cursorignore
44
+ .cursorindexingignore
45
+
46
+
47
+ ### Archive
48
+ archive/
@@ -0,0 +1,15 @@
1
+ repos:
2
+ # Make check
3
+ - repo: local
4
+ hooks:
5
+ - id: check
6
+ name: check
7
+ entry: just check
8
+ language: system
9
+ pass_filenames: false
10
+
11
+ # Aikido security scanner (secrets, passwords, API keys)
12
+ - repo: https://github.com/AikidoSec/pre-commit
13
+ rev: 77ecd16894235d3260203df342995eda0f14c5b5
14
+ hooks:
15
+ - id: aikido-local-scanner
@@ -0,0 +1 @@
1
+ 3.14
@@ -0,0 +1,32 @@
1
+ {
2
+ "sonarlint.connectedMode.project": {
3
+ "connectionId": "dnsight",
4
+ "projectKey": "dnsight_dnsight"
5
+ },
6
+ "cSpell.words": [
7
+ "addopts",
8
+ "asyncio",
9
+ "dmarc",
10
+ "dnsight",
11
+ "dnspython",
12
+ "htmlcov",
13
+ "httpx",
14
+ "isort",
15
+ "jsonschema",
16
+ "junitxml",
17
+ "mypy",
18
+ "pipx",
19
+ "pycache",
20
+ "pydantic",
21
+ "pypi",
22
+ "pytest",
23
+ "pyyaml",
24
+ "taplo",
25
+ "testpaths",
26
+ "typer",
27
+ "venv",
28
+ "whitelines",
29
+ "yamlfix"
30
+ ],
31
+ "python-envs.pythonProjects": []
32
+ }
@@ -0,0 +1,15 @@
1
+ extends: default
2
+
3
+ rules:
4
+ empty-lines:
5
+ max: 1 # allow up to 1 blank line (matches yamlfix whitelines = 1)
6
+ max-start: 0
7
+ max-end: 0
8
+ line-length:
9
+ max: 120
10
+ level: warning
11
+ truthy:
12
+ allowed-values: ['true', 'false']
13
+ check-keys: false
14
+ document-start:
15
+ present: false
dnsight-0.1.1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 DNSight
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
dnsight-0.1.1/PKG-INFO ADDED
@@ -0,0 +1,66 @@
1
+ Metadata-Version: 2.4
2
+ Name: dnsight
3
+ Version: 0.1.1
4
+ Summary: DNS, email, and web security hygiene SDK and CLI for DevOps and security engineers.
5
+ Project-URL: Homepage, https://github.com/dnsight/dnsight
6
+ Project-URL: Repository, https://github.com/dnsight/dnsight
7
+ Project-URL: Issues, https://github.com/dnsight/dnsight/issues
8
+ Author-email: Mikey Scott <mikey@middleauth.io>
9
+ License: MIT
10
+ License-File: LICENSE
11
+ Keywords: cli,devops,dkim,dmarc,dns,dnssec,security,spf
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Intended Audience :: System Administrators
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Programming Language :: Python :: 3.14
21
+ Classifier: Topic :: Internet :: Name Service (DNS)
22
+ Classifier: Topic :: Security
23
+ Classifier: Topic :: System :: Networking
24
+ Requires-Python: >=3.11
25
+ Requires-Dist: dnspython<3.0.0,>=2.8.0
26
+ Requires-Dist: httpx<0.29.0,>=0.28.1
27
+ Requires-Dist: pydantic<3.0.0,>=2.12.5
28
+ Requires-Dist: pyyaml<7.0.0,>=6.0.3
29
+ Requires-Dist: rich<15.0.0,>=14.3.3
30
+ Requires-Dist: typer<0.25.0,>=0.24.1
31
+ Description-Content-Type: text/markdown
32
+
33
+ # dnsight
34
+
35
+ [![CI](https://github.com/dnsight/dnsight/actions/workflows/ci.yaml/badge.svg)](https://github.com/dnsight/dnsight/actions/workflows/ci.yaml)
36
+ [![PyPI version](https://img.shields.io/pypi/v/dnsight)](https://pypi.org/project/dnsight/)
37
+ [![Python versions](https://img.shields.io/pypi/pyversions/dnsight)](https://pypi.org/project/dnsight/)
38
+ [![codecov](https://codecov.io/github/dnsight/dnsight/graph/badge.svg?token=B4BKEX1G8O)](https://codecov.io/github/dnsight/dnsight)
39
+ [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=dnsight_dnsight&metric=alert_status&token=479932a2a9cce01e25841c72ae83c300d5534029)](https://sonarcloud.io/summary/new_code?id=dnsight_dnsight)
40
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
41
+
42
+ > DNS, email, and web security hygiene SDK and CLI for DevOps and security engineers.
43
+
44
+ ## Installation
45
+
46
+ ```bash
47
+ # CLI
48
+ pipx install dnsight
49
+
50
+ # SDK only
51
+ pip install dnsight
52
+
53
+ # With uv
54
+ uv add dnsight
55
+ ```
56
+
57
+ ## Usage
58
+
59
+ ```bash
60
+ dnsight --help
61
+ dnsight --version
62
+ ```
63
+
64
+ ## License
65
+
66
+ MIT
@@ -0,0 +1,34 @@
1
+ # dnsight
2
+
3
+ [![CI](https://github.com/dnsight/dnsight/actions/workflows/ci.yaml/badge.svg)](https://github.com/dnsight/dnsight/actions/workflows/ci.yaml)
4
+ [![PyPI version](https://img.shields.io/pypi/v/dnsight)](https://pypi.org/project/dnsight/)
5
+ [![Python versions](https://img.shields.io/pypi/pyversions/dnsight)](https://pypi.org/project/dnsight/)
6
+ [![codecov](https://codecov.io/github/dnsight/dnsight/graph/badge.svg?token=B4BKEX1G8O)](https://codecov.io/github/dnsight/dnsight)
7
+ [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=dnsight_dnsight&metric=alert_status&token=479932a2a9cce01e25841c72ae83c300d5534029)](https://sonarcloud.io/summary/new_code?id=dnsight_dnsight)
8
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
9
+
10
+ > DNS, email, and web security hygiene SDK and CLI for DevOps and security engineers.
11
+
12
+ ## Installation
13
+
14
+ ```bash
15
+ # CLI
16
+ pipx install dnsight
17
+
18
+ # SDK only
19
+ pip install dnsight
20
+
21
+ # With uv
22
+ uv add dnsight
23
+ ```
24
+
25
+ ## Usage
26
+
27
+ ```bash
28
+ dnsight --help
29
+ dnsight --version
30
+ ```
31
+
32
+ ## License
33
+
34
+ MIT
dnsight-0.1.1/justfile ADDED
@@ -0,0 +1,85 @@
1
+ # Default recipe — lists all available commands
2
+ default:
3
+ @just --list
4
+
5
+ # == Environment ================================================================
6
+
7
+ # Install development dependencies
8
+ install:
9
+ uv sync
10
+ uv pip install -e .
11
+
12
+ # Install from lockfile only (CI)
13
+ install-ci:
14
+ uv sync --frozen
15
+
16
+ # Regenerate uv.lock
17
+ lock:
18
+ uv lock
19
+
20
+ # == Code quality ===============================================================
21
+
22
+ # Run all checks (lint, format, typecheck, config files)
23
+ check:
24
+ #!/usr/bin/env bash
25
+ set +e
26
+ failed=0
27
+ uv run ruff check src/ tests/ || { echo "FAILED: ruff check"; failed=1; }
28
+ uv run ruff format --check src/ tests/ || { echo "FAILED: ruff format"; failed=1; }
29
+ uv run mypy src/ || { echo "FAILED: mypy"; failed=1; }
30
+ uv run yamllint $(find . \( -name "*.yml" -o -name "*.yaml" \) | grep -vE "^\./(.venv|src|tests)/") || { echo "FAILED: yamllint"; failed=1; }
31
+ uv run check-jsonschema --builtin-schema github-workflows .github/workflows/*.yaml || { echo "FAILED: check-jsonschema"; failed=1; }
32
+ uv run taplo check $(find . -name "*.toml" | grep -vE "^\./(.venv|src|tests)/") || { echo "FAILED: taplo"; failed=1; }
33
+ exit $failed
34
+
35
+ # Auto-fix lint and formatting issues
36
+ fix:
37
+ #!/usr/bin/env bash
38
+ set +e
39
+ failed=0
40
+ uv run ruff check --fix src/ tests/ || { echo "FAILED: ruff check"; failed=1; }
41
+ uv run ruff format src/ tests/ || { echo "FAILED: ruff format"; failed=1; }
42
+ uv run yamlfix $(find . \( -name "*.yml" -o -name "*.yaml" \) | grep -vE "^\./(.venv|src|tests)/") || { echo "FAILED: yamlfix"; failed=1; }
43
+ uv run taplo fmt $(find . -name "*.toml" | grep -vE "^\./(.venv|src|tests)/") || { echo "FAILED: taplo"; failed=1; }
44
+ exit $failed
45
+
46
+ # == Pre-Commit =================================================================
47
+
48
+ # Install pre-commit hooks
49
+ pre-install:
50
+ uv run pre-commit install
51
+
52
+ # Run pre-commit on all files
53
+ pre:
54
+ uv run pre-commit run --all-files
55
+
56
+ # == Testing ====================================================================
57
+
58
+ # Run test suite with coverage
59
+ test *args:
60
+ uv run pytest {{args}}
61
+
62
+ # == Build and publish ==========================================================
63
+
64
+ # Build distribution packages
65
+ build:
66
+ just clean
67
+ uv build
68
+
69
+ # Publish to PyPI after build
70
+ publish:
71
+ just build
72
+ uv publish
73
+
74
+ # == Maintenance ================================================================
75
+
76
+ # Remove build artifacts and cache
77
+ clean:
78
+ rm -rf dist/ .coverage htmlcov/ .pytest_cache/ .mypy_cache/ .ruff_cache/
79
+ find . -type d -name __pycache__ -exec rm -rf {} +
80
+ find . -type f -name "*.pyc" -delete
81
+
82
+ # Re-create venv from existing lockfile
83
+ re-venv:
84
+ uv venv --clear
85
+ just install
@@ -0,0 +1,124 @@
1
+ [project]
2
+ name = "dnsight"
3
+ dynamic = ["version"]
4
+ description = "DNS, email, and web security hygiene SDK and CLI for DevOps and security engineers."
5
+ readme = "README.md"
6
+ license = { text = "MIT" }
7
+ requires-python = ">=3.11"
8
+ authors = [{ name = "Mikey Scott", email = "mikey@middleauth.io" }]
9
+ keywords = [
10
+ "dns",
11
+ "dmarc",
12
+ "spf",
13
+ "dkim",
14
+ "dnssec",
15
+ "security",
16
+ "cli",
17
+ "devops",
18
+ ]
19
+ classifiers = [
20
+ "Development Status :: 3 - Alpha",
21
+ "Intended Audience :: Developers",
22
+ "Intended Audience :: System Administrators",
23
+ "License :: OSI Approved :: MIT License",
24
+ "Operating System :: OS Independent",
25
+ "Programming Language :: Python :: 3.11",
26
+ "Programming Language :: Python :: 3.12",
27
+ "Programming Language :: Python :: 3.13",
28
+ "Programming Language :: Python :: 3.14",
29
+ "Topic :: Internet :: Name Service (DNS)",
30
+ "Topic :: Security",
31
+ "Topic :: System :: Networking",
32
+ ]
33
+
34
+ dependencies = [
35
+ "dnspython>=2.8.0,<3.0.0",
36
+ "httpx>=0.28.1,<0.29.0",
37
+ "pydantic>=2.12.5,<3.0.0",
38
+ "pyyaml>=6.0.3,<7.0.0",
39
+ "rich>=14.3.3,<15.0.0",
40
+ "typer>=0.24.1,<0.25.0",
41
+ ]
42
+
43
+ [project.urls]
44
+ "Homepage" = "https://github.com/dnsight/dnsight"
45
+ "Repository" = "https://github.com/dnsight/dnsight"
46
+ "Issues" = "https://github.com/dnsight/dnsight/issues"
47
+
48
+
49
+ [project.scripts]
50
+ dnsight = "dnsight.cli:app"
51
+
52
+ [build-system]
53
+ requires = ["hatchling", "hatch-vcs"]
54
+ build-backend = "hatchling.build"
55
+
56
+ [tool.hatch.version]
57
+ source = "vcs"
58
+
59
+ [tool.hatch.build.targets.wheel]
60
+ packages = ["src/dnsight"]
61
+
62
+ [tool.uv]
63
+ required-version = ">=0.10.7"
64
+
65
+ [dependency-groups]
66
+ dev = [
67
+ "hatch-vcs>=0.5.0,<0.6.0",
68
+ "pre-commit>=4.0.0,<5.0.0",
69
+ "mypy>=1.19.1,<2.0.0",
70
+ "pytest>=9.0.2,<10.0.0",
71
+ "pytest-asyncio>=1.3.0,<2.0.0",
72
+ "pytest-cov>=7.0.0,<8.0.0",
73
+ "types-pyyaml>=6.0.12.20250915,<7.0.0.0",
74
+ "yamllint>=1.38.0,<2.0.0",
75
+ "yamlfix>=1.19.1,<2.0.0",
76
+ "check-jsonschema>=0.37.0,<1.0.0",
77
+ "taplo>=0.9.3,<1.0.0",
78
+ "ruff>=0.8.6,<1.0.0",
79
+ ]
80
+
81
+ [tool.ruff]
82
+ target-version = "py311"
83
+ line-length = 88
84
+ indent-width = 4
85
+
86
+ [tool.ruff.lint]
87
+ select = ["E", "F", "I", "UP", "B", "SIM"]
88
+ ignore = ["E501"]
89
+
90
+ [tool.ruff.format]
91
+ quote-style = "double"
92
+ indent-style = "space"
93
+ skip-magic-trailing-comma = true
94
+ docstring-code-format = true
95
+
96
+ [tool.ruff.lint.isort]
97
+ known-first-party = ["dnsight"]
98
+ lines-after-imports = 2
99
+ section-order = [
100
+ "standard-library",
101
+ "third-party",
102
+ "first-party",
103
+ "local-folder",
104
+ ]
105
+ force-sort-within-sections = true
106
+ split-on-trailing-comma = false
107
+
108
+ [tool.mypy]
109
+ strict = true
110
+
111
+ [tool.pytest.ini_options]
112
+ asyncio_mode = "auto"
113
+ testpaths = ["tests"]
114
+ addopts = "--cov=src/dnsight --cov-report=term-missing --cov-report=xml --junitxml=junit.xml -o junit_family=legacy"
115
+
116
+ [tool.coverage.run]
117
+ source = ["src/dnsight"]
118
+ omit = ["*/tests/**"]
119
+
120
+ [tool.yamlfix]
121
+ whitelines = 1 # preserve single blank lines between list items etc.
122
+ section_whitelines = 1 # preserve blank lines between top-level sections
123
+ explicit_start = false # remove --- at top of file if you don't want it
124
+ sequence_style = "keep_style" # don't convert between flow/block list styles
@@ -0,0 +1,11 @@
1
+ from importlib.metadata import PackageNotFoundError
2
+
3
+
4
+ try:
5
+ from importlib.metadata import version
6
+
7
+ raw = version("dnsight")
8
+ # Show clean version for releases, "dev" for untagged local builds
9
+ __version__ = raw if "dev" not in raw else "dev.local"
10
+ except PackageNotFoundError:
11
+ __version__ = "dev"
@@ -0,0 +1,34 @@
1
+ from rich.console import Console
2
+ import typer
3
+
4
+ from dnsight import __version__
5
+
6
+
7
+ app = typer.Typer(
8
+ name="dnsight",
9
+ help="DNS, email, and web security hygiene toolkit.",
10
+ no_args_is_help=True,
11
+ )
12
+
13
+ console = Console()
14
+
15
+
16
+ def _version_callback(value: bool) -> None:
17
+ if value:
18
+ typer.echo(f"dnsight version: {__version__}")
19
+ raise typer.Exit()
20
+
21
+
22
+ @app.callback()
23
+ def main(
24
+ _version: bool | None = typer.Option(
25
+ None,
26
+ "--version",
27
+ "-v",
28
+ callback=_version_callback,
29
+ is_eager=True,
30
+ help="Show version and exit.",
31
+ ),
32
+ ) -> None:
33
+ # This uses callback pattern
34
+ pass
@@ -0,0 +1,18 @@
1
+ from typer.testing import CliRunner
2
+
3
+ from dnsight.cli import app
4
+
5
+
6
+ runner = CliRunner()
7
+
8
+
9
+ def test_help():
10
+ result = runner.invoke(app, ["--help"])
11
+ assert result.exit_code == 0
12
+ assert "dnsight" in result.output
13
+
14
+
15
+ def test_version():
16
+ result = runner.invoke(app, ["--version"])
17
+ assert result.exit_code == 0
18
+ assert "dnsight" in result.output