├── .github └── workflows │ └── gem_push.yml ├── .gitignore ├── .travis.yml ├── Gemfile ├── LICENSE ├── README.md ├── Rakefile ├── bin ├── console └── setup ├── exe └── furik ├── furik.gemspec ├── lib ├── furik.rb └── furik │ ├── cli.rb │ ├── configurable.rb │ ├── core_ext │ └── string.rb │ ├── events.rb │ ├── pull_requests.rb │ └── version.rb └── test ├── fixtures ├── orgs │ └── github │ │ └── repos?per_page=100.json ├── repos?per_page=100.json ├── user.json └── user │ ├── orgs?per_page=100.json │ └── repos?per_page=100.json ├── furik_test.rb ├── pull_requests_test.rb └── test_helper.rb /.github/workflows/gem_push.yml: -------------------------------------------------------------------------------- 1 | name: Publish gem to rubygems.org 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*' 7 | 8 | permissions: 9 | contents: read 10 | 11 | jobs: 12 | push: 13 | if: github.repository == 'pepabo/furik' 14 | runs-on: ubuntu-latest 15 | 16 | environment: 17 | name: rubygems.org 18 | url: https://rubygems.org/gems/furik 19 | 20 | permissions: 21 | contents: write 22 | id-token: write 23 | 24 | steps: 25 | # Set up 26 | - name: Harden Runner 27 | uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0 28 | with: 29 | egress-policy: audit 30 | 31 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 32 | 33 | - name: Set up Ruby 34 | uses: ruby/setup-ruby@fb404b9557c186e349162b0d8efb06e2bc36edea # v1.232.0 35 | with: 36 | bundler-cache: true 37 | ruby-version: ruby 38 | 39 | # Release 40 | - name: Publish to RubyGems 41 | uses: rubygems/release-gem@9e85cb11501bebc2ae661c1500176316d3987059 # v1 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /.yardoc 3 | /Gemfile.lock 4 | /_yardoc/ 5 | /coverage/ 6 | /doc/ 7 | /pkg/ 8 | /spec/reports/ 9 | /tmp/ 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | rvm: 3 | - 2.0.0 4 | - 2.1 5 | - 2.2 6 | before_install: gem install bundler -v 1.10.6 7 | script: 8 | - bundle exec rake test 9 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gemspec 4 | 5 | group :test do 6 | gem 'webmock' 7 | end 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015- GMO Pepabo, Inc. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Furik 2 | 3 | Furik is summary generator for your GitHub activities. 4 | 5 | [![ruby gem](https://img.shields.io/gem/v/furik.svg?style=flat-square)][gem] 6 | [![Travis](https://img.shields.io/travis/pepabo/furik.svg?style=flat-square)][travis] 7 | 8 | [gem]: https://rubygems.org/gems/furik 9 | [travis]: https://travis-ci.org/pepabo/furik 10 | 11 | ## Installation 12 | 13 | Install it yourself as: 14 | 15 | $ gem install furik 16 | 17 | ## Usage 18 | 19 | You can show GitHub activity while one day. 20 | 21 | $ furik activity 22 | 23 | Output example is here: 24 | 25 | ``` 26 | % furik activity 27 | Today's Activities 28 | - 29 | 30 | ### ruby/rubyspec 31 | 32 | - [pull_request](https://github.com/ruby/rubyspec/pull/158): Set Net::FTP.default_passive to false. 33 | - [comment](https://github.com/ruby/rubyspec/pull/158#issuecomment-155703551): :+1: (Set Net::FTP.default_passive t...) 34 | 35 | ### ruby/ruby 36 | 37 | - [pull_request](https://github.com/ruby/ruby/pull/1091): Fix typo, double 'means' 38 | (snip) 39 | ``` 40 | 41 | If you want to show GitHub and GitHub Enterprise activities, You need to add `-l` option to `furik` command. 42 | 43 | $ furik activity -l 44 | 45 | furik supports to store authentication via Pit. You are asked GitHub (and GitHub Enterprise) token from furik. 46 | 47 | Pit sotred your token to `~/.pit/default.yaml` by default. You can confirm or modify this yaml. 48 | 49 | ```sh 50 | % cat ~/.pit/default.yaml 51 | --- 52 | github.com: 53 | access_token: your_token 54 | your.github-enterprise.host: 55 | access_token: your_enterprise_token 56 | furik: 57 | github_enterprise_host: your.github-enterpise.host 58 | ``` 59 | 60 | NOTE: `your.github-enterprise.host` should be replaced with your true GH:E hostname(both yaml's key and value). 61 | 62 | ## Development 63 | 64 | After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. Run `bundle exec furik` to use the gem in this directory, ignoring other installed copies of this gem. 65 | 66 | ## Contributing 67 | 68 | Bug reports and pull requests are welcome on GitHub at https://github.com/pepabo/furik. 69 | 70 | ## LICENSE 71 | 72 | The MIT License (MIT) 73 | 74 | Copyright (c) 2015- GMO Pepabo, Inc. 75 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | require "rake/testtask" 3 | 4 | Rake::TestTask.new(:test) do |t| 5 | t.libs << "test" 6 | t.libs << "lib" 7 | t.test_files = FileList['test/**/*_test.rb'] 8 | end 9 | 10 | task :default => :test 11 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require "bundler/setup" 4 | require "furik" 5 | 6 | # You can add fixtures and/or initialization code here to make experimenting 7 | # with your gem easier. You can also use a different console, if you like. 8 | 9 | # (If you use this, don't forget to add pry to your Gemfile!) 10 | # require "pry" 11 | # Pry.start 12 | 13 | require "irb" 14 | IRB.start 15 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | 5 | bundle install 6 | 7 | # Do any other automated setup that you need to do here 8 | -------------------------------------------------------------------------------- /exe/furik: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'furik/cli' 3 | 4 | Furik::Cli.start 5 | -------------------------------------------------------------------------------- /furik.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'furik/version' 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = "furik" 8 | spec.version = Furik::VERSION 9 | spec.authors = ["linyows", "SHIBATA Hiroshi"] 10 | spec.email = ["linyows@gmail.com", "hsbt@ruby-lang.org"] 11 | 12 | spec.summary = %q{A summary generator of GitHub activity for retrospective.} 13 | spec.description = %q{A summary generator of GitHub activity for retrospective.} 14 | spec.homepage = "https://github.com/pepabo/furik" 15 | 16 | spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } 17 | spec.bindir = "exe" 18 | spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } 19 | spec.require_paths = ["lib"] 20 | 21 | spec.add_dependency 'octokit' 22 | spec.add_dependency 'pit' 23 | spec.add_dependency 'thor' 24 | 25 | spec.add_development_dependency "bundler", "~> 2" 26 | spec.add_development_dependency "rake" 27 | spec.add_development_dependency "minitest" 28 | end 29 | -------------------------------------------------------------------------------- /lib/furik.rb: -------------------------------------------------------------------------------- 1 | require 'octokit' 2 | require 'furik/core_ext/string' 3 | require 'furik/configurable' 4 | require 'furik/pull_requests' 5 | require 'furik/events' 6 | require "furik/version" 7 | 8 | module Furik 9 | class << self 10 | def gh_client 11 | Octokit::Client.new Configurable.github_octokit_options 12 | end 13 | 14 | def ghe_client 15 | Octokit::Client.new Configurable.github_enterprise_octokit_options 16 | end 17 | 18 | def events_with_grouping(gh: true, ghe: true, from: nil, to: nil, &block) 19 | events = [] 20 | 21 | if gh 22 | gh_events = Events.new(gh_client).events_with_grouping(from, to, &block) 23 | events.concat gh_events if gh_events.is_a?(Array) 24 | end 25 | 26 | if ghe 27 | ghe_events = Events.new(ghe_client).events_with_grouping(from, to, &block) 28 | events.concat ghe_events if ghe_events.is_a?(Array) 29 | end 30 | 31 | events 32 | end 33 | 34 | def pull_requests(gh: true, ghe: true, &block) 35 | pulls = [] 36 | 37 | if gh 38 | gh_pulls = PullRequests.new(gh_client).all(&block) 39 | pulls.concat gh_pulls if gh_pulls.is_a?(Array) 40 | end 41 | 42 | if ghe 43 | ghe_pulls = PullRequests.new(ghe_client).all(&block) 44 | pulls.concat ghe_pulls if ghe_pulls.is_a?(Array) 45 | end 46 | 47 | pulls 48 | end 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /lib/furik/cli.rb: -------------------------------------------------------------------------------- 1 | require "furik" 2 | require 'thor' 3 | 4 | module Furik 5 | class Cli < Thor 6 | desc 'pulls', 'show pull requests' 7 | method_option :gh, type: :boolean, aliases: '-g', default: true 8 | method_option :ghe, type: :boolean, aliases: '-l' 9 | method_option :start_date, type: :string, aliases: '-s' 10 | method_option :end_date, type: :string, aliases: '-e' 11 | def pulls 12 | start_date = Date.parse(options[:start_date]) if options[:start_date] 13 | end_date = Date.parse(options[:end_date]) if options[:end_date] 14 | 15 | puts "Pull Requests#{" (#{start_date}...#{end_date})" if start_date && end_date}" 16 | puts '-' 17 | puts '' 18 | 19 | Furik.pull_requests(gh: options[:gh], ghe: options[:ghe]) do |repo, issues| 20 | if issues && !issues.empty? 21 | string_issues = issues.each.with_object('') do |issue, memo| 22 | date = issue.created_at.localtime.to_date 23 | 24 | next if start_date && date < start_date 25 | next if end_date && date > end_date 26 | escaped_title = issue.title.gsub(/\[/, '\[').gsub(/\]/, '\]') 27 | memo << "- [#{escaped_title}](#{issue.html_url})" 28 | memo << " (#{issue.body.plain.cut})" if issue.body && !issue.body.empty? 29 | memo << " #{issue.created_at.localtime.to_date}\n" 30 | end 31 | 32 | unless string_issues == '' 33 | puts "### #{repo}" 34 | puts '' 35 | puts string_issues 36 | puts '' 37 | end 38 | end 39 | end 40 | end 41 | 42 | desc 'activity', 'show activity' 43 | method_option :gh, type: :boolean, aliases: '-g', default: true 44 | method_option :ghe, type: :boolean, aliases: '-l' 45 | method_option :since, type: :numeric, aliases: '-d', default: 0 46 | method_option :from, type: :string, aliases: '-f', default: Date.today.to_s 47 | method_option :to, type: :string, aliases: '-t', default: Date.today.to_s 48 | def activity 49 | from = Date.parse(options[:from]) 50 | to = Date.parse(options[:to]) 51 | since = options[:since] 52 | 53 | diff = (to - from).to_i 54 | diff.zero? ? from -= since : since = diff 55 | 56 | period = case since 57 | when 999 then 'All' 58 | when 0 then "Today's" 59 | else "#{since + 1}days" 60 | end 61 | puts "#{period} Activities" 62 | puts '-' 63 | puts '' 64 | 65 | Furik.events_with_grouping(gh: options[:gh], ghe: options[:ghe], from: from, to: to) do |repo, events| 66 | puts "### #{repo}" 67 | puts '' 68 | 69 | events.sort_by(&:type).reverse.each_with_object({ keys: [] }) do |event, memo| 70 | 71 | payload_type = event.type. 72 | gsub('Event', ''). 73 | gsub(/.*Comment/, 'Comment'). 74 | gsub('Issues', 'Issue'). 75 | underscore 76 | payload = event.payload.send(:"#{payload_type}") 77 | type = payload_type.dup 78 | 79 | title = case event.type 80 | when 'IssueCommentEvent' 81 | "#{payload.body.plain.cut} (#{event.payload.issue.title.cut(30)})" 82 | when 'CommitCommentEvent' 83 | payload.body.plain.cut 84 | when 'IssuesEvent' 85 | type = "#{event.payload.action}_#{type}" 86 | payload.title.plain.cut 87 | when 'PullRequestReviewCommentEvent' 88 | type = 'comment' 89 | if event.payload.pull_request.respond_to?(:title) 90 | "#{payload.body.plain.cut} (#{event.payload.pull_request.title.cut(30)})" 91 | else 92 | payload.body.plain.cut 93 | end 94 | else 95 | payload.title.plain.cut 96 | end 97 | 98 | link = payload.html_url 99 | key = "#{type}-#{link}" 100 | 101 | next if memo[:keys].include?(key) 102 | memo[:keys] << key 103 | 104 | puts "- [#{type}](#{link}): #{title}" 105 | end 106 | 107 | puts '' 108 | end 109 | end 110 | end 111 | end 112 | -------------------------------------------------------------------------------- /lib/furik/configurable.rb: -------------------------------------------------------------------------------- 1 | require 'yaml' 2 | require 'pit' 3 | 4 | module Furik 5 | module Configurable 6 | class << self 7 | def hub_config_path 8 | ENV['HUB_CONFIG_PATH'] || "#{Dir.home}/.config/hub" 9 | end 10 | 11 | def token_generate_path 12 | '/settings/tokens/new' 13 | end 14 | 15 | def token_by_hub(host = 'github.com') 16 | return unless File.exist?(hub_config_path) 17 | 18 | hub_config = YAML.load_file hub_config_path 19 | if !hub_config[host].nil? && !hub_config[host].empty? 20 | hub_config[host].last['oauth_token'] 21 | end 22 | end 23 | 24 | def token_by_pit(host = 'github.com') 25 | Pit.get(host, require: { 26 | 'access_token' => "#{host} Access Token? (https://#{host}#{token_generate_path})" 27 | })['access_token'] 28 | end 29 | 30 | def github_access_token 31 | token_by_hub || token_by_pit 32 | end 33 | 34 | def github_enterprise_host 35 | ENV['GITHUB_ENTERPRISE_HOST'] || github_enterprise_host_by_pit 36 | end 37 | 38 | def github_enterprise_host_by_pit 39 | Pit.get('furik', require: { 40 | 'github_enterprise_host' => 'Github:Enterprise Host?(ex: your.domain.com)' 41 | })['github_enterprise_host'] 42 | end 43 | 44 | def github_enterprise_access_token 45 | token_by_hub(github_enterprise_host) || token_by_pit(github_enterprise_host) 46 | end 47 | 48 | def default_octokit_options 49 | { 50 | auto_paginate: true, 51 | per_page: 100 52 | } 53 | end 54 | 55 | def github_octokit_options 56 | default_octokit_options.merge( 57 | access_token: github_access_token 58 | ) 59 | end 60 | 61 | def github_enterprise_octokit_options 62 | default_octokit_options.merge( 63 | access_token: github_enterprise_access_token, 64 | api_endpoint: "https://#{github_enterprise_host}/api/v3/" 65 | ) 66 | end 67 | end 68 | end 69 | end 70 | -------------------------------------------------------------------------------- /lib/furik/core_ext/string.rb: -------------------------------------------------------------------------------- 1 | class String 2 | def underscore 3 | self.gsub(/::/, '/'). 4 | gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2'). 5 | gsub(/([a-z\d])([A-Z])/,'\1_\2'). 6 | tr('-', '_'). 7 | downcase 8 | end 9 | 10 | def plain 11 | self.gsub("\r\n", ' ').gsub(/[\s\-_=]{2,}/, ' ').strip 12 | end 13 | 14 | def cut(size = 50, options = {}) 15 | text = self.dup 16 | options[:omission] ||= '...' 17 | chars = '' 18 | current_size = 0 19 | text.each_char do |c| 20 | current_size += c =~ /^[ -~。-゚]*$/ ? 1 : 2 21 | break if current_size > size 22 | chars << c 23 | end 24 | chars << options[:omission] if current_size > size 25 | chars.to_s 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/furik/events.rb: -------------------------------------------------------------------------------- 1 | module Furik 2 | class Events 3 | def initialize(client) 4 | @client = client 5 | @login = client.login 6 | end 7 | 8 | def events_with_grouping(from, to, &block) 9 | @client.user_events(@login).each.with_object({}) { |event, memo| 10 | if event && aggressives.include?(event.type) 11 | if from <= event.created_at.localtime.to_date && event.created_at.localtime.to_date <= to 12 | memo[event.repo.name] ||= [] 13 | memo[event.repo.name] << event 14 | end 15 | end 16 | }.each do |repo, events| 17 | block.call(repo, events) if block 18 | end 19 | end 20 | 21 | def aggressives 22 | %w( 23 | IssuesEvent 24 | PullRequestEvent 25 | PullRequestReviewCommentEvent 26 | IssueCommentEvent 27 | CommitCommentEvent 28 | ) 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /lib/furik/pull_requests.rb: -------------------------------------------------------------------------------- 1 | require 'octokit' 2 | 3 | module Furik 4 | class PullRequests 5 | def initialize(client) 6 | @client = client 7 | @login = client.login 8 | end 9 | 10 | def request_manager 11 | limit = @client.rate_limit 12 | if !limit.limit.zero? && limit.remaining.zero? 13 | puts "Oops! #{limit}" 14 | sleep limit.resets_in 15 | end 16 | # No rate limit for white listed users 17 | rescue Octokit::NotFound 18 | end 19 | 20 | def all(&block) 21 | @block = block 22 | @all || all! 23 | end 24 | 25 | def all! 26 | @all = all_repo_names.each.with_object([]) do |repo_name, memo| 27 | pulls = pull_requests(repo_name) 28 | memo.concat pulls if pulls.is_a?(Array) 29 | request_manager 30 | end 31 | end 32 | 33 | def org_name_from(repo_name) 34 | repo_name.split('/').first 35 | end 36 | 37 | # Use the issues api so specify the creator 38 | def pull_requests(repo_name) 39 | org_name = org_name_from(repo_name) 40 | 41 | unless @block 42 | if @org_name == org_name 43 | print '-' 44 | else 45 | puts '' 46 | print "#{org_name} -" 47 | @org_name = org_name 48 | end 49 | end 50 | 51 | issues = @client.issues(repo_name, creator: @login, state: 'open'). 52 | select { |issue| issue.pull_request } 53 | issues.concat @client.issues(repo_name, creator: @login, state: 'closed'). 54 | select { |issue| issue.pull_request } 55 | 56 | @block.call(repo_name, issues) if @block 57 | 58 | issues 59 | rescue Octokit::ClientError 60 | rescue => e 61 | puts e 62 | end 63 | 64 | def all_repo_names 65 | self.user_repo_names.concat(self.orgs_repo_names).uniq 66 | end 67 | 68 | # It contains repositories of your organization, If you are a public member 69 | # of the organization. 70 | def user_repo_names 71 | @client.repos.map(&:full_name) 72 | end 73 | 74 | def user_orgs_names 75 | @client.orgs.map(&:login) 76 | end 77 | 78 | # It contains all repositories for your organization. 79 | def org_repo_names(org_name) 80 | @client.org_repos(org_name).map(&:full_name) 81 | end 82 | 83 | def orgs_repo_names 84 | user_orgs_names.each_with_object([]) do |org_name, memo| 85 | memo.concat org_repo_names(org_name) 86 | end 87 | end 88 | end 89 | end 90 | -------------------------------------------------------------------------------- /lib/furik/version.rb: -------------------------------------------------------------------------------- 1 | module Furik 2 | VERSION = "0.2.1" 3 | end 4 | -------------------------------------------------------------------------------- /test/fixtures/orgs/github/repos?per_page=100.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 1296269, 4 | "owner": { 5 | "login": "octocat", 6 | "id": 1, 7 | "avatar_url": "https://github.com/images/error/octocat_happy.gif", 8 | "gravatar_id": "", 9 | "url": "https://api.github.com/users/octocat", 10 | "html_url": "https://github.com/octocat", 11 | "followers_url": "https://api.github.com/users/octocat/followers", 12 | "following_url": "https://api.github.com/users/octocat/following{/other_user}", 13 | "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", 14 | "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", 15 | "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", 16 | "organizations_url": "https://api.github.com/users/octocat/orgs", 17 | "repos_url": "https://api.github.com/users/octocat/repos", 18 | "events_url": "https://api.github.com/users/octocat/events{/privacy}", 19 | "received_events_url": "https://api.github.com/users/octocat/received_events", 20 | "type": "User", 21 | "site_admin": false 22 | }, 23 | "name": "Hello-World", 24 | "full_name": "octocat/Hello-World", 25 | "description": "This your first repo!", 26 | "private": false, 27 | "fork": false, 28 | "url": "https://api.github.com/repos/octocat/Hello-World", 29 | "html_url": "https://github.com/octocat/Hello-World", 30 | "archive_url": "http://api.github.com/repos/octocat/Hello-World/{archive_format}{/ref}", 31 | "assignees_url": "http://api.github.com/repos/octocat/Hello-World/assignees{/user}", 32 | "blobs_url": "http://api.github.com/repos/octocat/Hello-World/git/blobs{/sha}", 33 | "branches_url": "http://api.github.com/repos/octocat/Hello-World/branches{/branch}", 34 | "clone_url": "https://github.com/octocat/Hello-World.git", 35 | "collaborators_url": "http://api.github.com/repos/octocat/Hello-World/collaborators{/collaborator}", 36 | "comments_url": "http://api.github.com/repos/octocat/Hello-World/comments{/number}", 37 | "commits_url": "http://api.github.com/repos/octocat/Hello-World/commits{/sha}", 38 | "compare_url": "http://api.github.com/repos/octocat/Hello-World/compare/{base}...{head}", 39 | "contents_url": "http://api.github.com/repos/octocat/Hello-World/contents/{+path}", 40 | "contributors_url": "http://api.github.com/repos/octocat/Hello-World/contributors", 41 | "downloads_url": "http://api.github.com/repos/octocat/Hello-World/downloads", 42 | "events_url": "http://api.github.com/repos/octocat/Hello-World/events", 43 | "forks_url": "http://api.github.com/repos/octocat/Hello-World/forks", 44 | "git_commits_url": "http://api.github.com/repos/octocat/Hello-World/git/commits{/sha}", 45 | "git_refs_url": "http://api.github.com/repos/octocat/Hello-World/git/refs{/sha}", 46 | "git_tags_url": "http://api.github.com/repos/octocat/Hello-World/git/tags{/sha}", 47 | "git_url": "git:github.com/octocat/Hello-World.git", 48 | "hooks_url": "http://api.github.com/repos/octocat/Hello-World/hooks", 49 | "issue_comment_url": "http://api.github.com/repos/octocat/Hello-World/issues/comments{/number}", 50 | "issue_events_url": "http://api.github.com/repos/octocat/Hello-World/issues/events{/number}", 51 | "issues_url": "http://api.github.com/repos/octocat/Hello-World/issues{/number}", 52 | "keys_url": "http://api.github.com/repos/octocat/Hello-World/keys{/key_id}", 53 | "labels_url": "http://api.github.com/repos/octocat/Hello-World/labels{/name}", 54 | "languages_url": "http://api.github.com/repos/octocat/Hello-World/languages", 55 | "merges_url": "http://api.github.com/repos/octocat/Hello-World/merges", 56 | "milestones_url": "http://api.github.com/repos/octocat/Hello-World/milestones{/number}", 57 | "mirror_url": "git:git.example.com/octocat/Hello-World", 58 | "notifications_url": "http://api.github.com/repos/octocat/Hello-World/notifications{?since, all, participating}", 59 | "pulls_url": "http://api.github.com/repos/octocat/Hello-World/pulls{/number}", 60 | "releases_url": "http://api.github.com/repos/octocat/Hello-World/releases{/id}", 61 | "ssh_url": "git@github.com:octocat/Hello-World.git", 62 | "stargazers_url": "http://api.github.com/repos/octocat/Hello-World/stargazers", 63 | "statuses_url": "http://api.github.com/repos/octocat/Hello-World/statuses/{sha}", 64 | "subscribers_url": "http://api.github.com/repos/octocat/Hello-World/subscribers", 65 | "subscription_url": "http://api.github.com/repos/octocat/Hello-World/subscription", 66 | "svn_url": "https://svn.github.com/octocat/Hello-World", 67 | "tags_url": "http://api.github.com/repos/octocat/Hello-World/tags", 68 | "teams_url": "http://api.github.com/repos/octocat/Hello-World/teams", 69 | "trees_url": "http://api.github.com/repos/octocat/Hello-World/git/trees{/sha}", 70 | "homepage": "https://github.com", 71 | "language": null, 72 | "forks_count": 9, 73 | "stargazers_count": 80, 74 | "watchers_count": 80, 75 | "size": 108, 76 | "default_branch": "master", 77 | "open_issues_count": 0, 78 | "has_issues": true, 79 | "has_wiki": true, 80 | "has_pages": false, 81 | "has_downloads": true, 82 | "pushed_at": "2011-01-26T19:06:43Z", 83 | "created_at": "2011-01-26T19:01:12Z", 84 | "updated_at": "2011-01-26T19:14:43Z", 85 | "permissions": { 86 | "admin": false, 87 | "push": false, 88 | "pull": true 89 | } 90 | } 91 | ] 92 | -------------------------------------------------------------------------------- /test/fixtures/repos?per_page=100.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 1296269, 4 | "owner": { 5 | "login": "octocat", 6 | "id": 1, 7 | "avatar_url": "https://github.com/images/error/octocat_happy.gif", 8 | "gravatar_id": "", 9 | "url": "https://api.github.com/users/octocat", 10 | "html_url": "https://github.com/octocat", 11 | "followers_url": "https://api.github.com/users/octocat/followers", 12 | "following_url": "https://api.github.com/users/octocat/following{/other_user}", 13 | "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", 14 | "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", 15 | "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", 16 | "organizations_url": "https://api.github.com/users/octocat/orgs", 17 | "repos_url": "https://api.github.com/users/octocat/repos", 18 | "events_url": "https://api.github.com/users/octocat/events{/privacy}", 19 | "received_events_url": "https://api.github.com/users/octocat/received_events", 20 | "type": "User", 21 | "site_admin": false 22 | }, 23 | "name": "Hello-World", 24 | "full_name": "octocat/Hello-World", 25 | "description": "This your first repo!", 26 | "private": false, 27 | "fork": false, 28 | "url": "https://api.github.com/repos/octocat/Hello-World", 29 | "html_url": "https://github.com/octocat/Hello-World", 30 | "archive_url": "http://api.github.com/repos/octocat/Hello-World/{archive_format}{/ref}", 31 | "assignees_url": "http://api.github.com/repos/octocat/Hello-World/assignees{/user}", 32 | "blobs_url": "http://api.github.com/repos/octocat/Hello-World/git/blobs{/sha}", 33 | "branches_url": "http://api.github.com/repos/octocat/Hello-World/branches{/branch}", 34 | "clone_url": "https://github.com/octocat/Hello-World.git", 35 | "collaborators_url": "http://api.github.com/repos/octocat/Hello-World/collaborators{/collaborator}", 36 | "comments_url": "http://api.github.com/repos/octocat/Hello-World/comments{/number}", 37 | "commits_url": "http://api.github.com/repos/octocat/Hello-World/commits{/sha}", 38 | "compare_url": "http://api.github.com/repos/octocat/Hello-World/compare/{base}...{head}", 39 | "contents_url": "http://api.github.com/repos/octocat/Hello-World/contents/{+path}", 40 | "contributors_url": "http://api.github.com/repos/octocat/Hello-World/contributors", 41 | "downloads_url": "http://api.github.com/repos/octocat/Hello-World/downloads", 42 | "events_url": "http://api.github.com/repos/octocat/Hello-World/events", 43 | "forks_url": "http://api.github.com/repos/octocat/Hello-World/forks", 44 | "git_commits_url": "http://api.github.com/repos/octocat/Hello-World/git/commits{/sha}", 45 | "git_refs_url": "http://api.github.com/repos/octocat/Hello-World/git/refs{/sha}", 46 | "git_tags_url": "http://api.github.com/repos/octocat/Hello-World/git/tags{/sha}", 47 | "git_url": "git:github.com/octocat/Hello-World.git", 48 | "hooks_url": "http://api.github.com/repos/octocat/Hello-World/hooks", 49 | "issue_comment_url": "http://api.github.com/repos/octocat/Hello-World/issues/comments{/number}", 50 | "issue_events_url": "http://api.github.com/repos/octocat/Hello-World/issues/events{/number}", 51 | "issues_url": "http://api.github.com/repos/octocat/Hello-World/issues{/number}", 52 | "keys_url": "http://api.github.com/repos/octocat/Hello-World/keys{/key_id}", 53 | "labels_url": "http://api.github.com/repos/octocat/Hello-World/labels{/name}", 54 | "languages_url": "http://api.github.com/repos/octocat/Hello-World/languages", 55 | "merges_url": "http://api.github.com/repos/octocat/Hello-World/merges", 56 | "milestones_url": "http://api.github.com/repos/octocat/Hello-World/milestones{/number}", 57 | "mirror_url": "git:git.example.com/octocat/Hello-World", 58 | "notifications_url": "http://api.github.com/repos/octocat/Hello-World/notifications{?since, all, participating}", 59 | "pulls_url": "http://api.github.com/repos/octocat/Hello-World/pulls{/number}", 60 | "releases_url": "http://api.github.com/repos/octocat/Hello-World/releases{/id}", 61 | "ssh_url": "git@github.com:octocat/Hello-World.git", 62 | "stargazers_url": "http://api.github.com/repos/octocat/Hello-World/stargazers", 63 | "statuses_url": "http://api.github.com/repos/octocat/Hello-World/statuses/{sha}", 64 | "subscribers_url": "http://api.github.com/repos/octocat/Hello-World/subscribers", 65 | "subscription_url": "http://api.github.com/repos/octocat/Hello-World/subscription", 66 | "svn_url": "https://svn.github.com/octocat/Hello-World", 67 | "tags_url": "http://api.github.com/repos/octocat/Hello-World/tags", 68 | "teams_url": "http://api.github.com/repos/octocat/Hello-World/teams", 69 | "trees_url": "http://api.github.com/repos/octocat/Hello-World/git/trees{/sha}", 70 | "homepage": "https://github.com", 71 | "language": null, 72 | "forks_count": 9, 73 | "stargazers_count": 80, 74 | "watchers_count": 80, 75 | "size": 108, 76 | "default_branch": "master", 77 | "open_issues_count": 0, 78 | "has_issues": true, 79 | "has_wiki": true, 80 | "has_pages": false, 81 | "has_downloads": true, 82 | "pushed_at": "2011-01-26T19:06:43Z", 83 | "created_at": "2011-01-26T19:01:12Z", 84 | "updated_at": "2011-01-26T19:14:43Z", 85 | "permissions": { 86 | "admin": false, 87 | "push": false, 88 | "pull": true 89 | } 90 | } 91 | ] 92 | -------------------------------------------------------------------------------- /test/fixtures/user.json: -------------------------------------------------------------------------------- 1 | { 2 | "login": "octocat", 3 | "id": 1, 4 | "avatar_url": "https://github.com/images/error/octocat_happy.gif", 5 | "gravatar_id": "", 6 | "url": "https://api.github.com/users/octocat", 7 | "html_url": "https://github.com/octocat", 8 | "followers_url": "https://api.github.com/users/octocat/followers", 9 | "following_url": "https://api.github.com/users/octocat/following{/other_user}", 10 | "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", 11 | "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", 12 | "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", 13 | "organizations_url": "https://api.github.com/users/octocat/orgs", 14 | "repos_url": "https://api.github.com/users/octocat/repos", 15 | "events_url": "https://api.github.com/users/octocat/events{/privacy}", 16 | "received_events_url": "https://api.github.com/users/octocat/received_events", 17 | "type": "User", 18 | "site_admin": false, 19 | "name": "monalisa octocat", 20 | "company": "GitHub", 21 | "blog": "https://github.com/blog", 22 | "location": "San Francisco", 23 | "email": "octocat@github.com", 24 | "hireable": false, 25 | "bio": "There once was...", 26 | "public_repos": 2, 27 | "public_gists": 1, 28 | "followers": 20, 29 | "following": 0, 30 | "created_at": "2008-01-14T04:33:35Z", 31 | "updated_at": "2008-01-14T04:33:35Z", 32 | "total_private_repos": 100, 33 | "owned_private_repos": 100, 34 | "private_gists": 81, 35 | "disk_usage": 10000, 36 | "collaborators": 8, 37 | "plan": { 38 | "name": "Medium", 39 | "space": 400, 40 | "private_repos": 20, 41 | "collaborators": 0 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /test/fixtures/user/orgs?per_page=100.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "login": "github", 4 | "id": 1, 5 | "url": "https://api.github.com/orgs/github", 6 | "avatar_url": "https://github.com/images/error/octocat_happy.gif", 7 | "description": "A great organization" 8 | } 9 | ] 10 | -------------------------------------------------------------------------------- /test/fixtures/user/repos?per_page=100.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 1296269, 4 | "owner": { 5 | "login": "octocat", 6 | "id": 1, 7 | "avatar_url": "https://github.com/images/error/octocat_happy.gif", 8 | "gravatar_id": "", 9 | "url": "https://api.github.com/users/octocat", 10 | "html_url": "https://github.com/octocat", 11 | "followers_url": "https://api.github.com/users/octocat/followers", 12 | "following_url": "https://api.github.com/users/octocat/following{/other_user}", 13 | "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", 14 | "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", 15 | "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", 16 | "organizations_url": "https://api.github.com/users/octocat/orgs", 17 | "repos_url": "https://api.github.com/users/octocat/repos", 18 | "events_url": "https://api.github.com/users/octocat/events{/privacy}", 19 | "received_events_url": "https://api.github.com/users/octocat/received_events", 20 | "type": "User", 21 | "site_admin": false 22 | }, 23 | "name": "Hello-World", 24 | "full_name": "octocat/Hello-World", 25 | "description": "This your first repo!", 26 | "private": false, 27 | "fork": false, 28 | "url": "https://api.github.com/repos/octocat/Hello-World", 29 | "html_url": "https://github.com/octocat/Hello-World", 30 | "archive_url": "http://api.github.com/repos/octocat/Hello-World/{archive_format}{/ref}", 31 | "assignees_url": "http://api.github.com/repos/octocat/Hello-World/assignees{/user}", 32 | "blobs_url": "http://api.github.com/repos/octocat/Hello-World/git/blobs{/sha}", 33 | "branches_url": "http://api.github.com/repos/octocat/Hello-World/branches{/branch}", 34 | "clone_url": "https://github.com/octocat/Hello-World.git", 35 | "collaborators_url": "http://api.github.com/repos/octocat/Hello-World/collaborators{/collaborator}", 36 | "comments_url": "http://api.github.com/repos/octocat/Hello-World/comments{/number}", 37 | "commits_url": "http://api.github.com/repos/octocat/Hello-World/commits{/sha}", 38 | "compare_url": "http://api.github.com/repos/octocat/Hello-World/compare/{base}...{head}", 39 | "contents_url": "http://api.github.com/repos/octocat/Hello-World/contents/{+path}", 40 | "contributors_url": "http://api.github.com/repos/octocat/Hello-World/contributors", 41 | "downloads_url": "http://api.github.com/repos/octocat/Hello-World/downloads", 42 | "events_url": "http://api.github.com/repos/octocat/Hello-World/events", 43 | "forks_url": "http://api.github.com/repos/octocat/Hello-World/forks", 44 | "git_commits_url": "http://api.github.com/repos/octocat/Hello-World/git/commits{/sha}", 45 | "git_refs_url": "http://api.github.com/repos/octocat/Hello-World/git/refs{/sha}", 46 | "git_tags_url": "http://api.github.com/repos/octocat/Hello-World/git/tags{/sha}", 47 | "git_url": "git:github.com/octocat/Hello-World.git", 48 | "hooks_url": "http://api.github.com/repos/octocat/Hello-World/hooks", 49 | "issue_comment_url": "http://api.github.com/repos/octocat/Hello-World/issues/comments{/number}", 50 | "issue_events_url": "http://api.github.com/repos/octocat/Hello-World/issues/events{/number}", 51 | "issues_url": "http://api.github.com/repos/octocat/Hello-World/issues{/number}", 52 | "keys_url": "http://api.github.com/repos/octocat/Hello-World/keys{/key_id}", 53 | "labels_url": "http://api.github.com/repos/octocat/Hello-World/labels{/name}", 54 | "languages_url": "http://api.github.com/repos/octocat/Hello-World/languages", 55 | "merges_url": "http://api.github.com/repos/octocat/Hello-World/merges", 56 | "milestones_url": "http://api.github.com/repos/octocat/Hello-World/milestones{/number}", 57 | "mirror_url": "git:git.example.com/octocat/Hello-World", 58 | "notifications_url": "http://api.github.com/repos/octocat/Hello-World/notifications{?since, all, participating}", 59 | "pulls_url": "http://api.github.com/repos/octocat/Hello-World/pulls{/number}", 60 | "releases_url": "http://api.github.com/repos/octocat/Hello-World/releases{/id}", 61 | "ssh_url": "git@github.com:octocat/Hello-World.git", 62 | "stargazers_url": "http://api.github.com/repos/octocat/Hello-World/stargazers", 63 | "statuses_url": "http://api.github.com/repos/octocat/Hello-World/statuses/{sha}", 64 | "subscribers_url": "http://api.github.com/repos/octocat/Hello-World/subscribers", 65 | "subscription_url": "http://api.github.com/repos/octocat/Hello-World/subscription", 66 | "svn_url": "https://svn.github.com/octocat/Hello-World", 67 | "tags_url": "http://api.github.com/repos/octocat/Hello-World/tags", 68 | "teams_url": "http://api.github.com/repos/octocat/Hello-World/teams", 69 | "trees_url": "http://api.github.com/repos/octocat/Hello-World/git/trees{/sha}", 70 | "homepage": "https://github.com", 71 | "language": null, 72 | "forks_count": 9, 73 | "stargazers_count": 80, 74 | "watchers_count": 80, 75 | "size": 108, 76 | "default_branch": "master", 77 | "open_issues_count": 0, 78 | "has_issues": true, 79 | "has_wiki": true, 80 | "has_pages": false, 81 | "has_downloads": true, 82 | "pushed_at": "2011-01-26T19:06:43Z", 83 | "created_at": "2011-01-26T19:01:12Z", 84 | "updated_at": "2011-01-26T19:14:43Z", 85 | "permissions": { 86 | "admin": false, 87 | "push": false, 88 | "pull": true 89 | } 90 | } 91 | ] 92 | -------------------------------------------------------------------------------- /test/furik_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class FurikTest < Minitest::Test 4 | def test_that_it_has_a_version_number 5 | refute_nil ::Furik::VERSION 6 | end 7 | 8 | def test_gh_client_returns_octokit_client 9 | assert_equal Furik.gh_client.class, Octokit::Client 10 | end 11 | 12 | def test_ghe_client_returns_octokit_client 13 | assert_equal Furik.ghe_client.class, Octokit::Client 14 | end 15 | 16 | def test_events_with_grouping_empty_array 17 | assert_equal Furik.events_with_grouping(gh: false, ghe: false), [] 18 | end 19 | 20 | def test_pull_requests_returns_empty_array 21 | assert_equal Furik.pull_requests(gh: false, ghe: false), [] 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /test/pull_requests_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class PullRequestsTest < Minitest::Test 4 | def test_all_repo_names 5 | stub_get 'user' 6 | stub_get 'user/repos?per_page=100' 7 | stub_get 'user/orgs?per_page=100' 8 | stub_get 'orgs/github/repos?per_page=100' 9 | 10 | instance = Furik::PullRequests.new(Furik.gh_client) 11 | assert_equal instance.all_repo_names, ['octocat/Hello-World'] 12 | end 13 | 14 | def test_user_repo_names_returns_repos 15 | stub_get 'user' 16 | stub_get 'repos?per_page=100' 17 | stub_get 'user/repos?per_page=100' 18 | 19 | instance = Furik::PullRequests.new(Furik.gh_client) 20 | assert_equal instance.user_repo_names, ['octocat/Hello-World'] 21 | end 22 | 23 | def test_user_orgs_names 24 | stub_get 'user' 25 | stub_get 'user/orgs?per_page=100' 26 | 27 | instance = Furik::PullRequests.new(Furik.gh_client) 28 | assert_equal instance.user_orgs_names, ['github'] 29 | end 30 | 31 | def test_org_repo_names 32 | stub_get 'user' 33 | stub_get 'orgs/github/repos?per_page=100' 34 | 35 | instance = Furik::PullRequests.new(Furik.gh_client) 36 | assert_equal instance.org_repo_names('github'), ['octocat/Hello-World'] 37 | end 38 | 39 | def test_orgs_repo_names 40 | stub_get 'user' 41 | stub_get 'user/orgs?per_page=100' 42 | stub_get 'orgs/github/repos?per_page=100' 43 | 44 | instance = Furik::PullRequests.new(Furik.gh_client) 45 | assert_equal instance.orgs_repo_names, ['octocat/Hello-World'] 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__) 2 | require 'furik' 3 | 4 | require 'minitest/autorun' 5 | require 'webmock/minitest' 6 | 7 | module Furik::Configurable 8 | class << self 9 | def token_by_pit(host = 'github.com') 10 | 'x' * 40 11 | end 12 | end 13 | end 14 | 15 | ENV['GITHUB_ENTERPRISE_HOST'] = 'your.domain.com' 16 | 17 | def fixture_path 18 | File.expand_path('../fixtures', __FILE__) 19 | end 20 | 21 | def fixture(file) 22 | File.new(fixture_path + '/' + file) 23 | end 24 | 25 | def json_response(file) 26 | { 27 | status: 200, 28 | headers: { :content_type => 'application/json; charset=utf-8' }, 29 | body: fixture(file) 30 | } 31 | end 32 | 33 | def github_url(url) 34 | return url if url =~ /^http/ 35 | url = File.join(Octokit.api_endpoint, url) 36 | uri = Addressable::URI.parse(url) 37 | uri.path.gsub!("v3//", "v3/") 38 | uri.to_s 39 | end 40 | 41 | def stub_get(endpoint) 42 | stub_request(:get, github_url(endpoint)). 43 | to_return json_response("#{endpoint}.json") 44 | end 45 | --------------------------------------------------------------------------------