Skip to content

Commit

Permalink
ci: create pr summary weekly
Browse files Browse the repository at this point in the history
  • Loading branch information
doitian committed Aug 1, 2021
1 parent 8e00234 commit bb2bc7c
Show file tree
Hide file tree
Showing 7 changed files with 308 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .github/scripts/.bundle/config
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
BUNDLE_PATH: "vendor/bundle"
BUNDLE_WITHOUT: "production"
1 change: 1 addition & 0 deletions .github/scripts/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/vendor
8 changes: 8 additions & 0 deletions .github/scripts/Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# frozen_string_literal: true

source "https://rubygems.org"

git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }

gem "graphql-client"
gem "time_ago_in_words"
31 changes: 31 additions & 0 deletions .github/scripts/Gemfile.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
GEM
remote: https://rubygems.org/
specs:
activesupport (6.1.4)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 1.6, < 2)
minitest (>= 5.1)
tzinfo (~> 2.0)
zeitwerk (~> 2.3)
concurrent-ruby (1.1.9)
graphql (1.12.14)
graphql-client (0.16.0)
activesupport (>= 3.0)
graphql (~> 1.8)
i18n (1.8.10)
concurrent-ruby (~> 1.0)
minitest (5.14.4)
time_ago_in_words (0.1.1)
tzinfo (2.0.4)
concurrent-ruby (~> 1.0)
zeitwerk (2.4.2)

PLATFORMS
ruby

DEPENDENCIES
graphql-client
time_ago_in_words

BUNDLED WITH
2.1.4
15 changes: 15 additions & 0 deletions .github/scripts/github-graphql.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
require "graphql/client"
require "graphql/client/http"

module GitHubGraphQL
HTTP = GraphQL::Client::HTTP.new("https://api.github.com/graphql") do
def headers(context)
{
"User-Agent" => "My Client",
"Authorization" => "bearer #{ENV['GITHUB_TOKEN']}"
}
end
end
Schema = GraphQL::Client.load_schema(HTTP)
Client = GraphQL::Client.new(schema: Schema, execute: HTTP)
end
194 changes: 194 additions & 0 deletions .github/scripts/pr-summary.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
#!/usr/bin/env ruby

require 'time_ago_in_words'
require_relative 'github-graphql'

ReviewRequestsQuery = GitHubGraphQL::Client.parse <<-GRAPHQL
query($query: String!) {
search(query: $query, type: ISSUE, first: 100) {
nodes {
... on PullRequest {
number
url
title
isDraft
additions
deletions
changedFiles
createdAt
updatedAt
commits {
totalCount
}
reviewDecision
reviewRequests(last: 100) {
nodes {
requestedReviewer {
__typename,
... on User {
login
}
}
}
}
latestReviews(first:100) {
nodes {
author {
login
}
state
}
}
author {
login
}
}
}
}
}
GRAPHQL

repo_owner = 'nervosnetwork'
repo_name = 'rfcs'
body = []

def pluralize(count, singular, plural)
if count == 1
"#{count} #{singular}"
else
"#{count} #{plural}"
end
end

def format_pr(pr)
commits = pluralize(pr.commits.total_count, 'commit', 'commits')
changed_files = pluralize(pr.changed_files, 'file', 'files')
created = Time.parse(pr.created_at).ago_in_words
updated = Time.parse(pr.updated_at).ago_in_words
"#{pr.title} (#{commits}, #{changed_files}, +#{pr.additions}-#{pr.deletions}, created #{created}, last updated #{updated}) #{pr.url}"
end

def pr_review_state(pr)
states = pr.latest_reviews.nodes.group_by(&:state)
components = []
if states.include?('CHANGES_REQUESTED')
components << "#{states['CHANGES_REQUESTED'].size} requested changes"
end
if states.include?('COMMENTED')
components << "#{states['COMMENTED'].size} commented"
end
if states.include?('DISMISSED')
components << "#{states['DISMISSED'].size} dismissed"
end
if states.include?('PENDING')
components << "#{states['PENDING'].size} pending"
end

components.join(', ')
end

def author_link(login, repos)
query = "is:open is:pr author:#{login} #{repos} -review:approved"
"https://github.com/pulls?q=" + URI.encode_www_form_component(query)
end

def reviewer_link(login, repos)
query = "is:open is:pr review-requested:#{login} #{repos}"
"https://github.com/pulls?q=" + URI.encode_www_form_component(query)
end

pr_repos = "repo:#{repo_owner}/#{repo_name}"
open_prs = GitHubGraphQL::Client.query(ReviewRequestsQuery, variables: {
query: "#{pr_repos} is:pr is:open sort:created-asc"
}).data.search.nodes

if open_prs.size > 0
pending_author_stats = Hash.new {|h, k| h[k] = 0}
pending_reviewers_stats = Hash.new {|h, k| h[k] = 0}

body << "# Pull Requests (#{open_prs.size})\n"

ready_to_merge, remaining_prs = open_prs.partition do |pr|
pr.review_decision == 'APPROVED'
end
if ready_to_merge.size > 0
body << "## Ready To Merge (#{ready_to_merge.size})\n"
ready_to_merge.each do |pr|
body << "- #{pr.author.login} #{pr.title} #{pr.url}"
end
body << ''
end

pending_author, remaining_prs = remaining_prs.partition do |pr|
pr.is_draft || (
pr.review_decision == 'CHANGES_REQUESTED' &&
pr.latest_reviews.nodes.map(&:state).include?('CHANGES_REQUESTED')
)
end
if pending_author.size > 0
body << "## Pending Author (#{pending_author.size})\n"
pending_author.each do |pr|
pending_author_stats[pr.author.login] += 1

if pr.is_draft
body << "- **DRAFT**: @#{pr.author.login} #{format_pr(pr)}"
else
body << "- @#{pr.author.login} #{format_pr(pr)}"
review_state = pr_review_state(pr)
if review_state != ''
body << " - #{review_state}"
end
pending_reviewers = pr.review_requests.nodes.map do |node|
node.requested_reviewer.login
end.compact
if pending_reviewers.size > 0
pending_reviewers.each do |login|
pending_reviewers_stats[login] += 1
end
body << " - requested reviews from @#{pending_reviewers.join(' @')}"
end
end
end
body << ''
end

if remaining_prs.size > 0
body << "## Pending Reviewers (#{remaining_prs.size()})\n"
remaining_prs.each do |pr|
body << "- #{pr.author.login} #{format_pr(pr)}"
review_state = pr_review_state(pr)
if review_state != ''
body << " - #{review_state}"
end
pending_reviewers = pr.review_requests.nodes.map do |node|
node.requested_reviewer&.login
end.compact
if pending_reviewers.size > 0
pending_reviewers.each do |login|
pending_reviewers_stats[login] += 1
end
body << " - requested reviews from @#{pending_reviewers.join(' @')}"
end
end
body << ''
end

if pending_author_stats.size + pending_reviewers_stats.size > 0
body << "## By Owners\n"
(pending_author_stats.keys + pending_reviewers_stats.keys).uniq.each do |login|
body << "- @#{login}"
if pending_author_stats.include?(login)
body << " - #{pluralize(pending_author_stats[login], 'pr', 'prs')} to update #{author_link(login, pr_repos)}"
end
if pending_reviewers_stats.include?(login)
body << " - #{pluralize(pending_reviewers_stats[login], 'pr', 'prs')} to review #{reviewer_link(login, pr_repos)}"
end
end
body << ''
end

body << ''
end

puts body.join("\n")

56 changes: 56 additions & 0 deletions .github/workflows/pr-summary.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
name: Create PR Summary

on:
schedule:
- cron: '30 0 * * 1'
workflow_dispatch: {}

jobs:
discussion:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2

- uses: actions/setup-ruby@v1
with:
ruby-version: 2.7

- name: bundle install
run: cd .github/scripts/ && bundle install

- name: run ruby script
id: today
run: |
today="$(cd .github/scripts/ && bundle exec ruby today.rb)"
today="${today//$'%'/%25}"
today="${today//$'\n'/%0A}"
today="${today//$'\r'/%0D}"
echo "::set-output name=today::$today"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Get current date
id: date
run: echo "::set-output name=date::$(date +'%Y-%m-%d')"

- uses: octokit/graphql-action@v2.x
if: ${{ steps.today.outputs.today != '' }}
with:
query: |
mutation createDiscussionWithBody($body: String!) {
createDiscussion(input: {
title: "PR Summary ${{ steps.date.outputs.date }}",
body: $body,
categoryId: "DIC_kwDOB1Oxh84B-hNl",
repositoryId: "MDEwOlJlcG9zaXRvcnkxMjI5MjU0NDc=",
}) {
discussion {
url
}
}
}
body: ${{ toJSON(steps.today.outputs.today) }}

env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

0 comments on commit bb2bc7c

Please sign in to comment.