Class: GithubRepository

Inherits:
ActiveRecord::Base
  • Object
show all
Defined in:
app/models/github_repository.rb

Constant Summary collapse

STATUSES =
['Active', 'Deprecated', 'Unmaintained', 'Help Wanted', 'Removed']
API_FIELDS =
[:description, :fork, :created_at, :updated_at, :pushed_at, :homepage,
:size, :stargazers_count, :language, :has_issues, :has_wiki, :has_pages,
:forks_count, :mirror_url, :open_issues_count, :default_branch,
:subscribers_count, :private]

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.create_from_github(full_name, token = nil) ⇒ Object


401
402
403
404
405
406
407
408
# File 'app/models/github_repository.rb', line 401

def self.create_from_github(full_name, token = nil)
  github_client = AuthToken.new_client(token)
  repo_hash = github_client.repo(full_name, accept: 'application/vnd.github.drax-preview+json').to_hash
  return false if repo_hash.nil? || repo_hash.empty?
  create_from_hash(repo_hash)
rescue Octokit::Unauthorized, Octokit::InvalidRepository, Octokit::RepositoryUnavailable, Octokit::NotFound, Octokit::Conflict, Octokit::Forbidden, Octokit::InternalServerError, Octokit::BadGateway => e
  nil
end

.create_from_hash(repo_hash) ⇒ Object


410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
# File 'app/models/github_repository.rb', line 410

def self.create_from_hash(repo_hash)
  repo_hash = repo_hash.to_hash
  ActiveRecord::Base.transaction do
    g = GithubRepository.find_by(github_id: repo_hash[:id])
    g = GithubRepository.find_by('lower(full_name) = ?', repo_hash[:full_name].downcase) if g.nil?
    g = GithubRepository.new(github_id: repo_hash[:id], full_name: repo_hash[:full_name]) if g.nil?
    g.owner_id = repo_hash[:owner][:id]
    g.full_name = repo_hash[:full_name] if g.full_name.downcase != repo_hash[:full_name].downcase
    g.github_id = repo_hash[:id] if g.github_id.nil?
    g.license = repo_hash[:license][:key] if repo_hash[:license]
    g.source_name = repo_hash[:parent][:full_name] if repo_hash[:fork] && repo_hash[:parent]
    g.assign_attributes repo_hash.slice(*GithubRepository::API_FIELDS)

    if g.changed?
      return g.save ? g : nil
    else
      return g
    end
  end
rescue ActiveRecord::RecordNotUnique
  nil
end

.language(language) ⇒ Object


57
58
59
# File 'app/models/github_repository.rb', line 57

def self.language(language)
  where('lower(github_repositories.language) = ?', language.try(:downcase))
end

Instance Method Details

#avatar_url(size = 60) ⇒ Object


190
191
192
# File 'app/models/github_repository.rb', line 190

def avatar_url(size = 60)
  "https://avatars.githubusercontent.com/u/#{owner_id}?size=#{size}"
end

#blob_urlObject


170
171
172
# File 'app/models/github_repository.rb', line 170

def blob_url
  "#{url}/blob/#{default_branch}/"
end

#colorObject


122
123
124
# File 'app/models/github_repository.rb', line 122

def color
  Languages::Language[language].try(:color)
end

#commits_urlObject


178
179
180
# File 'app/models/github_repository.rb', line 178

def commits_url
  "#{url}/commits"
end

#contributors_urlObject


158
159
160
# File 'app/models/github_repository.rb', line 158

def contributors_url
  "#{url}/graphs/contributors"
end

#create_webhook(token) ⇒ Object


322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
# File 'app/models/github_repository.rb', line 322

def create_webhook(token)
  github_client(token).create_hook(
    full_name,
    'web',
    {
      :url => 'https://libraries.io/hooks/github',
      :content_type => 'json'
    },
    {
      :events => ['push', 'pull_request'],
      :active => true
    }
  )
rescue Octokit::UnprocessableEntity => e
  nil
end

#delete_old_manifestsObject


