-
Notifications
You must be signed in to change notification settings - Fork 0
/
gh_committers_spy.py
186 lines (148 loc) · 6.74 KB
/
gh_committers_spy.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
import requests
import yaml
import csv
import argparse
from datetime import datetime, timedelta
from urllib.parse import urlparse
from termcolor import colored
import emoji
def display_ascii_art():
title = "GH Committers Spy - by 0xtiago"
ascii_art = r"""
_____ _ _ _____ _ _ _____
/ ____| | | | / ____| (_) | / ____|
| | __| |__| | | | ___ _ __ ___ _ __ ___ _| |_ ___ _ __ ___ | (___ _ __ _ _
| | |_ | __ | | | / _ \| '_ ` _ \| '_ ` _ \| | __/ _ \ '__/ __| \___ \| '_ \| | | |
| |__| | | | | | |___| (_) | | | | | | | | | | | | || __/ | \__ \ ____) | |_) | |_| |
\_____|_| |_| \_____\___/|_| |_| |_|_| |_| |_|_|\__\___|_| |___/ |_____/| .__/ \__, |
| | __/ |
|_| |___/
"""
colored_title = colored(title, 'cyan', attrs=['bold'])
colored_art = colored(ascii_art, 'yellow')
print(colored_title)
print(colored_art)
config_file = 'config.yaml' #Configuration file
repos_file = 'repositories.txt' # Repositories to be analysed
output_file = 'results.csv' # Results of each reposutory
committers_file = 'committers.csv' # List of unique committers username and total number of commits
# Function to read configuration from YAML file
def read_config(file_path):
with open(file_path, 'r') as file:
config = yaml.safe_load(file)
return config
#Read configuration from YAML file
config = read_config(config_file)
GITHUB_TOKEN = config['github_token']
# Headers for authentication in the GitHub API
headers = {
'Authorization': f'token {GITHUB_TOKEN}'
}
# Function to read the repository adresses from the repository file.
def read_repos_from_file(file_path):
with open(file_path, 'r') as file:
repos = [convert_repo_url(line.strip()) for line in file if line.strip()]
return repos
# Function to convert full repository URLs to "owner/repo" format
def convert_repo_url(repo):
repo = remove_git_extension(repo)
if repo.startswith("https://"):
parsed_url = urlparse(repo)
path_parts = parsed_url.path.strip('/').split('/')
if len(path_parts) >= 2:
return f'{path_parts[0]}/{path_parts[1]}'
return repo
# Remove .git extension from URLs, if they exist
def remove_git_extension(repo):
if repo.endswith('.git'):
return repo[:-4]
return repo
# Function to get the default branch name of a repository
def get_default_branch(repo):
url = f'https://api.github.com/repos/{repo}'
response = requests.get(url, headers=headers)
if response.status_code == 200:
repo_info = response.json()
return repo_info.get('default_branch', 'main')
return 'main'
# Function to get active contributors in the last X (-d) days on the main branch
def get_active_collaborators(repo, days):
default_branch = get_default_branch(repo)
since_date = (datetime.now() - timedelta(days=days)).isoformat() + 'Z'
url = f'https://api.github.com/repos/{repo}/commits?sha={default_branch}&since={since_date}'
response = requests.get(url, headers=headers)
if response.status_code != 200:
error_message = f'Error accessing the repository {repo}: {response.status_code}'
print(error_message)
return {}, error_message
commits = response.json()
active_collaborators = {}
for commit in commits:
author = commit['author']
if author is not None:
author_name = author['login']
if author_name in active_collaborators:
active_collaborators[author_name] += 1
else:
active_collaborators[author_name] = 1
return active_collaborators, 'Success'
# Function to write results to the CSV file
def write_results_to_csv(results, file_path):
with open(file_path, 'w', newline='') as csvfile:
fieldnames = ['Repository', 'Active Committers', 'Status']
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
writer.writeheader()
for result in results:
writer.writerow(result)
# Function to write commits per user to a CSV file
def write_committers_to_csv(committers, file_path):
with open(file_path, 'w', newline='') as csvfile:
fieldnames = ['User', 'Number of Commits']
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
writer.writeheader()
for user, commits in committers.items():
writer.writerow({'User': user, 'Number of Commits': commits})
# Main function
def main():
display_ascii_art()
parser = argparse.ArgumentParser(description='Verifies unique active contributors to GitHub repositories.')
parser.add_argument('-d', '--days', type=int, required=True, help='Number of days to check employee activity.')
args = parser.parse_args()
days = args.days
# Read repository list from file
repos = read_repos_from_file(repos_file)
total_repos = len(repos)
# List to store results
results = []
# Dictionary to store commits per user
all_committers = {}
# Loop through the repositories and get the number of active contributors
for i, repo in enumerate(repos, start=1):
committers, status = get_active_collaborators(repo, days)
num_active_collaborators = len(committers)
results.append({
'Repository': repo,
'Active Committers': num_active_collaborators,
'Status': status
})
# Display the number of active committers in the console
print(f'{i}/{total_repos} Repository: {repo}, Active Committers: {num_active_collaborators}, Status: {status}')
# Update committer dictionary
for user, commits in committers.items():
if user in all_committers:
all_committers[user] += commits
else:
all_committers[user] = commits
# Write results to CSV file
write_results_to_csv(results, output_file)
write_committers_to_csv(all_committers, committers_file)
print(f'Results written to file {output_file}')
print(f'Committers written to the file {committers_file}')
# print(f'Total unique committers: {len(all_committers)}')
colored_committers = colored(len(all_committers), 'green', attrs=['bold'])
print(f'Total unique committers: {colored_committers}')
print()
colored_buymeacoffee = colored("https://buymeacoffee.com/tiagotavares", 'yellow', attrs=['bold'])
print(f"{emoji.emojize(':handshake:')} If this tool helped you, how about inviting me for a coffee?? {colored_buymeacoffee} {emoji.emojize(':grinning_face:')}.")
if __name__ == '__main__':
main()