-
Notifications
You must be signed in to change notification settings - Fork 1
/
github_importer.rb
executable file
·169 lines (131 loc) · 4.82 KB
/
github_importer.rb
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
#!/usr/bin/env ruby
# frozen_string_literal: true
require 'rest_client'
require 'json'
require 'octokit'
require 'fileutils'
require 'yaml'
# Export from these organizations
ORGS = %w[teamdigitale italia].freeze
# List of technologies shown in the UI
TECH_LIST = %w[
angular react design html arduino bootstrap frontend
perl python cpp scala php csharp java android ios dotnet
wordpress metabase ansible docker magento joomla django
].sort.freeze
# Import issues with at least one of these labels
ONLY_WITH_LABEL = ['help wanted', 'Hacktoberfest'].freeze
# Show these labels in the UI, if present.
ISSUE_TYPES = ['bug', 'enhancement', 'new project', 'Hacktoberfest'].freeze
# Ignore these repos using the full_name (ie. 'organization/repo')
BLACKLISTED_REPOS = [].freeze
GITHUB_TOKEN = ENV.fetch('GITHUB_TOKEN', '')
SAVE_TO_REPO = "italia/developers.italia.it-data"
def fetch(url, headers = {})
rest_params = {
'per_page' => 100,
'page' => 1,
'type' => 'public'
}
results = []
loop do
print "."
response = JSON.parse(RestClient.get(url, {
Authorization: "token #{GITHUB_TOKEN}", params: rest_params
}.merge(headers)))
if response.is_a? Array
results += response
else
results = response
break
end
break if response.size != rest_params['per_page']
rest_params['page'] += 1
end
results
end
def fetch_teams(org)
teams = fetch("https://api.github.com/orgs/#{org}/teams")
teams.map do |team|
members = fetch("#{team['url']}/members")
# Only get the fields we use in the frontend, so we don't end up
# cluttering the history for fields we don't even use.
team['members'] = members.map { |m| fetch(m['url']).slice('login', 'name', 'avatar_url', 'html_url') }
team
end
end
def fetch_issues(repos)
github_issues = []
repos.each do |repo|
open_issues, full_name = repo.values_at('open_issues_count', 'full_name')
next if open_issues.zero? || BLACKLISTED_REPOS.include?(full_name)
issues = fetch("https://api.github.com/repos/#{full_name}/issues")
issues.each do |issue|
next if issue.key?('pull_request')
labels = issue['labels'].map { |label| label['name'] }
# XXX: ugly hack: issues can have just one type, but we treat issues labelled
# with "Hacktoberfest" as a new type because of the limits of the UI implementation.
#
# Let's have "Hacktoberfest" issues take precedence over "bug" whenever a
# certain issue has both the labels ("Hacktoberfest" being capitalized comes before
# uncapitalized labels)
labels.sort!
# Only get the issues marked with at least one label in ONLY_WITH_LABEL
next unless labels.any? { |item| ONLY_WITH_LABEL.include? item }
issue_data = {
'created_at' => issue['created_at'],
'url' => issue['html_url'],
'title' => issue['title'],
'name' => repo['name'],
'language' => TECH_LIST & repo['topics'],
'repository_url' => repo['html_url'],
'labels' => labels,
'type' => (labels & ISSUE_TYPES).first || '',
'subproject' => repo['name']
}
# Remove the issue label(s) marking this as help wanted, so they don't
# get displayed in the UI.
issue_data['labels'].reject! { |label| ONLY_WITH_LABEL.include? label }
github_issues.push(issue_data)
end
end
github_issues
end
def git_update_file(client, path, contents)
resp = client.contents(SAVE_TO_REPO, path: path)
orig_contents = Base64.decode64(resp.content).force_encoding('UTF-8')
if orig_contents == contents
puts "#{path} unchanged"
return
else
puts "Updating #{path}..."
end
client.create_contents(SAVE_TO_REPO,
path,
":robot: Update #{path}",
contents,
sha: resp.sha,
branch: 'main')
end
abort 'Set GITHUB_TOKEN first.' if GITHUB_TOKEN.empty?
repos = ORGS.map do |org|
fetch(
"https://api.github.com/orgs/#{org}/repos", {
accept: 'application/vnd.github.mercy-preview+json'
}
)
end.flatten.reject { |r| r['archived'] || r['disabled'] }
puts "Got #{repos.size} GitHub repos"
github_issues = fetch_issues(repos)
puts "Got #{github_issues.size} issues"
github_teams = fetch_teams('italia')
puts "Got #{github_teams.size} teams"
# Fetch org members
client = Octokit::Client.new(access_token: GITHUB_TOKEN)
client.auto_paginate = true
github_members = client.organization_public_members('italia').map { |m| m.to_hash.transform_keys(&:to_s) }
puts "Got #{github_members.size} members"
git_update_file(client, "github_issues.json", github_issues.to_json)
git_update_file(client, "github_teams.yml", github_teams.to_yaml)
git_update_file(client, "github_members.yml", github_members.to_yaml)
git_update_file(client, "github_tech_list.yml", TECH_LIST.to_yaml)