397
398
399
# File 'app/models/github_repository.rb', line 397

def delete_old_manifests
  manifests.where.not(id: manifests.latest.map(&:id)).each(&:destroy)
end

#download_fork_source(token = nil) ⇒ Object


246
247
248
249
# File 'app/models/github_repository.rb', line 246

def download_fork_source(token = nil)
  return true unless self.fork? && self.source.nil?
  GithubRepository.create_from_github(source_name, token)
end

#download_forks(token = nil) ⇒ Object


255
256
257
258
259
260
261
262
# File 'app/models/github_repository.rb', line 255

def download_forks(token = nil)
  return true if fork?
  return true unless forks_count && forks_count > 0 && forks_count < 100
  return true if forks_count == forked_repositories.count
  AuthToken.new_client(token).forks(full_name).each do |fork|
    GithubRepository.create_from_hash(fork)
  end
end

#download_forks_async(token = nil) ⇒ Object


251
252
253
# File 'app/models/github_repository.rb', line 251

def download_forks_async(token = nil)
  GithubDownloadForkWorker.perform_async(self.id, token)
end

#download_github_contributions(token = nil) ⇒ Object


264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
# File 'app/models/github_repository.rb', line 264

def download_github_contributions(token = nil)
  contributions = github_client(token).contributors(full_name)
  return if contributions.empty?
  existing_github_contributions = github_contributions.includes(:github_user).to_a
  platform = projects.first.try(:platform)
  contributions.each do |c|
    return unless c['id']

    unless cont = existing_github_contributions.find{|c| c.github_user.try(:github_id) == c.id }
      user = GithubUser.find_or_create_by(github_id: c.id) do |u|
        u. = c.
        u.user_type = c.type
      end
      cont = github_contributions.find_or_create_by(github_user: user)
    end

    cont.count = c.contributions
    cont.platform = platform
    cont.save! if cont.changed?
  end
  true
rescue Octokit::Unauthorized, Octokit::InvalidRepository, Octokit::RepositoryUnavailable, Octokit::NotFound, Octokit::Conflict, Octokit::Forbidden, Octokit::InternalServerError, Octokit::BadGateway => e
  nil
end

#download_manifests(token = nil) ⇒ Object


339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
# File 'app/models/github_repository.rb', line 339

def download_manifests(token = nil)
  r = Typhoeus::Request.new("http://libhooks.herokuapp.com/v2/repos/#{full_name}",
    method: :get,
    params: { token: token },
    headers: { 'Accept' => 'application/json' }).run
  begin
    body = Oj.load(r.body)
    if body
      new_manifests = body["manifests"]
    else
      new_manifests = nil
    end
  rescue Oj::ParseError
    new_manifests = nil
  end

  if body && body['metadata']
    meta = body['metadata']

    self.has_readme       = meta['readme']['path']        if meta['readme']
    self.has_changelog    = meta['changelog']['path']     if meta['changelog']
    self.has_contributing = meta['contributing']['path']  if meta['contributing']
    self.has_license      = meta['license']['path']       if meta['license']
    self.has_coc          = meta['codeofconduct']['path'] if meta['codeofconduct']
    self.has_threat_model = meta['threatmodel']['path']   if meta['threatmodel']
    self.has_audit        = meta['audit']['path']         if meta['audit']

    save! if self.changed?
  end

  return if new_manifests.nil?

  new_manifests.each do |m|
    args = {platform: m['platform'], kind: m['type'], filepath: m['filepath'], sha: m['sha']}

    unless manifests.find_by(args)
      manifest = manifests.create(args)
      m['dependencies'].each do |dep|
        platform = manifest.platform

        project = Project.platform(platform).find_by_name(dep['name'])

        manifest.repository_dependencies.create({
          project_id: project.try(:id),
          project_name: dep['name'].try(:strip),
          platform: platform,
          requirements: dep['version'],
          kind: dep['type']
        })
      end
    end
  end

  delete_old_manifests

  repository_subscriptions.each(&:update_subscriptions)
