Skip to content

v1.0.0: Parallel requests, comprehensive CLI, and test suite#3

Merged
danielmeint merged 13 commits intomainfrom
feature/parallelize-requests
Jan 18, 2026
Merged

v1.0.0: Parallel requests, comprehensive CLI, and test suite#3
danielmeint merged 13 commits intomainfrom
feature/parallelize-requests

Conversation

@danielmeint
Copy link
Copy Markdown
Owner

@danielmeint danielmeint commented Jan 18, 2026

Summary

This release brings nextdnsctl to production-ready status with major new features:

  • Parallel API requests: New --concurrency option (default: 5, max: 20) for bulk operations
  • Enhanced CLI commands: list, export, clear for both denylist and allowlist
  • Profile name resolution: Use profile names instead of IDs (e.g., nextdnsctl denylist list "My Profile")
  • Dry-run mode: Preview changes with --dry-run before applying
  • Comprehensive test suite: 45 tests covering parsing, config, CLI, API resilience, and concurrency
  • CI/CD: GitHub Actions workflow with Python 3.10/3.11/3.12 matrix
  • Type safety: Full mypy compliance with type hints throughout

Breaking Changes

  • Minimum Python version raised from 3.6 to 3.10

Commits included

  • Add parallel API request support with --concurrency option
  • Add list command for denylist and allowlist
  • Add export, clear commands and --dry-run flag
  • Add support for profile names in addition to IDs
  • Refactor denylist/allowlist code to reduce duplication
  • Add security improvements and streaming imports
  • Add type hints and improve API URL handling
  • Add comprehensive test suite with CI integration
  • Fix mypy type checking errors
  • Centralize version to single source of truth for v1.0.0

Test plan

  • All 45 unit/integration tests pass
  • mypy: no type errors
  • flake8: no linting issues
  • Manual testing against live NextDNS API:
    • profile-list
    • denylist/allowlist add/remove/list
    • import/export from file
    • --dry-run mode
    • Profile resolution by name
    • --concurrency flag (sequential and parallel modes)

🤖 Generated with Claude Code


Note

Production-ready v1.0 with a revamped CLI, robust API client, and CI/test coverage.

  • Parallel bulk ops via --concurrency (with progress) and --dry-run; profile resolution by name or ID
  • New denylist/allowlist subcommands: list, export, clear (plus existing add/remove); streaming imports and improved parsing
  • Refactored, typed API client with resilient retries/backoff, 429 handling, proper URL joining; configurable --retry-* and --timeout
  • Config improvements: NEXTDNS_API_KEY env support and secure file perms; centralized version in __init__ (1.0.0)
  • Tests/CI: comprehensive unit/integration suite, requirements-dev.txt, GitHub Actions with Python 3.10–3.12 matrix; consolidated linting
  • Packaging/docs: setup.py reads version, requires Python ≥3.10; README expanded; .gitignore cleaned; removed IDE files

Written by Cursor Bugbot for commit 1b4fb3e. This will update automatically on new commits. Configure here.

danielmeint and others added 12 commits January 17, 2026 20:29
- Add --concurrency CLI option (range 1-20, default 5)
- Rewrite _perform_domain_operations() to use ThreadPoolExecutor
- Add progress bar for parallel mode using click.progressbar()
- Preserve verbose per-domain output in sequential mode (--concurrency 1)
- Handle rate limits gracefully by stopping new submissions and reporting skipped domains
- Print summary (completed/failed/skipped) after parallel operations

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add `denylist list <profile_id>` command to show all denylist entries
- Add `allowlist list <profile_id>` command to show all allowlist entries
- Support --active-only and --inactive-only filters
- Show inactive entries with "(inactive)" suffix
- Display total count in summary

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Export command:
- Add `denylist export` and `allowlist export` commands
- Export to file or stdout (with -)
- Support --active-only and --inactive-only filters

Clear command:
- Add `denylist clear` and `allowlist clear` commands
- Remove all entries from a list
- Require confirmation (skip with --yes/-y)

Dry-run flag:
- Add global --dry-run flag to show what would be done
- Works with add, remove, import, and clear commands
- Skips confirmation prompts in dry-run mode

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add _resolve_profile_id() helper that resolves profile name or ID
- Support case-insensitive profile name matching
- Cache profiles list in ctx.obj to avoid repeated API calls
- Show helpful error with available profiles when not found
- Update all commands to accept either profile name or ID

Example usage:
  nextdnsctl denylist list "My Profile"
  nextdnsctl denylist list abc123

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Bump version to 0.3.0
- Update README with comprehensive documentation for new features:
  - Parallel API requests with --concurrency option
  - list, export, clear commands for denylist/allowlist
  - --dry-run flag for previewing changes
  - Profile name support (in addition to IDs)
  - Global options documentation
  - Improved command reference

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Create generic API functions (get_domain_list, add_to_domain_list,
  remove_from_domain_list) that accept list_type parameter
