import os import time import requests from github import Github import base64 import json import re import concurrent.futures from retrying import retry from functools import wraps # Список токенов TOKENS = [ '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', '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', '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', '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' # Добавьте столько токенов, сколько нужно ] TOKEN_INDEX = 0 ORG_NAME = 'adeo' GITHUB_ENTERPRISE_URL = 'https://github.lmru.tech' # URL вашей инстанции GitHub Enterprise OUTPUT_FILE = 'external_repositories.txt' CACHE_DIR = 'repo_cache' REPOS_CACHE_FILE = 'repos_cache.json' MAX_WORKERS = 16 # Начнем с 8 потоков # Регулярное выражение для поиска URL url_regex = re.compile(r'https?://[^\s]+') excluded_url_pattern = re.compile(r'https?://art\.lmru\.tech') # Уникальные внешние URL unique_urls = set() # Создание директории для кэша if not os.path.exists(CACHE_DIR): os.makedirs(CACHE_DIR) # Декоратор для задержки между запросами def rate_limited(func): @wraps(func) def wrapper(*args, **kwargs): time.sleep(1) # Задержка в 1 секунду между запросами return func(*args, **kwargs) return wrapper # Функция для получения текущего токена def get_current_token(): global TOKEN_INDEX token = TOKENS[TOKEN_INDEX % len(TOKENS)] TOKEN_INDEX += 1 return token # Создание экземпляра GitHub API с аутентификацией и кастомным URL def create_github_instance(): token = get_current_token() return Github(base_url=f"{GITHUB_ENTERPRISE_URL}/api/v3", login_or_token=token) @rate_limited @retry(wait_exponential_multiplier=1000, wait_exponential_max=10000, stop_max_attempt_number=5) def get_repo_contents(repo, path=""): return repo.get_contents(path) @rate_limited @retry(wait_exponential_multiplier=1000, wait_exponential_max=10000, stop_max_attempt_number=5) def get_file_content(file_content): return base64.b64decode(file_content.content).decode('utf-8') @rate_limited def check_rate_limit(): headers = { "Authorization": f"token {get_current_token()}" } rate_limit_url = f"{GITHUB_ENTERPRISE_URL}/api/v3/rate_limit" response = requests.get(rate_limit_url, headers=headers) if response.status_code == 200: limits = response.json() core_limit = limits["resources"]["core"]["limit"] core_remaining = limits["resources"]["core"]["remaining"] core_reset = limits["resources"]["core"]["reset"] print(f"Core limit: {core_limit}") print(f"Core remaining: {core_remaining}") print(f"Core reset time: {time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime(core_reset))}") return core_remaining else: print(f"Failed to retrieve rate limits: {response.status_code}, {response.text}") return None @rate_limited def fetch_all_repos(): repos = [] g = create_github_instance() try: for repo in g.get_organization(ORG_NAME).get_repos(): repos.append(repo.full_name) except Exception as e: print(f'Error retrieving organization repositories: {str(e)}') if '403' in str(e): print('Hit rate limit, sleeping for 60 seconds...') time.sleep(60) print(f"Total repositories retrieved: {len(repos)}") # Логирование количества репозиториев return repos def get_all_repos(): if os.path.exists(REPOS_CACHE_FILE): with open(REPOS_CACHE_FILE, 'r') as f: repos = json.load(f) print(f"Loaded {len(repos)} repositories from cache") else: repos = fetch_all_repos() with open(REPOS_CACHE_FILE, 'w') as f: json.dump(repos, f) print(f"Saved {len(repos)} repositories to cache") return repos def cache_file_path(repo_name, file_path): return os.path.join(CACHE_DIR, f"{repo_name.replace('/', '_')}_{file_path.replace('/', '_')}") def is_cached(repo_name, file_path): return os.path.exists(cache_file_path(repo_name, file_path)) def read_cache(repo_name, file_path): with open(cache_file_path(repo_name, file_path), 'r') as f: return f.read() def write_cache(repo_name, file_path, content): with open(cache_file_path(repo_name, file_path), 'w') as f: f.write(content) def find_external_urls(content, repo_name, file_path): urls = url_regex.findall(content) for url in urls: if not excluded_url_pattern.search(url): unique_urls.add(url) print(f'Found external URL in {repo_name}/{file_path}: {url}') def check_file(repo, file_content): file_path = file_content.path if is_cached(repo.name, file_path): print(f'Reading from cache: {repo.name}/{file_path}') content = read_cache(repo.name, file_path) else: try: # Чтение содержимого файла с повторными попытками content = get_file_content(file_content) write_cache(repo.name, file_path, content) except Exception as e: print(f'Error decoding {file_path} in {repo.name}: {str(e)}') return # Поиск внешних URL find_external_urls(content, repo.name, file_path) def check_and_report_external_repositories(repo): try: contents = get_repo_contents(repo) while contents: file_content = contents.pop(0) if file_content.type == "dir": print(f'Entering directory: {file_content.path}') contents.extend(get_repo_contents(repo, file_content.path)) else: print(f'Checking file: {file_content.path}') if file_content.path.endswith(( "package.json", "requirements.txt", "setup.py", "pom.xml", "build.gradle", "Cargo.toml", "composer.json", "Makefile", "Dockerfile", "saltfile", "Pipfile", "environment.yml", "Jenkinsfile", "Gemfile", "Rakefile", "go.mod", "go.sum", "build.sbt", "Vagrantfile", "shard.yml", "Chart.yaml", "mix.exs", "nimble", "rebar.config", "Project.toml", "deps.edn", "shadow-cljs.edn", "Spago.dhall", "flake.nix", "default.nix", "pubspec.yaml", "Paket.dependencies", "Packages.swift", "universe.json", "stack.yaml", "hpack.yaml", "default.build", "project.clj" )): check_file(repo, file_content) except Exception as e: if '404' in str(e): print(f'Skipping empty repository {repo.name}') else: print(f'Error processing repository {repo.name}: {str(e)}') def process_repository(repo_name): print(f'Checking repository: {repo_name}') g = create_github_instance() repo = g.get_repo(repo_name) check_and_report_external_repositories(repo) print(f'Finished checking repository: {repo_name}') start_time = time.time() # Проверка лимитов перед началом выполнения if check_rate_limit() is None: exit(1) # Получение всех репозиториев repos = get_all_repos() # Проверка, что репозитории были получены if not repos: print("No repositories retrieved. Exiting.") exit(1) # Использование пула потоков для обработки репозиториев with concurrent.futures.ThreadPoolExecutor(max_workers=MAX_WORKERS) as executor: executor.map(process_repository, repos) end_time = time.time() elapsed_time = end_time - start_time # Запись уникальных внешних URL в файл with open(OUTPUT_FILE, 'w') as file: for url in unique_urls: file.write(f"{url}\n") print(f'Unique external URLs have been written to {OUTPUT_FILE}') print(f'Total time taken: {elapsed_time / 60:.2f} minutes') # Проверка лимитов после выполнения check_rate_limit() import os import subprocess import time import shutil import re from github import Github import concurrent.futures import json # Список токенов TOKENS = [ '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', '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', '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', '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' # Добавьте столько токенов, сколько нужно ] TOKEN_INDEX = 0 ORG_NAME = 'adeo' GITHUB_ENTERPRISE_URL = 'https://github.lmru.tech' CLONE_DIR = '/tmp/github_test' # Укажите путь к директории, куда будут клонироваться репозитории REPOS_CACHE_FILE = 'repos_cache.json' OUTPUT_FILE = 'external_repositories.txt' MAX_WORKERS = 8 # Количество параллельных процессов для клонирования BATCH_SIZE = 50 # Количество репозиториев в одной партии для обработки # Регулярное выражение для поиска URL url_regex = re.compile(r'https?://[^\s]+') excluded_url_pattern = re.compile(r'https?://art\.lmru\.tech') # Уникальные внешние URL unique_urls = set() # Функция для получения текущего токена def get_current_token(): global TOKEN_INDEX token = TOKENS[TOKEN_INDEX % len(TOKENS)] TOKEN_INDEX += 1 return token # Создание экземпляра GitHub API с аутентификацией и кастомным URL def create_github_instance(): token = get_current_token() return Github(base_url=f"{GITHUB_ENTERPRISE_URL}/api/v3", login_or_token=token) # Получение всех репозиториев и сохранение их в файл кэша def fetch_all_repos(): repos = [] g = create_github_instance() try: for repo in g.get_organization(ORG_NAME).get_repos(): repos.append(repo.clone_url) except Exception as e: print(f'Error retrieving organization repositories: {str(e)}') if '403' in str(e): print('Hit rate limit, sleeping for 60 seconds...') time.sleep(60) print(f"Total repositories retrieved: {len(repos)}") with open(REPOS_CACHE_FILE, 'w') as f: json.dump(repos, f) return repos # Получение списка репозиториев из кэша def get_all_repos(): if os.path.exists(REPOS_CACHE_FILE): with open(REPOS_CACHE_FILE, 'r') as f: repos = json.load(f) print(f"Loaded {len(repos)} repositories from cache") else: repos = fetch_all_repos() return repos # Клонирование репозитория def clone_repo(repo_url): try: token = get_current_token() repo_name = repo_url.split('/')[-1].replace('.git', '') clone_url = repo_url.replace('https://', f'https://{token}:x-oauth-basic@') clone_path = os.path.join(CLONE_DIR, repo_name) if not os.path.exists(clone_path): subprocess.run(['git', 'clone', clone_url, clone_path]) print(f'Cloned {repo_url} to {clone_path}') else: print(f'Repository {repo_name} already exists, pulling latest changes') subprocess.run(['git', '-C', clone_path, 'pull']) except Exception as e: print(f'Error cloning repository {repo_url}: {str(e)}') # Поиск внешних URL в файлах def find_external_urls(file_path): with open(file_path, 'r', encoding='utf-8', errors='ignore') as f: content = f.read() urls = url_regex.findall(content) for url in urls: if not excluded_url_pattern.search(url): unique_urls.add(url) print(f'Found external URL in {file_path}: {url}') # Анализ репозитория def analyze_repo(repo_path): for root, _, files in os.walk(repo_path): for file in files: if file.endswith(( "package.json", "requirements.txt", "setup.py", "pom.xml", "build.gradle", "Cargo.toml", "composer.json", "Makefile", "Dockerfile", "saltfile", "Pipfile", "environment.yml", "Jenkinsfile", "Gemfile", "Rakefile", "go.mod", "go.sum", "build.sbt", "Vagrantfile", "shard.yml", "Chart.yaml", "mix.exs", "nimble", "rebar.config", "Project.toml", "deps.edn", "shadow-cljs.edn", "Spago.dhall", "flake.nix", "default.nix", "pubspec.yaml", "Paket.dependencies", "Packages.swift", "universe.json", "stack.yaml", "hpack.yaml", "default.build", "project.clj" )): find_external_urls(os.path.join(root, file)) # Обработка репозиториев партиями def process_repos_in_batches(repos): for i in range(0, len(repos), BATCH_SIZE): batch = repos[i:i + BATCH_SIZE] # Клонирование репозиториев with concurrent.futures.ThreadPoolExecutor(max_workers=MAX_WORKERS) as executor: executor.map(clone_repo, batch) # Анализ клонированных репозиториев for repo_url in batch: repo_name = repo_url.split('/')[-1].replace('.git', '') repo_path = os.path.join(CLONE_DIR, repo_name) if os.path.isdir(repo_path): analyze_repo(repo_path) # Очистка после анализа for repo_url in batch: repo_name = repo_url.split('/')[-1].replace('.git', '') repo_path = os.path.join(CLONE_DIR, repo_name) if os.path.isdir(repo_path): shutil.rmtree(repo_path) print(f'Removed {repo_path} after analysis') # Основной код if __name__ == '__main__': repos = get_all_repos() process_repos_in_batches(repos) # Запись уникальных внешних URL в файл with open(OUTPUT_FILE, 'w') as file: for url in unique_urls: file.write(f"{url}\n") print(f'Unique external URLs have been written to {OUTPUT_FILE}')