end

#download_ownerObject


89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'app/models/github_repository.rb', line 89

def download_owner
  o = github_client.user(owner_name)
  if o.type == "Organization"
    if go = GithubOrganisation.create_from_github(owner_id.to_i)
      self.github_organisation_id = go.id
      save
    end
  else
    GithubUser.find_or_create_by(github_id: o.id) do |u|
      u. = o.
      u.user_type = o.type
    end
  end
rescue Octokit::RepositoryUnavailable, Octokit::InvalidRepository, Octokit::NotFound, Octokit::Forbidden, Octokit::InternalServerError, Octokit::BadGateway => e
  nil
end

#download_readme(token = nil) ⇒ Object


202
203
204
205
206
207
208
209
210
211
# File 'app/models/github_repository.rb', line 202

def download_readme(token = nil)
  contents = {html_body: github_client(token).readme(full_name, accept: 'application/vnd.github.V3.html')}
  if readme.nil?
    create_readme(contents)
  else
    readme.update_attributes(contents)
  end
rescue Octokit::Unauthorized, Octokit::InvalidRepository, Octokit::RepositoryUnavailable, Octokit::NotFound, Octokit::Forbidden, Octokit::InternalServerError, Octokit::BadGateway
  nil
end

#download_tags(token = nil) ⇒ Object


289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
# File 'app/models/github_repository.rb', line 289

def download_tags(token = nil)
  existing_tags = github_tags.to_a
  github_client(token).refs(full_name, 'tags').each do |tag|
    return unless tag['ref']
    match = tag.ref.match(/refs\/tags\/(.*)/)
    if match
      name = match[1]
      if existing_tags.find{|t| t.name == name }.nil?

        object = github_client(token).get(tag.object.url)

        tag_hash = {
          name: name,
          kind: tag.object.type,
          sha: tag.object.sha
        }

        # map depending on if its a commit or a tag
        case tag.object.type
        when 'commit'
          tag_hash[:published_at] = object.committer.date
        when 'tag'
          tag_hash[:published_at] = object.tagger.date
        end

        github_tags.create!(tag_hash)
      end
    end
  end
rescue Octokit::Unauthorized, Octokit::InvalidRepository, Octokit::RepositoryUnavailable, Octokit::NotFound, Octokit::Conflict, Octokit::Forbidden, Octokit::InternalServerError, Octokit::BadGateway => e
  nil
end

#forksObject


130
131
132
# File 'app/models/github_repository.rb', line 130

def forks
  forks_count || 0
end

#forks_urlObject


146
147
148
# File 'app/models/github_repository.rb', line 146

def forks_url
  "#{url}/network"
end

#github_client(token = nil) ⇒ Object


194
195
196
# File 'app/models/github_repository.rb', line 194

def github_client(token = nil)
  AuthToken.fallback_client(token)
end

#id_or_nameObject


198
199
200
# File 'app/models/github_repository.rb', line 198

def id_or_name
  github_id || full_name
end

#is_deprecated?Boolean

Returns:

  • (Boolean)

61
62
63
# File 'app/models/github_repository.rb', line 61

def is_deprecated?
  status == 'Deprecated'
end

#is_removed?Boolean

Returns:

  • (Boolean)

65
66
67
# File 'app/models/github_repository.rb', line 65

def is_removed?
  status == 'Removed'
end

#is_unmaintained?Boolean

Returns:

  • (Boolean)

69
70
71
# File 'app/models/github_repository.rb', line 69

def is_unmaintained?
  status == 'Unmaintained'
end

#issues_urlObject


154
155
156
# File 'app/models/github_repository.rb', line 154

def issues_url
  "#{url}/issues"
end

#maintained?Boolean

Returns:

  • (Boolean)

73
74
75
# File 'app/models/github_repository.rb', line 73

def maintained?
  !is_deprecated? && !is_removed? && !is_unmaintained?
end

#ownerObject


85
86
87
# File 'app/models/github_repository.rb', line 85

def owner
  github_organisation_id.present? ? github_organisation : github_user