- Keep original functions as thin wrappers for backwards compatibility
- Create shared command handlers (_handle_list_command, etc.) used by
  both denylist and allowlist commands
- Fix USER_AGENT version mismatch (0.2.0 -> 0.3.0)
- Net reduction of ~130 lines of duplicate code

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Security:
- Support NEXTDNS_API_KEY environment variable (priority over config file)
- Set secure file permissions (600) on config file
- Set secure directory permissions (700) on config directory

Import improvements:
- Stream file/URL content for memory efficiency with large files
- Support inline comments (e.g., "example.com # reason")
- Better comment and whitespace handling

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add comprehensive type hints to all modules (api.py, config.py, nextdnsctl.py)
- Use urllib.parse.urljoin for safer API URL construction
- Import typing module for List, Dict, Optional, Callable, etc.
- Improves IDE support and code documentation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implement testing infrastructure based on test-strategy.md:
- Add requirements-dev.txt for declarative dev dependencies
- Create pytest fixtures in tests/conftest.py
- Add unit tests for config and domain parsing
- Add integration tests for CLI commands (profile, denylist, auth, import)
- Add API resilience tests (retry, rate limiting, network errors)
- Add concurrency validation tests
- Replace lint.yml with test.yml (pytest + flake8, Python 3.10-3.12 matrix)

45 tests covering unit, integration, and edge case scenarios.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add types-requests stub to requirements-dev.txt
- Handle None returns from api_call() in get_profiles/get_domain_list
- Change function signatures to accept Sequence[str] instead of List[str]
- Add explicit type annotations for list comprehensions and progressbar

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Define __version__ in nextdnsctl/__init__.py as canonical source
- Import version in nextdnsctl.py and api.py instead of duplicating
- Update setup.py to read version via regex (avoids import issues)
- Update classifiers for 1.0: Production/Stable, Python 3.10+

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@danielmeint danielmeint requested a review from Copilot January 18, 2026 16:00
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces nextdnsctl v1.0.0 with significant enhancements for production use, including parallel API requests, comprehensive CLI commands, profile name resolution, and a complete test suite.

Changes:

  • Add parallel processing support with configurable concurrency (1-20 workers, default: 5)
  • Expand CLI with list, export, and clear commands for both denylist and allowlist
  • Add profile name resolution, dry-run mode, and enhanced error handling
  • Implement comprehensive test suite (45 tests) covering parsing, config, CLI, API resilience, and concurrency
  • Add CI/CD workflow with Python 3.10/3.11/3.12 matrix testing
  • Add full type hints and mypy compliance

Reviewed changes

Copilot reviewed 15 out of 24 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tests/test_parsing.py Tests for domain line parsing logic with comments and whitespace handling
tests/test_config.py Tests for API key storage, loading, and file permissions
tests/test_concurrency.py Tests verifying parallel vs sequential execution modes
tests/test_cli_integration.py Integration tests for CLI commands with mocked API
tests/test_api_resilience.py Tests for retry logic, rate limiting, and network error handling
tests/conftest.py Pytest fixtures for test setup and mocking
setup.py Updated to read version dynamically and require Python 3.10+
requirements-dev.txt Development dependencies for testing and linting
nextdnsctl/nextdnsctl.py Major refactor adding parallel execution, profile resolution, and new commands
nextdnsctl/config.py Enhanced with environment variable support and secure file permissions
nextdnsctl/api.py Refactored with generic domain list functions and improved URL handling
nextdnsctl/init.py Centralized version definition (1.0.0)
README.md Comprehensive documentation update with examples and workflows
.github/workflows/test.yml New CI workflow with matrix testing across Python versions
.github/workflows/lint.yml Removed (consolidated into test.yml)
Files not reviewed (7)
  • .idea/.gitignore: Language not supported
  • .idea/inspectionProfiles/Project_Default.xml: Language not supported
  • .idea/inspectionProfiles/profiles_settings.xml: Language not supported
  • .idea/misc.xml: Language not supported
  • .idea/modules.xml: Language not supported
  • .idea/nextdnsctl.iml: Language not supported
  • .idea/vcs.xml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread README.md Outdated
Comment thread nextdnsctl/api.py
from .config import load_api_key

API_BASE = "https://api.nextdns.io"
API_BASE = "https://api.nextdns.io/"
Copy link

Copilot AI Jan 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The trailing slash in API_BASE combined with endpoint.lstrip('/') in the urljoin call (line 37) means endpoints can be passed with or without leading slashes. While this works, it would be clearer to either consistently include or exclude the trailing slash and document the expected endpoint format to avoid confusion.

Copilot uses AI. Check for mistakes.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@danielmeint danielmeint merged commit 0e6ec41 into main Jan 18, 2026
9 checks passed
@danielmeint danielmeint deleted the feature/parallelize-requests branch January 18, 2026 16:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants