Skip to content
This repository has been archived by the owner on Mar 29, 2022. It is now read-only.

Require PRs be in a repo with hacktoberfest topic and be accepted #596

Merged
merged 15 commits into from
Oct 2, 2020
1 change: 1 addition & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ jobs:
PGUSER: postgres
PGDATABASE: hacktoberfest_test
END_DATE: "2019-10-01 00:00:00 UTC"
RULES_DATE: "2019-11-01 00:00:00 UTC"
START_DATE: "2019-11-01 00:00:00 UTC"
TEST_USER_GITHUB_TOKEN: ${{ secrets.TEST_USER_GITHUB_TOKEN }}
GITHUB_CLIENT_ID: ${{ secrets.GITHUB_CLIENT_ID }}
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ DALLI_SERVER=memcached
GITHUB_CLIENT_ID=<fill-in-for-dev-setup>
GITHUB_CLIENT_SECRET=<fill-in-for-dev-setup>
START_DATE=<fill-in-for-dev-setup>
RULES_DATE=<fill-in-for-dev-setup-same-as-start>
END_DATE=<fill-in-for-dev-setup>
AIRTABLE_API_KEY=<fill-in-for-dev-setup>
AIRTABLE_APP_ID=<fill-in-for-dev-setup>
Expand Down
9 changes: 9 additions & 0 deletions app/assets/images/icon-dark-unaccepted.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions app/assets/images/icon-light-unaccepted.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
41 changes: 36 additions & 5 deletions app/assets/stylesheets/profile.scss
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@
height: 32px;
margin-right: 20px;

