#!/usr/bin/env python3 """ Existing Tool Discovery Checks what automation tools are already in place Prevents duplication and suggests integration points """ import json from pathlib import Path from typing import Dict, List from collections import defaultdict class ExistingToolDiscovery: """Discovers existing automation tools in a project""" def __init__(self, project_root: str = "."): self.root = Path(project_root).resolve() def discover_all(self) -> Dict: """Discover all existing automation tools""" return { 'linting': self._discover_linting(), 'testing': self._discover_testing(), 'ci_cd': self._discover_ci_cd(), 'git_hooks': self._discover_git_hooks(), 'formatting': self._discover_formatting(), 'security': self._discover_security(), 'documentation': self._discover_documentation(), 'summary': self._generate_summary() } def _discover_linting(self) -> Dict: """Find linting tools""" tools = {} linting_patterns = { '.eslintrc*': {'tool': 'ESLint', 'language': 'JavaScript/TypeScript', 'purpose': 'Code linting'}, '.pylintrc': {'tool': 'Pylint', 'language': 'Python', 'purpose': 'Code linting'}, 'pylint.rc': {'tool': 'Pylint', 'language': 'Python', 'purpose': 'Code linting'}, '.flake8': {'tool': 'Flake8', 'language': 'Python', 'purpose': 'Code linting'}, 'tslint.json': {'tool': 'TSLint', 'language': 'TypeScript', 'purpose': 'Code linting'}, '.rubocop.yml': {'tool': 'RuboCop', 'language': 'Ruby', 'purpose': 'Code linting'}, 'phpcs.xml': {'tool': 'PHP_CodeSniffer', 'language': 'PHP', 'purpose': 'Code linting'}, } for pattern, info in linting_patterns.items(): matches = list(self.root.glob(pattern)) if matches: tools[info['tool']] = { **info, 'config_file': str(matches[0].relative_to(self.root)), 'found': True } return { 'tools_found': tools, 'count': len(tools), 'recommendation': self._linting_recommendation(tools) } def _discover_testing(self) -> Dict: """Find testing frameworks""" tools = {} testing_patterns = { 'jest.config.js': {'tool': 'Jest', 'language': 'JavaScript', 'purpose': 'Unit testing'}, 'jest.config.ts': {'tool': 'Jest', 'language': 'TypeScript', 'purpose': 'Unit testing'}, 'pytest.ini': {'tool': 'Pytest', 'language': 'Python', 'purpose': 'Unit testing'}, 'phpunit.xml': {'tool': 'PHPUnit', 'language': 'PHP', 'purpose': 'Unit testing'}, 'karma.conf.js': {'tool': 'Karma', 'language': 'JavaScript', 'purpose': 'Test runner'}, '.rspec': {'tool': 'RSpec', 'language': 'Ruby', 'purpose': 'Testing'}, 'go.mod': {'tool': 'Go test', 'language': 'Go', 'purpose': 'Testing'}, } for pattern, info in testing_patterns.items(): matches = list(self.root.glob(pattern)) if matches: tools[info['tool']] = { **info, 'config_file': str(matches[0].relative_to(self.root)), 'found': True } # Check for test directories test_dirs = [] for pattern in ['tests/', 'test/', '__tests__/', 'spec/']: if (self.root / pattern).exists(): test_dirs.append(pattern) return { 'tools_found': tools, 'test_directories': test_dirs, 'count': len(tools), 'recommendation': self._testing_recommendation(tools, test_dirs) } def _discover_ci_cd(self) -> Dict: """Find CI/CD configurations""" tools = {} ci_patterns = { '.github/workflows': {'tool': 'GitHub Actions', 'platform': 'GitHub', 'purpose': 'CI/CD'}, '.gitlab-ci.yml': {'tool': 'GitLab CI', 'platform': 'GitLab', 'purpose': 'CI/CD'}, '.circleci/config.yml': {'tool': 'CircleCI', 'platform': 'CircleCI', 'purpose': 'CI/CD'}, 'Jenkinsfile': {'tool': 'Jenkins', 'platform': 'Jenkins', 'purpose': 'CI/CD'}, '.travis.yml': {'tool': 'Travis CI', 'platform': 'Travis', 'purpose': 'CI/CD'}, 'azure-pipelines.yml': {'tool': 'Azure Pipelines', 'platform': 'Azure', 'purpose': 'CI/CD'}, '.drone.yml': {'tool': 'Drone CI', 'platform': 'Drone', 'purpose': 'CI/CD'}, } for pattern, info in ci_patterns.items(): path = self.root / pattern if path.exists(): tools[info['tool']] = { **info, 'config': str(Path(pattern)), 'found': True } return { 'tools_found': tools, 'count': len(tools), 'recommendation': self._ci_cd_recommendation(tools) } def _discover_git_hooks(self) -> Dict: """Find git hooks configuration""" tools = {} hook_patterns = { '.pre-commit-config.yaml': {'tool': 'pre-commit', 'purpose': 'Pre-commit hooks'}, '.husky': {'tool': 'Husky', 'purpose': 'Git hooks (Node.js)'}, '.git/hooks': {'tool': 'Native Git hooks', 'purpose': 'Git hooks'}, 'lefthook.yml': {'tool': 'Lefthook', 'purpose': 'Git hooks'}, } for pattern, info in hook_patterns.items(): path = self.root / pattern if path.exists(): tools[info['tool']] = { **info, 'location': str(Path(pattern)), 'found': True } return { 'tools_found': tools, 'count': len(tools), 'recommendation': self._git_hooks_recommendation(tools) } def _discover_formatting(self) -> Dict: """Find code formatting tools""" tools = {} formatting_patterns = { '.prettierrc*': {'tool': 'Prettier', 'language': 'JavaScript/TypeScript', 'purpose': 'Code formatting'}, '.editorconfig': {'tool': 'EditorConfig', 'language': 'Universal', 'purpose': 'Editor settings'}, 'pyproject.toml': {'tool': 'Black (if configured)', 'language': 'Python', 'purpose': 'Code formatting'}, '.php-cs-fixer.php': {'tool': 'PHP-CS-Fixer', 'language': 'PHP', 'purpose': 'Code formatting'}, } for pattern, info in formatting_patterns.items(): matches = list(self.root.glob(pattern)) if matches: tools[info['tool']] = { **info, 'config_file': str(matches[0].relative_to(self.root)), 'found': True } return { 'tools_found': tools, 'count': len(tools), 'recommendation': self._formatting_recommendation(tools) } def _discover_security(self) -> Dict: """Find security scanning tools""" tools = {} # Check for dependency scanning if (self.root / 'package.json').exists(): tools['npm audit'] = { 'tool': 'npm audit', 'platform': 'Node.js', 'purpose': 'Dependency scanning', 'found': True } if (self.root / 'Pipfile').exists(): tools['pipenv check'] = { 'tool': 'pipenv check', 'platform': 'Python', 'purpose': 'Dependency scanning', 'found': True } # Check for security configs security_patterns = { '.snyk': {'tool': 'Snyk', 'purpose': 'Security scanning'}, 'sonar-project.properties': {'tool': 'SonarQube', 'purpose': 'Code quality & security'}, } for pattern, info in security_patterns.items(): if (self.root / pattern).exists(): tools[info['tool']] = { **info, 'config': pattern, 'found': True } return { 'tools_found': tools, 'count': len(tools), 'recommendation': self._security_recommendation(tools) } def _discover_documentation(self) -> Dict: """Find documentation tools""" tools = {} doc_patterns = { 'mkdocs.yml': {'tool': 'MkDocs', 'purpose': 'Documentation site'}, 'docusaurus.config.js': {'tool': 'Docusaurus', 'purpose': 'Documentation site'}, 'conf.py': {'tool': 'Sphinx', 'purpose': 'Documentation (Python)'}, 'jsdoc.json': {'tool': 'JSDoc', 'purpose': 'JavaScript documentation'}, '.readthedocs.yml': {'tool': 'ReadTheDocs', 'purpose': 'Documentation hosting'}, } for pattern, info in doc_patterns.items(): if (self.root / pattern).exists(): tools[info['tool']] = { **info, 'config': pattern, 'found': True } return { 'tools_found': tools, 'count': len(tools), 'recommendation': self._documentation_recommendation(tools) } def _generate_summary(self) -> Dict: """Generate overall summary""" all_discoveries = [ self._discover_linting(), self._discover_testing(), self._discover_ci_cd(), self._discover_git_hooks(), self._discover_formatting(), self._discover_security(), self._discover_documentation(), ] total_tools = sum(d['count'] for d in all_discoveries) maturity_level = "minimal" if total_tools >= 10: maturity_level = "comprehensive" elif total_tools >= 5: maturity_level = "moderate" return { 'total_tools_found': total_tools, 'maturity_level': maturity_level, 'gaps': self._identify_gaps(all_discoveries) } def _identify_gaps(self, discoveries: List[Dict]) -> List[str]: """Identify missing automation""" gaps = [] # Check for common gaps linting = discoveries[0] testing = discoveries[1] ci_cd = discoveries[2] security = discoveries[5] if linting['count'] == 0: gaps.append('No linting tools configured') if testing['count'] == 0: gaps.append('No testing framework configured') if ci_cd['count'] == 0: gaps.append('No CI/CD pipeline configured') if security['count'] == 0: gaps.append('No security scanning tools') return gaps # Recommendation methods def _linting_recommendation(self, tools: Dict) -> str: if not tools: return "ADD: Set up linting (ESLint for JS/TS, Pylint for Python)" return "ENHANCE: Extend existing linting rules" def _testing_recommendation(self, tools: Dict, test_dirs: List) -> str: if not tools and not test_dirs: return "ADD: Set up testing framework (Jest, Pytest, etc.)" if tools and not test_dirs: return "ADD: Create test directories and write tests" return "ENHANCE: Increase test coverage" def _ci_cd_recommendation(self, tools: Dict) -> str: if not tools: return "ADD: Set up CI/CD (GitHub Actions, GitLab CI, etc.)" return "ENHANCE: Add more checks to existing CI/CD" def _git_hooks_recommendation(self, tools: Dict) -> str: if not tools: return "ADD: Set up pre-commit hooks for quality checks" return "ENHANCE: Add more hooks (pre-push, commit-msg, etc.)" def _formatting_recommendation(self, tools: Dict) -> str: if not tools: return "ADD: Set up code formatting (Prettier, Black, etc.)" return "OK: Formatting tools in place" def _security_recommendation(self, tools: Dict) -> str: if not tools: return "ADD: Set up security scanning (critical gap!)" return "ENHANCE: Add more security tools (SAST, dependency scanning)" def _documentation_recommendation(self, tools: Dict) -> str: if not tools: return "ADD: Set up documentation generation" return "OK: Documentation tools in place" def generate_report(self) -> Dict: """Generate complete discovery report""" return self.discover_all() if __name__ == '__main__': import sys path = sys.argv[1] if len(sys.argv) > 1 else '.' discoverer = ExistingToolDiscovery(path) report = discoverer.generate_report() print(json.dumps(report, indent=2))