├── Rakefile ├── Gemfile ├── lib ├── githop │ ├── version.rb │ └── command.rb └── githop.rb ├── .rubocop.yml ├── .gitignore ├── bin └── githop ├── githop.gemspec ├── LICENSE ├── Gemfile.lock └── README.md /Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler/gem_tasks' 2 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gemspec 4 | -------------------------------------------------------------------------------- /lib/githop/version.rb: -------------------------------------------------------------------------------- 1 | module GitHop 2 | VERSION = '0.0.6' 3 | end 4 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | Metrics/AbcSize: 2 | Max: 35 3 | 4 | Metrics/LineLength: 5 | Max: 105 6 | 7 | Metrics/MethodLength: 8 | Max: 15 9 | 10 | Style/Documentation: 11 | Enabled: false 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /.yardoc 3 | /Gemfile.lock 4 | /_yardoc/ 5 | /coverage/ 6 | /doc/ 7 | /pkg/ 8 | /spec/reports/ 9 | /tmp/ 10 | *.bundle 11 | *.so 12 | *.o 13 | *.a 14 | mkmf.log 15 | 16 | bigquery-sample.marshal 17 | privatekey.p12 18 | -------------------------------------------------------------------------------- /bin/githop: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | if $PROGRAM_NAME == __FILE__ 4 | ENV['BUNDLE_GEMFILE'] = File.expand_path('../../Gemfile', __FILE__) 5 | require 'rubygems' 6 | require 'bundler/setup' 7 | $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__) 8 | end 9 | 10 | require 'githop' 11 | 12 | GitHop::Command.run(ARGV.first) 13 | -------------------------------------------------------------------------------- /lib/githop/command.rb: -------------------------------------------------------------------------------- 1 | require 'active_support/time' 2 | 3 | module GitHop 4 | class Command 5 | def self.run(other_user, date = 1.year.ago) 6 | config = YAML.load(File.read("#{ENV['HOME']}/.githop.yml")) 7 | result = GitHop.hop(config, other_user, date) 8 | result = GitHop.pretty_print(result, other_user) unless result['rows'].nil? 9 | 10 | if result.is_a?(Array) && result.count > 0 11 | puts result 12 | else 13 | puts "No events found, but #{other_user || 'you'} surely had an awesome day nevertheless :)" 14 | end 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /githop.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'githop/version' 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = 'githop' 8 | spec.version = GitHop::VERSION 9 | spec.authors = ['Boris Bügling'] 10 | spec.email = ['boris@buegling.com'] 11 | spec.summary = 'Uses BigQuery and GitHub Archive to create something like TimeHop for GitHub.' 12 | spec.homepage = 'https://github.com/neonichu/githop' 13 | spec.license = 'MIT' 14 | 15 | spec.files = `git ls-files -z`.split("\x0") 16 | spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } 17 | spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) 18 | spec.require_paths = ['lib'] 19 | 20 | spec.add_development_dependency 'bundler', '~> 1.7' 21 | spec.add_development_dependency 'rake', '~> 10.0' 22 | 23 | spec.add_dependency 'activesupport' 24 | spec.add_dependency 'bigquery' 25 | end 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Boris Bügling 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | 'Software'), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 18 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 19 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 20 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | githop (0.0.6) 5 | activesupport 6 | bigquery 7 | 8 | GEM 9 | remote: https://rubygems.org/ 10 | specs: 11 | activesupport (4.2.1) 12 | i18n (~> 0.7) 13 | json (~> 1.7, >= 1.7.7) 14 | minitest (~> 5.1) 15 | thread_safe (~> 0.3, >= 0.3.4) 16 | tzinfo (~> 1.1) 17 | addressable (2.3.8) 18 | autoparse (0.3.3) 19 | addressable (>= 2.3.1) 20 | extlib (>= 0.9.15) 21 | multi_json (>= 1.0.0) 22 | bigquery (0.7.0) 23 | google-api-client (~> 0.8.2) 24 | extlib (0.9.16) 25 | faraday (0.9.1) 26 | multipart-post (>= 1.2, < 3) 27 | google-api-client (0.8.6) 28 | activesupport (>= 3.2) 29 | addressable (~> 2.3) 30 | autoparse (~> 0.3) 31 | extlib (~> 0.9) 32 | faraday (~> 0.9) 33 | googleauth (~> 0.3) 34 | launchy (~> 2.4) 35 | multi_json (~> 1.10) 36 | retriable (~> 1.4) 37 | signet (~> 0.6) 38 | googleauth (0.4.1) 39 | faraday (~> 0.9) 40 | jwt (~> 1.4) 41 | logging (~> 2.0) 42 | memoist (~> 0.12) 43 | multi_json (= 1.11) 44 | signet (~> 0.6) 45 | i18n (0.7.0) 46 | json (1.8.2) 47 | jwt (1.5.0) 48 | launchy (2.4.3) 49 | addressable (~> 2.3) 50 | little-plugger (1.1.3) 51 | logging (2.0.0) 52 | little-plugger (~> 1.1) 53 | multi_json (~> 1.10) 54 | memoist (0.12.0) 55 | minitest (5.7.0) 56 | multi_json (1.11.0) 57 | multipart-post (2.0.0) 58 | rake (10.4.2) 59 | retriable (1.4.1) 60 | signet (0.6.0) 61 | addressable (~> 2.3) 62 | extlib (~> 0.9) 63 | faraday (~> 0.9) 64 | jwt (~> 1.0) 65 | multi_json (~> 1.10) 66 | thread_safe (0.3.5) 67 | tzinfo (1.2.2) 68 | thread_safe (~> 0.1) 69 | 70 | PLATFORMS 71 | ruby 72 | 73 | DEPENDENCIES 74 | bundler (~> 1.7) 75 | githop! 76 | rake (~> 10.0) 77 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # githop 🐙⏰ 2 | 3 | [![No Maintenance Intended](http://unmaintained.tech/badge.svg)](http://unmaintained.tech/) 4 | 5 | Uses [BigQuery][3] and [GitHub Archive][2] to create something like [TimeHop][4] for GitHub. It will 6 | show you the things you did exactly one year ago. 7 | 8 | ## Usage 9 | 10 | If you are orta and it is 24th of May, 2015, you might see something like this: 11 | 12 | ```bash 13 | $ githop 14 | At 2014-05-24 11:54:54, you forked the repo cocodelabs/NSAttributedString-CCLFormat 15 | At 2014-05-24 11:56:49, you created a PR on cocodelabs/NSAttributedString-CCLFormat 16 | At 2014-05-24 22:03:42, you created a PR on CocoaPods/cocoadocs.org 17 | At 2014-05-24 22:32:20, you created the tag 2.7.2 on orta/ARAnalytics 18 | ``` 19 | 20 | or specify a username to see another person's history: 21 | 22 | ```bash 23 | $ githop jpsim 24 | At 2014-05-24 10:15:34, jpsim watched the repo shahruz/Sketch-Toolbox 25 | At 2014-05-24 10:18:35, jpsim watched the repo heardrwt/RHObjectiveBeagle 26 | ``` 27 | 28 | For the real lazy, it is also available on the web: , specify 29 | the desired GitHub user as path. Code for the web version can be found [here][7]. 30 | 31 | ## Installation 32 | 33 | Use 34 | 35 | ```bash 36 | gem install githop 37 | ``` 38 | 39 | to install it and configure some settings in `~/.githop.yml`: 40 | 41 | ```yaml 42 | --- 43 | github_user: your GitHub username 44 | 45 | bigquery: { 46 | client_id: BigQuery OAuth Client-ID, 47 | service_email: BigQuery Service E-Mail, 48 | keyfile: Location of your BigQuery key (a .p12 file), 49 | project_id: ID of your BigQuery project 50 | } 51 | ``` 52 | 53 | Please follow [these steps][5] to set up a [BigQuery][3] OAuth application and take a look at 54 | [this][6] for manually verifying your Google Cloud account works. [BigQuery][3] is free for 3TB 55 | of queries and you don't have to set up any payments to use GitHop. 56 | 57 | ## Thanks 58 | 59 | Felix Krause for the [idea][1]. 60 | 61 | [1]: https://twitter.com/KrauseFx/status/602506804547977216 62 | [2]: https://www.githubarchive.org/ 63 | [3]: https://cloud.google.com/bigquery/what-is-bigquery 64 | [4]: http://timehop.com 65 | [5]: https://github.com/abronte/BigQuery#keys 66 | [6]: https://www.githubarchive.org/#bigquery 67 | [7]: https://gist.github.com/neonichu/465391e44617bd5975ce 68 | -------------------------------------------------------------------------------- /lib/githop.rb: -------------------------------------------------------------------------------- 1 | require 'githop/command' 2 | require 'githop/version' 3 | 4 | require 'active_support/time' 5 | require 'big_query' 6 | require 'json' 7 | require 'yaml' 8 | 9 | module GitHop 10 | private 11 | 12 | def self.build_query_2014(gh_user, end_date) 13 | month = end_date.strftime('%Y%m') 14 | end_date = end_date.strftime('%Y-%m-%d') 15 | 16 | query = < 'commented a commit on the repo', 67 | 'CreateEvent' => 'created the', 68 | 'DeleteEvent' => 'deleted the', 69 | 'ForkEvent' => 'forked the repo', 70 | 'IssueCommentEvent' => 'commented an issue on', 71 | 'IssuesEvent' => 'created an issue on', 72 | 'MemberEvent' => 'was added to the repo', 73 | 'PullRequestEvent' => 'created a PR on', 74 | 'PullRequestReviewCommentEvent' => 'commented on a PR on the repo', 75 | 'PushEvent' => 'pushed commits to', 76 | 'ReleaseEvent' => 'published a new release of', 77 | 'WatchEvent' => 'watched the repo' 78 | } 79 | end 80 | 81 | def self.pretty_print(result, user = nil) 82 | pushed_repos = [] 83 | 84 | result['rows'].map { |row| row['f'] }.map do |event| 85 | type, owner, repo, created_at = event[0]['v'], event[1]['v'], event[2]['v'], event[3]['v'] 86 | _, _, ref, ref_type = event[4]['v'], event[5]['v'], event[6]['v'], event[7]['v'] 87 | 88 | # Filter some not so interesting events 89 | next if %w(CommitCommentEvent IssueCommentEvent IssuesEvent MemberEvent PullRequestReviewCommentEvent WatchEvent).include?(type) 90 | 91 | action = labels[type] 92 | fail "Unsupported event type #{type}" if action.nil? 93 | target = "#{owner}/#{repo}" 94 | 95 | if type == 'CreateEvent' 96 | action += " #{ref_type}" 97 | action += " #{ref} on" if ref_type != 'repository' 98 | end 99 | 100 | if type == 'DeleteEvent' 101 | action += " #{ref_type} #{ref} on" 102 | end 103 | 104 | if type == 'PushEvent' 105 | next if pushed_repos.include?(target) 106 | pushed_repos << target 107 | end 108 | 109 | "At #{created_at}, #{user || 'you'} #{action} #{target}" 110 | end.compact 111 | end 112 | end 113 | --------------------------------------------------------------------------------