.spammy-box {
.ineligible-box {
background-image: asset-url("icon-light-ineligible.svg");
background-repeat: no-repeat;
background-size: contain;
Expand All @@ -107,6 +107,16 @@
height: 32px;
}

.unaccepted-box {
background-image: asset-url("icon-light-unaccepted.svg");
background-repeat: no-repeat;
background-size: contain;
background-position: center;
display: inline-block;
width: 32px;
height: 32px;
}

.eligible-box {
background-image: asset-url("icon-light-eligible.svg");
background-repeat: no-repeat;
Expand Down Expand Up @@ -245,7 +255,7 @@
height: 32px;
margin-right: 20px;

.spammy-box {
.ineligible-box {
background-image: asset-url("icon-light-ineligible.svg");
background-repeat: no-repeat;
background-size: contain;
Expand All @@ -255,6 +265,16 @@
height: 32px;
}

.unaccepted-box {
background-image: asset-url("icon-light-unaccepted.svg");
background-repeat: no-repeat;
background-size: contain;
background-position: center;
display: inline-block;
width: 32px;
height: 32px;
}

.eligible-box {
background-image: asset-url("icon-light-eligible.svg");
background-repeat: no-repeat;
Expand Down Expand Up @@ -656,12 +676,23 @@ body.dark {
.main-content-wrapper
.legend-container
.icon-box
.spammy-box,
.timeline-date .spammy-box,
.profile-page .profile-left .timeline-wrapper .pr-icon .spammy-box {
.ineligible-box,
.timeline-date .ineligible-box,
.profile-page .profile-left .timeline-wrapper .pr-icon .ineligible-box {
background-image: asset-url("icon-dark-ineligible.svg") !important;
}

.profile-page
.profile-left
.main-content-wrapper
.legend-container
.icon-box
.unaccepted-box,
.timeline-date .unaccepted-box,
.profile-page .profile-left .timeline-wrapper .pr-icon .unaccepted-box {
background-image: asset-url("icon-dark-unaccepted.svg") !important;
}

.profile-page
.profile-left
.main-content-wrapper
Expand Down
10 changes: 10 additions & 0 deletions app/models/github_pull_request.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,22 @@ def merged?
@graphql_hash.merged
end

def approved?
@graphql_hash.reviewDecision == 'APPROVED'
end

def label_names
@graphql_hash.labels.edges.map do |e|
e.node.name.downcase
end
end

def repository_topics
@graphql_hash.repository.repositoryTopics.edges.map do |e|
e.node.topic.name.downcase
end
end

def name
url.split('/')[4]
end
Expand Down
67 changes: 53 additions & 14 deletions app/models/pull_request.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,38 +3,61 @@
class PullRequest < ApplicationRecord
attr_reader :github_pull_request

delegate :title, :body, :url, :created_at, :name, :owner, :repo_id,
:name_with_owner, :label_names, :merged?, to: :github_pull_request
delegate :title, :body, :url, :name, :owner, :repo_id,
:name_with_owner, :label_names, :repository_topics, :merged?,
:approved?, to: :github_pull_request

state_machine initial: :new do
event :spam_repo do
transition %i[new waiting] => :spam_repo,
transition all - %i[eligible] => :spam_repo,
if: ->(pr) { pr.spammy? }
end

event :invalid_label do
transition %i[new waiting] => :invalid_label,
transition all - %i[eligible] => :invalid_label,
if: ->(pr) { pr.labelled_invalid? }
end

event :topic_missing do
transition all - %i[eligible] => :topic_missing,
unless: ->(pr) { pr.in_topic_repo? }
end

event :not_accepted do
transition all - %i[eligible] => :not_accepted,
unless: ->(pr) { pr.maintainer_accepted? }
end

event :eligible do
transition %i[new waiting] => :eligible,
if: ->(pr) { !pr.spammy_or_invalid? && pr.older_than_week? }
if: lambda { |pr|
pr.passed_review_period? &&
!pr.spammy? &&
!pr.labelled_invalid? &&
pr.in_topic_repo? &&
pr.maintainer_accepted?
}
end

event :waiting do
transition %i[new spam_repo invalid_label] => :waiting,
if: ->(pr) { !pr.spammy_or_invalid? && !pr.older_than_week? }
transition all - %i[eligible] => :waiting,
if: lambda { |pr|
!pr.passed_review_period? &&
!pr.spammy? &&
!pr.labelled_invalid? &&
pr.in_topic_repo? &&
pr.maintainer_accepted?
}
end

before_transition to: %i[waiting],
from: %i[new] do |pr, _transition|
pr.waiting_since = pr.created_at
pr.waiting_since = Time.parse(pr.github_pull_request.created_at).utc
pr.save!
end

before_transition to: %i[waiting],
from: %i[spam_repo invalid_label] do |pr, _transition|
from: all - %i[new] do |pr, _transition|
pr.waiting_since = Time.zone.now
pr.save!
end
Expand All @@ -43,6 +66,8 @@ class PullRequest < ApplicationRecord
def check_state
return if spam_repo
return if invalid_label
return if topic_missing
return if not_accepted
return if eligible

waiting
Expand All @@ -51,11 +76,11 @@ def check_state
def most_recent_time
return waiting_since unless waiting_since.nil?

github_pull_request.created_at
Time.parse(github_pull_request.created_at).utc
end

def older_than_week?
most_recent_time <= (Time.zone.now - 7.days)
def passed_review_period?
most_recent_time <= (Time.zone.now - 14.days)
end

def labelled_invalid?
Expand All @@ -64,12 +89,26 @@ def labelled_invalid?
label_names.select { |l| l[/\b(invalid|spam)\b/i] }.any?
end

def labelled_accepted?
label_names.select { |l| l.downcase.strip == 'hacktoberfest-accepted' }.any?
end

def spammy?
SpamRepositoryService.call(repo_id)
end

def spammy_or_invalid?
labelled_invalid? || spammy?
def in_topic_repo?
# Don't have this requirement for old PRs
return true if created_at <= Hacktoberfest.rules_date

repository_topics.select { |topic| topic.strip == 'hacktoberfest' }.any?
end

def maintainer_accepted?
# Don't have this requirement for old PRs
return true if created_at <= Hacktoberfest.rules_date

merged? || approved? || labelled_accepted?
end

def github_id
Expand Down
15 changes: 13 additions & 2 deletions app/services/github_pull_request_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class UserNotFoundOnGithubError < StandardError; end
attr_reader :user

PULL_REQUEST_QUERY = <<~GRAPHQL
query($nodeId:ID!){
query($nodeId:ID!) {
node(id:$nodeId) {
... on User {
pullRequests(states: [OPEN, MERGED, CLOSED] last: 100) {
Expand All @@ -17,8 +17,19 @@ class UserNotFoundOnGithubError < StandardError; end
body
url
createdAt
repository{
merged
reviewDecision
repository {
databaseId
repositoryTopics(first: 100) {
edges {
node {
topic {
name
}
}
}
}
}
labels(first: 100) {
edges {
Expand Down
22 changes: 18 additions & 4 deletions app/views/users/show/_timeline.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,26 @@
<div class="pending-box">
</div>
</div>
<div class="icon-name-desc">
<div class="icon-name">
In Review
</div>
<div class="icon-desc">
Your PR has been accepted by a maintainer and is currently within the review period, which lasts for fourteen days.
</div>
</div>
</div>
<div class="legend-container">
<div class="icon-box">
<div class="unaccepted-box">
</div>
</div>
<div class="icon-name-desc">
<div class="icon-name">
Pending
</div>
<div class="icon-desc">
Your PR is currently within the review period, which lasts for seven days.
Your PR has not yet been accepted by a maintainer. A maintainer can accept your PR by merging it, adding the "hacktoberfest-accepted" label to it, or approving it.
</div>
</div>
</div>
Expand All @@ -69,15 +83,15 @@
</div>
<div class="legend-container">
<div class="icon-box">
<div class="spammy-box">
<div class="ineligible-box">
</div>
</div>
<div class="icon-name-desc">
<div class="icon-name">
Ineligible Repository
</div>
<div class="icon-desc">
Your PR was submitted to a repository that we've excluded from Hacktoberfest as it doesn't align with our core values.
Your PR was submitted to a repository that is not participating in Hacktoberfest. Maintainers of the repository can add the "hacktoberfest" topic to their repository if they wish to participate.
</div>
</div>
</div>
Expand All @@ -88,7 +102,7 @@
</div>
<div class="icon-name-desc">
<div class="icon-name">
Eligible
Accepted
</div>
<div class="icon-desc">
Good job! Your PR has passed the review period and counts toward completing the Hacktoberfest challenge!
Expand Down
9 changes: 7 additions & 2 deletions app/views/users/show/pr/_icon.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,14 @@
<div class="invalid-box">
</div>
</div>
<% elsif pr.spam_repo? %>
<% elsif pr.spam_repo? || pr.topic_missing? %>
<div class="icon-box">
<div class="spammy-box">
<div class="ineligible-box">
</div>
</div>
<% elsif pr.not_accepted? %>
<div class="icon-box">
<div class="unaccepted-box">
</div>
</div>
<% elsif pr.eligible? %>
Expand Down
2 changes: 1 addition & 1 deletion app/views/users/show/pr/_pr.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
</div>
<div class="pr-time-title">
<div class="timeline-date">
<p class="date"><%= DateTime.parse(pr.created_at).to_formatted_s(:long) %><br/></p>
<p class="date"><%= Time.parse(pr.github_pull_request.created_at).to_formatted_s(:long) %><br/></p>
</div>
<h4 class="pr-title">
You submitted <a href="<%= pr.url %>"><%= pr.title %></a> to <%= pr.name_with_owner %><br/>
Expand Down
1 change: 1 addition & 0 deletions config/dotenv.template
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ HACKTOBERFEST_API_KEY=sekret
GITHUB_CLIENT_ID=
GITHUB_CLIENT_SECRET=
START_DATE=
RULES_DATE=
END_DATE=
AIRTABLE_API_KEY=
AIRTABLE_APP_ID=
Expand Down
4 changes: 4 additions & 0 deletions config/initializers/constants.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ def end_date
@end_date ||= Time.parse(ENV.fetch('END_DATE')).utc
end

def rules_date
@rules_date ||= Time.parse(ENV.fetch('RULES_DATE')).utc
end

def pull_request_maturation_days
@pull_request_maturation_days ||= ENV.fetch('MATURATION_DAYS', 7).to_i.days
end
Expand Down
Loading