A curated list of libraries & SDKs for the Bluesky API and AT Protocol
at master 187 lines 5.1 kB view raw
1require_relative 'cargo_import' 2require_relative 'import_helpers' 3require_relative 'npm_import' 4require_relative 'requests' 5 6require 'didkit' 7require 'fileutils' 8require 'json' 9require 'licensee' 10require 'minisky' 11require 'time' 12 13class TangledImport 14 include ImportHelpers 15 include Requests 16 17 def initialize 18 @user_cache = {} 19 end 20 21 def url_matches?(url) 22 url =~ %r{^https://tangled\.org/@?[\w\-\.\:]+/[\w\-\.]+} 23 end 24 25 def import_url(url, project) 26 url =~ %r{^https://tangled\.org/@?([\w\-\.\:]+)/([\w\-\.]+)} 27 user, repo = $1, $2 28 29 did = DID.resolve_handle(user) 30 sky = Minisky.new(did.document.pds_host, nil) 31 32 repos = sky.fetch_all('com.atproto.repo.listRecords', 33 { repo: did, collection: 'sh.tangled.repo', limit: 100 }, 34 field: 'records' 35 ) 36 37 repo_record = repos.detect { |x| x['value']['name'] == repo } 38 39 repo_folder = clone_repo(user, repo) 40 41 data = repo_data_from_record(repo_record['value']) 42 data['user_login'] = user 43 data['user_profile'] = "https://tangled.org/#{user}" 44 45 if tag_info = get_latest_tag(repo_folder) 46 data['last_tag'] = tag_info 47 end 48 49 if (license = Licensee.license(repo_folder)) && license.spdx_id != 'NOASSERTION' 50 data['license'] = license.spdx_id 51 end 52 53 data['stars'] = get_stars(repo_record['uri']) 54 55 if project.language == :js 56 npm = get_npm_releases(repo_folder, project) 57 last_update = npm.map { |n| n.last_release_time }.sort.last 58 59 if last_update 60 data['last_release'] = { 'published_at' => last_update } 61 end 62 elsif project.language == :rust 63 crates = get_cargo_releases(repo_folder, project) 64 last_update = crates.map { |c| c.last_release_time }.sort.last 65 66 if last_update 67 data['last_release'] = { 'published_at' => last_update } 68 end 69 end 70 71 data['last_commit'] = get_latest_commit(repo_folder) 72 73 data 74 end 75 76 def clone_repo(user, repo) 77 repos_cache = File.expand_path(File.join(__dir__, '..', 'tmp', 'repos')) 78 FileUtils.mkdir_p(repos_cache) 79 80 dirname = "#{user}_#{repo}" 81 repo_folder = File.join(repos_cache, dirname) 82 83 if Dir.exist?(repo_folder) 84 Dir.chdir(repo_folder) do 85 system('git pull -q') 86 end 87 else 88 Dir.chdir(repos_cache) do 89 system("git clone https://tangled.org/#{user}/#{repo} #{dirname}") 90 end 91 end 92 93 repo_folder 94 end 95 96 def repo_data_from_record(record) 97 { 98 'name' => record['name'], 99 'description' => record['description'], 100 } 101 end 102 103 def get_latest_tag(repo_folder) 104 Dir.chdir(repo_folder) do 105 newest_tag_commit = %x(git rev-list --tags --max-count=1).strip 106 return nil if newest_tag_commit.empty? 107 108 newest_tag = %x(git tag --points-at "#{newest_tag_commit}" | head -1).strip 109 timestamp = %x(git show -s --format=%cI "#{newest_tag_commit}").strip 110 111 { 112 'name' => newest_tag, 113 'committer_date' => Time.parse(timestamp) 114 } 115 end 116 end 117 118 def get_latest_commit(repo_folder) 119 Dir.chdir(repo_folder) do 120 { 121 'committer_date' => %x(git show -s --format=%cI).strip.then { |x| Time.parse(x) }, 122 'author_date' => %x(git show -s --format=%aI).strip.then { |x| Time.parse(x) } 123 } 124 end 125 end 126 127 def get_stars(repo_record_uri) 128 url = URI("https://constellation.microcosm.blue/links/count") 129 url.query = URI.encode_www_form(target: repo_record_uri, collection: 'sh.tangled.feed.star', path: '.subject') 130 131 response = get_response(url) 132 raise FetchError.new(response) unless response.code.to_i == 200 133 134 json = JSON.parse(response.body) 135 json['total'] 136 end 137 138 def get_npm_releases(repo_folder, project) 139 Dir.chdir(repo_folder) do 140 packages = %x(find . -name 'package.json').strip 141 releases = [] 142 143 packages.lines.each do |file| 144 contents = File.read(file.strip) 145 package = NPMImport::PackageJSON.new(contents) 146 147 next if package.version.nil? || package.private? 148 149 if npm = NPMImport.new.get_package_info(package.name) 150 package_repo_url = normalize_repo_url(npm.repository_url) 151 package_homepage = normalize_repo_url(npm.homepage_url) 152 153 if project.urls.map { |u| normalize_repo_url(u) }.any? { |u| u == package_repo_url || u == package_homepage } 154 releases << npm 155 end 156 end 157 end 158 159 releases 160 end 161 end 162 163 def get_cargo_releases(repo_folder, project) 164 Dir.chdir(repo_folder) do 165 packages = %x(find . -name 'Cargo.toml').strip 166 releases = [] 167 168 packages.lines.each do |file| 169 contents = File.read(file.strip) 170 package = CargoImport::CargoToml.new(contents) 171 172 next if package.name.nil? 173 174 if crate = CargoImport.new.get_crate_info(package.name) 175 crate_repo_url = normalize_repo_url(crate.repository_url) 176 crate_homepage = normalize_repo_url(crate.homepage_url) 177 178 if project.urls.map { |u| normalize_repo_url(u) }.any? { |u| u == crate_repo_url || u == crate_homepage } 179 releases << crate 180 end 181 end 182 end 183 184 releases 185 end 186 end 187end