A curated list of libraries & SDKs for the Bluesky API and AT Protocol
at master 225 lines 6.6 kB view raw
1require_relative 'cargo_import' 2require_relative 'import_helpers' 3require_relative 'npm_import' 4require_relative 'requests' 5 6require 'base64' 7require 'json' 8require 'time' 9 10class GithubImport 11 include ImportHelpers 12 include Requests 13 14 def initialize 15 @user_cache = {} 16 end 17 18 def request_headers 19 { 'Authorization' => "Bearer " + auth_config['github_token'] } 20 end 21 22 def url_matches?(url) 23 url =~ %r{^https://github\.com/[\w\-\.]+/[\w\-\.]+} 24 end 25 26 def import_url(url, project) 27 url =~ %r{^https://github\.com/([\w\-\.]+)/([\w\-\.]+)} 28 user, repo = $1, $2 29 30 repo_info = get_repo_info(user, repo) 31 data = extract_repo_data(repo_info) 32 33 if release = get_latest_release(user, repo) 34 data['last_release'] = release 35 end 36 37 if tag_info = get_latest_tag(user, repo) 38 data['last_tag'] = tag_info 39 end 40 41 if ['JavaScript', 'TypeScript'].include?(repo_info['language']) 42 npm = get_npm_releases(user, repo, project) 43 last_update = npm.map { |n| n.last_release_time }.sort.last 44 45 if last_update && (release.nil? || last_update > release['published_at']) 46 data['last_release'] = { 'published_at' => last_update } 47 end 48 elsif repo_info['language'] == 'Rust' 49 crates = get_cargo_releases(user, repo, project) 50 last_update = crates.map { |c| c.last_release_time }.sort.last 51 52 if last_update && (release.nil? || last_update > release['published_at']) 53 data['last_release'] = { 'published_at' => last_update } 54 end 55 end 56 57 data['last_commit'] = get_latest_commit(user, repo) 58 59 if user_info = get_user_info(user) 60 data['user_name'] = user_info['name'] 61 end 62 63 data 64 end 65 66 def get_repo_info(user, repo) 67 response = get_response("https://api.github.com/repos/#{user}/#{repo}") 68 raise FetchError.new(response) unless response.code.to_i == 200 69 70 JSON.parse(response.body) 71 end 72 73 def extract_repo_data(json) 74 data = { 75 'name' => json['name'], 76 'description' => json['description'], 77 'user_login' => json['owner']['login'], 78 'user_profile' => "https://github.com/#{json['owner']['login']}", 79 'homepage' => json['homepage'], 80 'stars' => json['stargazers_count'] 81 } 82 83 if json['license'] && json['license']['spdx_id'] != 'NOASSERTION' 84 data['license'] = json['license']['spdx_id'] 85 end 86 87 data 88 end 89 90 def get_latest_release(user, repo) 91 response = get_response("https://api.github.com/repos/#{user}/#{repo}/releases/latest") 92 93 if response.code.to_i == 200 94 json = JSON.parse(response.body) 95 96 { 97 'tag_name' => json['tag_name'], 98 'name' => json['name'], 99 'draft' => json['draft'], 100 'prerelease' => json['prerelease'], 101 'created_at' => Time.parse(json['created_at']), 102 'published_at' => Time.parse(json['published_at']) 103 } 104 elsif response.code.to_i == 404 105 nil 106 else 107 raise FetchError.new(response) 108 end 109 end 110 111 def get_latest_tag(user, repo) 112 response = get_response("https://api.github.com/repos/#{user}/#{repo}/tags") 113 raise FetchError.new(response) unless response.code.to_i == 200 114 115 json = JSON.parse(response.body) 116 117 if tag = json.first 118 tag_name = tag['name'] 119 response = get_response(tag['commit']['url']) 120 raise FetchError.new(response) unless response.code.to_i == 200 121 122 json = JSON.parse(response.body) 123 { 124 'name' => tag_name, 125 'author_date' => Time.parse(json['commit']['author']['date']), 126 'committer_date' => Time.parse(json['commit']['committer']['date']) 127 } 128 else 129 nil 130 end 131 end 132 133 def get_latest_commit(user, repo) 134 response = get_response("https://api.github.com/repos/#{user}/#{repo}/commits") 135 raise FetchError.new(response) unless response.code.to_i == 200 136 137 json = JSON.parse(response.body) 138 139 if commit = json.first 140 { 141 'author_date' => Time.parse(commit['commit']['author']['date']), 142 'committer_date' => Time.parse(commit['commit']['committer']['date']) 143 } 144 else 145 raise FetchError.new(response) 146 end 147 end 148 149 def get_user_info(user) 150 @user_cache[user] ||= begin 151 response = get_response("https://api.github.com/users/#{user}") 152 raise FetchError.new(response) unless response.code.to_i == 200 153 154 JSON.parse(response.body) 155 end 156 end 157 158 def get_npm_releases(user, repo, project) 159 sleep 5 160 161 search_url = URI("https://api.github.com/search/code") 162 search_url.query = URI.encode_www_form(q: "repo:#{user}/#{repo} filename:package.json", per_page: 100) 163 response = get_response(search_url) 164 raise FetchError.new(response) unless response.code.to_i == 200 165 166 json = JSON.parse(response.body) 167 releases = [] 168 169 json['items'].each do |file| 170 response = get_response(file['url']) 171 raise FetchError.new(response) unless response.code.to_i == 200 172 173 details = JSON.parse(response.body) 174 contents = Base64.decode64(details['content']) 175 package = NPMImport::PackageJSON.new(contents) 176 177 next if package.version.nil? || package.private? 178 179 if npm = NPMImport.new.get_package_info(package.name) 180 package_repo_url = normalize_repo_url(npm.repository_url) 181 package_homepage = normalize_repo_url(npm.homepage_url) 182 183 if project.urls.map { |u| normalize_repo_url(u) }.any? { |u| u == package_repo_url || u == package_homepage } 184 releases << npm 185 end 186 end 187 end 188 189 releases 190 end 191 192 def get_cargo_releases(user, repo, project) 193 sleep 5 194 195 search_url = URI("https://api.github.com/search/code") 196 search_url.query = URI.encode_www_form(q: "repo:#{user}/#{repo} filename:Cargo.toml", per_page: 100) 197 response = get_response(search_url) 198 raise FetchError.new(response) unless response.code.to_i == 200 199 200 json = JSON.parse(response.body) 201 releases = [] 202 203 json['items'].each do |file| 204 response = get_response(file['url']) 205 raise FetchError.new(response) unless response.code.to_i == 200 206 207 details = JSON.parse(response.body) 208 contents = Base64.decode64(details['content']) 209 package = CargoImport::CargoToml.new(contents) 210 211 next if package.name.nil? 212 213 if crate = CargoImport.new.get_crate_info(package.name) 214 crate_repo_url = normalize_repo_url(crate.repository_url) 215 crate_homepage = normalize_repo_url(crate.homepage_url) 216 217 if project.urls.map { |u| normalize_repo_url(u) }.any? { |u| u == crate_repo_url || u == crate_homepage } 218 releases << crate 219 end 220 end 221 end 222 223 releases 224 end 225end