Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Support for Storing Repo Labels #1683

Merged
merged 1 commit into from
Oct 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions app/models/label.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
class Label < ActiveRecord::Base
has_many :repo_labels
has_many :repos, through: :repo_lables
end
3 changes: 3 additions & 0 deletions app/models/repo.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ class Repo < ActiveRecord::Base
has_many :doc_methods, dependent: :destroy
delegate :open_issues, to: :issues

has_many :repo_labels
has_many :labels, through: :repo_labels

before_validation :set_full_name!, :downcase_name, :strip_whitespaces
after_create :background_populate_issues!, :update_repo_info!, :background_populate_docs!

Expand Down
4 changes: 4 additions & 0 deletions app/models/repo_label.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
class RepoLabel < ActiveRecord::Base
belongs_to :repo
belongs_to :label
end
37 changes: 37 additions & 0 deletions app/services/repo_label_assigner.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# frozen_string_literal: true

# PORO
# This class takes in a repo and fetch all labels for that repo. It then
# creates labels and associates them with the passed in repo
#
# Example:
#
# repo = Repo.first
# assigner = RepoLabelAssigner.new(repo: repo)
# assigner.create_and_associate_labels!
#
class RepoLabelAssigner
def initialize(repo:)
@repo = repo
url = ['repos', repo.user_name, repo.name, 'labels'].join('/')
@github_bub_response = GitHubBub.get(url)
end

def create_and_associate_labels!
return unless github_bub_response.success?

remote_labels.each do |label_hash|
label_name = label_hash['name'].downcase
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The issue stated that we wanted to only store hacktoberfest related labels. I'm happy to add a next unless line here if that's still the case but this is pretty simple as is.

label = Label.where(name: label_name).first_or_create!
repo.repo_labels.where(label: label).first_or_create
end
end

private

attr_reader :github_bub_response, :repo

def remote_labels
Array(github_bub_response.json_body)
end
end
9 changes: 9 additions & 0 deletions db/migrate/20221004010742_create_labels_table.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
class CreateLabelsTable < ActiveRecord::Migration[6.1]
def change
create_table :labels do |t|
t.string :name, null: false

t.timestamps
end
end
end
12 changes: 12 additions & 0 deletions db/migrate/20221004010749_create_repo_labels_table.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
class CreateRepoLabelsTable < ActiveRecord::Migration[6.1]
def change
create_table :repo_labels do |t|
t.references :repo, foreign_key: true, null: false
t.references :label, foreign_key: true, null: false

t.timestamps
end

add_index :repo_labels, [:repo_id, :label_id], unique: true
end
end
20 changes: 19 additions & 1 deletion db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 2022_09_08_011235) do
ActiveRecord::Schema.define(version: 2022_10_04_010749) do

# These are extensions that must be enabled in order to support this database
enable_extension "pg_stat_statements"
Expand Down Expand Up @@ -106,6 +106,22 @@
t.index ["updated_at"], name: "index_issues_on_updated_at", where: "((state)::text = 'open'::text)"
end

create_table "labels", force: :cascade do |t|
t.string "name", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end

create_table "repo_labels", force: :cascade do |t|
t.bigint "repo_id", null: false
t.bigint "label_id", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["label_id"], name: "index_repo_labels_on_label_id"
t.index ["repo_id", "label_id"], name: "index_repo_labels_on_repo_id_and_label_id", unique: true
t.index ["repo_id"], name: "index_repo_labels_on_repo_id"
end

create_table "repo_subscriptions", id: :serial, force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
Expand Down Expand Up @@ -190,6 +206,8 @@
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end

add_foreign_key "repo_labels", "labels"
add_foreign_key "repo_labels", "repos"
add_foreign_key "repo_subscriptions", "repos"
add_foreign_key "repo_subscriptions", "users"
end
7 changes: 7 additions & 0 deletions lib/tasks/schedule.rake
Original file line number Diff line number Diff line change
Expand Up @@ -111,4 +111,11 @@ namespace :schedule do
next unless Date.today.sunday?
Rake::Task['sitemap:refresh'].invoke
end

desc 'fetch and assign labels for repos'
task fetch_labels_and_assign: :environment do
Repo.find_each(batch_size: 100) do |repo|
RepoLabelAssigner.new(repo: repo).create_and_associate_labels!
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I considered creating a new background job here and enqueueing it for 10 repos at a time but worried about rate-limiting and not knowing the concurrency of background processing in production. I also wasn't sure what the total count of repos in production was.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Repo count is on the main page it's ~6,000. The way we do API requests is that we rotate through everyone's tokens on the service for each request.

GitHubBub::Request.set_before_callback do |request|
if request.token?
# Request is authorized, do nothing
else
request.token = code_triage_random_api_key_store.call
end
end

It's a good idea not to make a bunch of extra requests, but we're not that constrained. If this is N API requests where N is the number of repos in the system, that's absolutely no problem.

end
end
end
44 changes: 44 additions & 0 deletions test/unit/repo_labels_assigner_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# frozen_string_literal: true

require 'test_helper'

class RepoLabelsAssignerTest < ActiveSupport::TestCase
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Outside of my comfort zone here (I'm used to RSpec) so if this needs to change please let me know!

test '#create_and_associate_labels! creates labels' do
repo = repos(:rails_rails)
VCR.use_cassette('fetch_labels_for_repo', record: :once) do
assigner = RepoLabelAssigner.new(repo: repo)
assert_difference('Label.count', 30) do
assigner.create_and_associate_labels!
end
end
end

test '#create_and_associate_labels! it does not create existing labels' do
repo = repos(:rails_rails)
Label.create!(name: :autoloading)
VCR.use_cassette('fetch_labels_for_repo', record: :once) do
assigner = RepoLabelAssigner.new(repo: repo)
assigner.create_and_associate_labels!
assert_equal Label.where(name: :autoloading).count, 1
end
end

test '#create_and_associate_labels! it does not care about label case' do
repo = repos(:rails_rails)
VCR.use_cassette('fetch_labels_for_repo', record: :once) do
assigner = RepoLabelAssigner.new(repo: repo)
assigner.create_and_associate_labels!
assert_equal Label.where(name: :ActionMailer).count, 0
end
end

test '#create_and_associate_labels! associates labels with the repo' do
repo = repos(:rails_rails)
VCR.use_cassette('fetch_labels_for_repo', record: :once) do
assigner = RepoLabelAssigner.new(repo: repo)
assigner.create_and_associate_labels!
end

assert_equal Label.count, RepoLabel.where(repo: repo).count
end
end
91 changes: 91 additions & 0 deletions test/vcr_cassettes/fetch_labels_for_repo.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.