Files
2025-11-29 18:16:25 +08:00

343 lines
13 KiB
Python

#!/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))