end

#owner_nameObject


114
115
116
# File 'app/models/github_repository.rb', line 114

def owner_name
  full_name.split('/')[0]
end

#pages_urlObject


134
135
136
# File 'app/models/github_repository.rb', line 134

def pages_url
  "http://#{owner_name}.github.io/#{project_name}"
end

#project_nameObject


118
119
120
# File 'app/models/github_repository.rb', line 118

def project_name
  full_name.split('/')[1]
end

#raw_urlObject


174
175
176
# File 'app/models/github_repository.rb', line 174

def raw_url
  "#{url}/raw/#{default_branch}/"
end

#readme_urlObject


182
183
184
# File 'app/models/github_repository.rb', line 182

def readme_url
  "#{url}#readme"
end

#repository_dependenciesObject


81
82
83
# File 'app/models/github_repository.rb', line 81

def repository_dependencies
  manifests.latest.includes(repository_dependencies: :project).map(&:repository_dependencies).flatten.uniq
end

#source_urlObject


166
167
168
# File 'app/models/github_repository.rb', line 166

def source_url
  "https://github.com/#{source_name}"
end

#stargazers_urlObject


150
151
152
# File 'app/models/github_repository.rb', line 150

def stargazers_url
  "#{url}/stargazers"
end

#starsObject


126
127
128
# File 'app/models/github_repository.rb', line 126

def stars
  stargazers_count || 0
end

#tags_urlObject


186
187
188
# File 'app/models/github_repository.rb', line 186

def tags_url
  "#{url}/tags"
end

#to_paramObject


110
111
112
# File 'app/models/github_repository.rb', line 110

def to_param
  full_name
end

#to_sObject


106
107
108
# File 'app/models/github_repository.rb', line 106

def to_s
  full_name
end

#touch_projectsObject


77
78
79
# File 'app/models/github_repository.rb', line 77

def touch_projects
  projects.find_each(&:save)
end

#update_all_info(token = nil) ⇒ Object


235
236
237
238
239
240
241
242
243
244
# File 'app/models/github_repository.rb', line 235

def update_all_info(token = nil)
  update_from_github(token)
  download_readme(token)
  download_tags(token)
  download_github_contributions(token)
  download_manifests(token)
  download_owner
  download_fork_source(token)
  touch_projects
end

#update_all_info_async(token = nil) ⇒ Object


231
232
233
# File 'app/models/github_repository.rb', line 231

def update_all_info_async(token = nil)
  GithubDownloadWorker.perform_async(self.id, token)
end

#update_from_github(token = nil) ⇒ Object


213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
# File 'app/models/github_repository.rb', line 213

def update_from_github(token = nil)
  begin
    r = github_client(token).repo(id_or_name, accept: 'application/vnd.github.drax-preview+json').to_hash
    return if r.nil? || r.empty?
    self.github_id = r[:id] unless self.github_id == r[:id]
    self.full_name = r[:full_name] if self.full_name.downcase != r[:full_name].downcase
    self.owner_id = r[:owner][:id]
    self.license = Project.format_license(r[:license][:key]) if r[:license]
    self.source_name = r[:parent][:full_name] if r[:fork]
    assign_attributes r.slice(*API_FIELDS)
    save! if self.changed?
  rescue Octokit::Unauthorized, Octokit::InvalidRepository, Octokit::RepositoryUnavailable, Octokit::Forbidden, Octokit::InternalServerError, Octokit::BadGateway => e
    nil
  rescue Octokit::NotFound
    update_attribute(:status, 'Removed') if !self.private?
  end
end

#urlObject


162
163
164
# File 'app/models/github_repository.rb', line 162

def url
  "https://github.com/#{full_name}"
end

#watchers_urlObject


142
143
144
# File 'app/models/github_repository.rb', line 142

def watchers_url
  "#{url}/watchers"
end

#wiki_urlObject


138
139
140
# File 'app/models/github_repository.rb', line 138

def wiki_url
  "#{url}/wiki"
end