repofetch 0.4.2.pre.rc.3 → 0.4.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f023092a0a4175633900e4bd9da512c5287a781be907cf57a8971a1fdd7fda6c
4
- data.tar.gz: 6d1a8f7e801bf4f4e228249a650f5270dc38153bdfa9a7c32771dd02d01c098b
3
+ metadata.gz: 4fcfbdc976dd0c5345c0b46b617491569f862dfca0b770f7e8ba7a140580a799
4
+ data.tar.gz: 6367a3ec9d1604ca2f9ca3bcc1916a4550cc9c05044f580233f62a0583b57f9e
5
5
  SHA512:
6
- metadata.gz: 3fc54f3354a0f0b531eea8a183391ac839244c714546439f4e21d5d42e4d9233ffc17150149b8c48c29fa1adcb21b7c5d46a504855daffbf9e36c10ab8710d1a
7
- data.tar.gz: 29a97740e37ea30bd2f5017f42a0647c3cb944bd811e4d17831fb1d9fc55fe125c9db1d54aa4c9c4c14bef271ae850e2a6b0af067c78604006cc3aa5b939fd25
6
+ metadata.gz: c1cf7e66ceaa403c241adb2423882fcbc66e123f52f29311525bef0b13a98f45db205dc696374042cc1917e025b294e9256690f251ca1557c94360cf8d1a616b
7
+ data.tar.gz: fb091b4fc478ec7a9ea70b334cdf01b388602da811dda13c970d99d9aa03fa34a5fd68db0083a46401c1040da2c5012d31cf01a395d6b35b17741ad8cb470335
data/CONTRIBUTING.md CHANGED
@@ -13,6 +13,12 @@ bundle install
13
13
  bundle exec overcommit --install
14
14
  ```
15
15
 
16
+ ### Demo Animations
17
+
18
+ The demo animations are created using [vhs]. If you are going to create or
19
+ update the demo animations, please follow the installation instructions for
20
+ [vhs].
21
+
16
22
  ## Writing a 3rd-party plugin
17
23
 
18
24
  3rd-party plugins are Ruby gems that users can install and activate in their
@@ -131,3 +137,4 @@ MyCoolPlugin.register
131
137
  negative space, and uncolored text for positive space.
132
138
 
133
139
  [git-base]: https://www.rubydoc.info/github/ruby-git/ruby-git/Git/Base
140
+ [vhs]: https://github.com/charmbracelet/vhs
data/Gemfile.lock CHANGED
@@ -1,12 +1,13 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- repofetch (0.4.2.pre.rc.3)
4
+ repofetch (0.4.3)
5
5
  actionview (~> 7.0, >= 7.0.4)
6
6
  dotenv (~> 2.8)
7
7
  faraday-retry (~> 2.0)
8
8
  git (~> 1.12)
9
9
  octokit (~> 6.0, >= 6.0.1)
10
+ sawyer
10
11
 
11
12
  GEM
12
13
  remote: https://rubygems.org/
@@ -33,7 +34,7 @@ GEM
33
34
  diff-lcs (1.5.0)
34
35
  docile (1.4.0)
35
36
  dotenv (2.8.1)
36
- erubi (1.11.0)
37
+ erubi (1.12.0)
37
38
  faraday (2.7.2)
38
39
  faraday-net_http (>= 2.0, < 3.1)
39
40
  ruby2_keywords (>= 0.0.4)
@@ -46,27 +47,26 @@ GEM
46
47
  i18n (1.12.0)
47
48
  concurrent-ruby (~> 1.0)
48
49
  iniparse (1.5.0)
49
- json (2.6.2)
50
+ json (2.6.3)
50
51
  loofah (2.19.1)
51
52
  crass (~> 1.0.2)
52
53
  nokogiri (>= 1.5.9)
53
- mini_portile2 (2.8.0)
54
- minitest (5.16.3)
55
- nokogiri (1.13.10)
56
- mini_portile2 (~> 2.8.0)
54
+ minitest (5.17.0)
55
+ nokogiri (1.13.10-x86_64-linux)
57
56
  racc (~> 1.4)
58
57
  octokit (6.0.1)
59
58
  faraday (>= 1, < 3)
60
59
  sawyer (~> 0.9)
60
+ os (1.1.4)
61
61
  overcommit (0.59.1)
62
62
  childprocess (>= 0.6.3, < 5)
63
63
  iniparse (~> 1.4)
64
64
  rexml (~> 3.2)
65
65
  parallel (1.22.1)
66
- parser (3.1.3.0)
66
+ parser (3.2.0.0)
67
67
  ast (~> 2.4.1)
68
68
  public_suffix (5.0.1)
69
- racc (1.6.1)
69
+ racc (1.6.2)
70
70
  rails-dom-testing (2.0.3)
71
71
  activesupport (>= 4.2.0)
72
72
  nokogiri (>= 1.6)
@@ -83,38 +83,38 @@ GEM
83
83
  rspec-mocks (~> 3.12.0)
84
84
  rspec-core (3.12.0)
85
85
  rspec-support (~> 3.12.0)
86
- rspec-expectations (3.12.0)
86
+ rspec-expectations (3.12.2)
87
87
  diff-lcs (>= 1.2.0, < 2.0)
88
88
  rspec-support (~> 3.12.0)
89
- rspec-mocks (3.12.0)
89
+ rspec-mocks (3.12.2)
90
90
  diff-lcs (>= 1.2.0, < 2.0)
91
91
  rspec-support (~> 3.12.0)
92
92
  rspec-snapshot (2.0.1)
93
93
  awesome_print (> 1.0.0)
94
94
  rspec (> 3.0.0)
95
95
  rspec-support (3.12.0)
96
- rubocop (1.39.0)
96
+ rubocop (1.43.0)
97
97
  json (~> 2.3)
98
98
  parallel (~> 1.10)
99
- parser (>= 3.1.2.1)
99
+ parser (>= 3.2.0.0)
100
100
  rainbow (>= 2.2.2, < 4.0)
101
101
  regexp_parser (>= 1.8, < 3.0)
102
102
  rexml (>= 3.2.5, < 4.0)
103
- rubocop-ast (>= 1.23.0, < 2.0)
103
+ rubocop-ast (>= 1.24.1, < 2.0)
104
104
  ruby-progressbar (~> 1.7)
105
- unicode-display_width (>= 1.4.0, < 3.0)
106
- rubocop-ast (1.24.0)
105
+ unicode-display_width (>= 2.4.0, < 3.0)
106
+ rubocop-ast (1.24.1)
107
107
  parser (>= 3.1.1.0)
108
108
  rubocop-rake (0.6.0)
109
109
  rubocop (~> 1.0)
110
- rubocop-rspec (2.15.0)
110
+ rubocop-rspec (2.16.0)
111
111
  rubocop (~> 1.33)
112
112
  ruby-progressbar (1.11.0)
113
113
  ruby2_keywords (0.0.5)
114
114
  sawyer (0.9.2)
115
115
  addressable (>= 2.3.5)
116
116
  faraday (>= 0.17.3, < 3)
117
- simplecov (0.21.2)
117
+ simplecov (0.22.0)
118
118
  docile (~> 1.1)
119
119
  simplecov-html (~> 0.11)
120
120
  simplecov_json_formatter (~> 0.1)
@@ -125,16 +125,17 @@ GEM
125
125
  simplecov_json_formatter (0.1.4)
126
126
  tzinfo (2.0.5)
127
127
  concurrent-ruby (~> 1.0)
128
- unicode-display_width (2.3.0)
128
+ unicode-display_width (2.4.2)
129
129
  webrick (1.7.0)
130
130
  yard (0.9.28)
131
131
  webrick (~> 1.7.0)
132
132
 
133
133
  PLATFORMS
134
- ruby
134
+ x86_64-linux
135
135
 
136
136
  DEPENDENCIES
137
137
  bundler (~> 2.0)
138
+ os (~> 1.1)
138
139
  overcommit (~> 0.59)
139
140
  rake (~> 13.0)
140
141
  repofetch!
@@ -148,4 +149,4 @@ DEPENDENCIES
148
149
  yard (~> 0.9.28)
149
150
 
150
151
  BUNDLED WITH
151
- 2.1.2
152
+ 2.4.3
data/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  [![Gem Version](https://badge.fury.io/rb/repofetch.svg)](https://badge.fury.io/rb/repofetch)
4
4
  [![GitHub contributors (via allcontributors.org)](https://img.shields.io/github/all-contributors/spenserblack/repofetch)](./CREDITS.md)
5
5
  ![CI](https://github.com/spenserblack/repofetch/workflows/CI/badge.svg)
6
- [![CodeQL](https://github.com/spenserblack/repofetch/actions/workflows/codeql.yml/badge.svg)](https://github.com/spenserblack/repofetch/actions/workflows/codeql.yml)
6
+ [![CodeQL](https://github.com/spenserblack/repofetch/actions/workflows/github-code-scanning/codeql/badge.svg)](https://github.com/spenserblack/repofetch/actions/workflows/github-code-scanning/codeql)
7
7
  [![codecov](https://codecov.io/gh/spenserblack/repofetch/branch/master/graph/badge.svg?token=3572AEWQAY)](https://codecov.io/gh/spenserblack/repofetch)
8
8
 
9
9
  Fetch details about your remote repository.
@@ -12,7 +12,7 @@ Fetch details about your remote repository.
12
12
 
13
13
  ![basic demo](./demos/demo.gif)
14
14
 
15
- ![advanced plugin usage](./demos/github-plugin.gif)
15
+ ![advanced plugin usage](./demos/gitlab-plugin.gif)
16
16
 
17
17
  ## Description
18
18
 
@@ -20,9 +20,15 @@ repofetch is a CLI tool to fetch stats (think [neofetch] or
20
20
  [onefetch]) that uses plugins for its implementation. The original version was focused on
21
21
  repository stats, and any official plugin will be for repositories, hence the "repo" in
22
22
  repofetch. With 3rd-party plugins, however, it can support other types of outputs, too.
23
+ This tool may be renamed in the future.
24
+
25
+ Follow [this discussion](https://github.com/spenserblack/repofetch/discussions/219)
26
+ for details about the potential rename.
23
27
 
24
28
  ## Installation
25
29
 
30
+ [![Packaging status](https://repology.org/badge/vertical-allrepos/ruby:repofetch.svg)](https://repology.org/project/ruby:repofetch/versions)
31
+
26
32
  ### Via RubyGems.org
27
33
 
28
34
  ```bash
@@ -33,10 +39,24 @@ gem install repofetch
33
39
 
34
40
  If you are using an Arch machine, you can install repofetch from the [Aur](https://aur.archlinux.org).
35
41
 
42
+ #### Stable
43
+
44
+ ```
45
+ yay -S ruby-repofetch-bin
46
+ ```
47
+
48
+ #### Development
49
+
36
50
  ```
37
51
  yay -S ruby-repofetch
38
52
  ```
39
53
 
54
+ ### Via NetBSD
55
+
56
+ ```bash
57
+ pkgin install ruby-repofetch
58
+ ```
59
+
40
60
  ### Installing Version `< 0.4.0`
41
61
 
42
62
  Version 0.3.3 and lower was a different implementation written in Rust. While `>= 0.4.0` is unstable
data/RELEASE_NOTES CHANGED
@@ -1,6 +1,30 @@
1
- 0.4.2-rc.3
1
+ 0.4.3
2
2
 
3
- ## Other
3
+ ## Added
4
4
 
5
- - Deploy built gem as GitHub release asset for each release
6
- - Publish gem with GitHub Actions
5
+ - Bitbucket Cloud plugin
6
+ - Simple help message for the GitLab plugin (`repofetch --plugin Repofetch::Gitlab -- --help`)
7
+ - `--path` option, which is an alias for `--repository`
8
+ - SSH url stat to GitHub and GitLab plugins
9
+
10
+ ## Changed
11
+
12
+ - "URL" stat in GitHub and GitLab plugins to "HTTP(S)"
13
+ - GitLab header to be styled
14
+
15
+ ## Fixed
16
+
17
+ - Confusing error on incorrect CLI args for the GitLab plugin
18
+
19
+ ## Breaking for users
20
+
21
+ ### Changed
22
+
23
+ - The `--plugin` short option to `-P`
24
+ - `-p` to be the short option for `--path`
25
+
26
+ ## Breaking for 3rd-party plugins
27
+
28
+ ### Changed
29
+
30
+ - The exception that is caught when initializing a plugin from `ArgumentError` to `Repofetch::PluginUsageError`
data/Rakefile CHANGED
@@ -3,11 +3,18 @@
3
3
  require 'bundler/setup'
4
4
  require 'rake'
5
5
 
6
+ require 'os'
6
7
  require 'rspec/core/rake_task'
7
8
  require 'rubocop/rake_task'
8
9
  require 'yard'
9
10
 
10
- RSpec::Core::RakeTask.new(:spec)
11
+ RSpec::Core::RakeTask.new(:spec) do |t|
12
+ t.rspec_opts = if OS.windows?
13
+ '--exclude-pattern spec/**/*_unix_spec.rb'
14
+ else
15
+ '--exclude-pattern spec/**/*_windows_spec.rb'
16
+ end
17
+ end
11
18
  RuboCop::RakeTask.new(:format) do |t|
12
19
  t.requires << 'rubocop-rspec'
13
20
  end
@@ -1,5 +1,7 @@
1
1
  ---
2
2
  # vim: set ft=yaml:
3
3
  plugins:
4
+ - 'repofetch/bitbucketcloud'
4
5
  - 'repofetch/github'
6
+ - 'repofetch/gitlab'
5
7
  emojis: true
@@ -0,0 +1,17 @@
1
+ %{blue}@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
2
+ %{blue}@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
3
+ %{blue} @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
4
+ %{blue} @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
5
+ %{blue} @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
6
+ %{blue} @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
7
+ %{blue} @@@@@@@@@ .........
8
+ %{blue} @@@@@@@@@ .........
9
+ %{blue} @@@@@@@@@ :::::::::
10
+ %{blue} @@@@@@@@@ :::::::::
11
+ %{blue} @@@@@@@@@ !!!!!!!!!
12
+ %{blue} @@@@@@@@@ !!!!!!!!!
13
+ %{blue} @@@@@@@@@@@@@@@@@@@@@@@@@@@
14
+ %{blue} @@@@@@@@@@@@@@@@@@@@@@@@@@@
15
+ %{blue} @@@@@@@@@@@@@@@@@@@@@@@@@
16
+ %{blue} @@@@@@@@@@@@@@@@@@@@@@@@@
17
+ %{blue} @@@@@@@@@@@@@@@@@@@@@@@
@@ -0,0 +1,125 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'action_view'
4
+ require 'optparse'
5
+ require 'repofetch'
6
+ require 'repofetch/exceptions'
7
+ require 'sawyer'
8
+
9
+ class Repofetch
10
+ # Adds support for Bitbucket repositories.
11
+ class BitbucketCloud < Repofetch::Plugin
12
+ include ActionView::Helpers::NumberHelper
13
+
14
+ ASCII = File.read(File.expand_path('bitbucketcloud/ASCII', __dir__))
15
+
16
+ attr_reader :repo_identifier
17
+
18
+ def initialize(repo_identifier)
19
+ super
20
+
21
+ @repo_identifier = repo_identifier
22
+ end
23
+
24
+ def header
25
+ "#{repo_data['owner']['display_name']}/#{repo_data['name']} @ Bitbucket"
26
+ end
27
+
28
+ def stats
29
+ stats = [http_clone_url, ssh_clone_url, watchers, forks, created, updated, size, issues, pull_requests]
30
+
31
+ stats.each { |stat| %i[bold blue].each { |style| stat.style_label!(style) } }
32
+ end
33
+
34
+ def ascii
35
+ ASCII
36
+ end
37
+
38
+ def agent
39
+ @agent ||= Sawyer::Agent.new('https://api.bitbucket.org/2.0') do |http|
40
+ http.headers['Authorization'] = "Bearer #{token}" unless token.nil?
41
+ end
42
+ end
43
+
44
+ def token
45
+ ENV.fetch('BITBUCKET_TOKEN', nil)
46
+ end
47
+
48
+ def self.matches_repo?(*)
49
+ false
50
+ end
51
+
52
+ def self.from_git(*)
53
+ new
54
+ end
55
+
56
+ def self.from_args(args)
57
+ parser = OptionParser.new do |opts|
58
+ opts.banner = 'Usage: <plugin activation> -- [options] OWNER/PROJECT'
59
+ opts.separator ''
60
+ opts.separator 'This plugin can use the BITBUCKET_TOKEN environment variable'
61
+ end
62
+ parser.parse(args)
63
+
64
+ raise Repofetch::PluginUsageError, parser.to_s unless args.length == 1
65
+
66
+ new(args[0])
67
+ end
68
+
69
+ protected
70
+
71
+ def repo_data
72
+ @repo_data ||= agent.call(:get, "repositories/#{@repo_identifier}").data
73
+ end
74
+
75
+ def clone_urls
76
+ @clone_urls ||= repo_data['links']['clone'].to_h { |clone| [clone['name'].to_sym, clone['href']] }
77
+ end
78
+
79
+ def http_clone_url
80
+ Repofetch::Stat.new('HTTP(S)', clone_urls[:https], emoji: '🌐')
81
+ end
82
+
83
+ def ssh_clone_url
84
+ Repofetch::Stat.new('SSH', clone_urls[:ssh], emoji: '🔑')
85
+ end
86
+
87
+ def watchers
88
+ @watcher_data ||= agent.call(:get, "repositories/#{@repo_identifier}/watchers").data
89
+ Repofetch::Stat.new('subscribers', @watcher_data['size'], emoji: '👀')
90
+ end
91
+
92
+ def forks
93
+ @fork_data ||= agent.call(:get, "repositories/#{@repo_identifier}/forks").data
94
+ Repofetch::Stat.new('forks', @fork_data['size'], emoji: '🔱')
95
+ end
96
+
97
+ def created
98
+ Repofetch::TimespanStat.new('created', repo_data['created_on'], emoji: '🐣')
99
+ end
100
+
101
+ def updated
102
+ Repofetch::TimespanStat.new('updated', repo_data['updated_on'], emoji: '📤')
103
+ end
104
+
105
+ def size
106
+ # NOTE: Size is in bytes
107
+ # TODO: Move this somewhere else instead of using a copy-paste
108
+ byte_size = number_to_human_size(repo_data['size'] || 0, precision: 2, significant: false,
109
+ strip_insignificant_zeros: false)
110
+ Repofetch::Stat.new('size', byte_size, emoji: '💽')
111
+ end
112
+
113
+ def issues
114
+ @issue_data ||= agent.call(:get, "repositories/#{@repo_identifier}/issues").data
115
+ Repofetch::Stat.new('issues', @issue_data['size'], emoji: '❗')
116
+ end
117
+
118
+ def pull_requests
119
+ @pull_request_data ||= agent.call(:get, "repositories/#{@repo_identifier}/pullrequests").data
120
+ Repofetch::Stat.new('pull requests', @pull_request_data['size'], emoji: '🔀')
121
+ end
122
+ end
123
+ end
124
+
125
+ Repofetch::BitbucketCloud.register
data/lib/repofetch/cli.rb CHANGED
@@ -4,6 +4,7 @@ require 'git'
4
4
  require 'optparse'
5
5
  require 'repofetch'
6
6
  require 'repofetch/config'
7
+ require 'repofetch/exceptions'
7
8
 
8
9
  class Repofetch
9
10
  # Command line interface for repofetch.
@@ -29,7 +30,7 @@ class Repofetch
29
30
 
30
31
  begin
31
32
  plugin = new_plugin
32
- rescue ArgumentError => e
33
+ rescue Repofetch::PluginUsageError => e
33
34
  warn e
34
35
  return 1
35
36
  end
@@ -62,13 +63,14 @@ class Repofetch
62
63
  private
63
64
 
64
65
  def add_repository_options(opts)
65
- opts.on('-r', '--repository PATH', 'Use the provided repository. Defaults to the current directory.') do |path|
66
+ opts.on('-r', '--repository', '-p', '--path PATH',
67
+ 'Use the provided path. Defaults to the current directory.') do |path|
66
68
  @repository_path = path
67
69
  end
68
70
  end
69
71
 
70
72
  def add_plugin_options(opts)
71
- opts.on('-p', '--plugin PLUGIN', 'Use the specified plugin.') do |plugin|
73
+ opts.on('-P', '--plugin PLUGIN', 'Use the specified plugin.') do |plugin|
72
74
  @plugin = available_plugins[plugin]
73
75
  end
74
76
 
@@ -1,13 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Raised when there aren't any available plugins.
4
3
  class Repofetch
5
4
  class Error < RuntimeError
6
5
  end
7
6
 
7
+ # Raised when there aren't any available plugins.
8
8
  class NoPluginsError < Error
9
9
  end
10
10
 
11
+ # Raised when more than one plugin is activated.
11
12
  class TooManyPluginsError < Error
12
13
  end
14
+
15
+ # Raised when a user incorrectly uses the CLI with a plugin.
16
+ class PluginUsageError < Error
17
+ end
13
18
  end
@@ -4,14 +4,15 @@ require 'action_view'
4
4
  require 'octokit'
5
5
  require 'optparse'
6
6
  require 'repofetch'
7
+ require 'repofetch/exceptions'
7
8
 
8
9
  class Repofetch
9
10
  # Adds support for GitHub repositories.
10
11
  class Github < Repofetch::Plugin
11
12
  include ActionView::Helpers::NumberHelper
12
13
 
13
- HTTP_REMOTE_REGEX = %r{https?://github\.com/(?<owner>[\w.\-]+)/(?<repository>[\w.\-]+)}.freeze
14
- SSH_REMOTE_REGEX = %r{git@github\.com:(?<owner>[\w.\-]+)/(?<repository>[\w.\-]+)}.freeze
14
+ HTTP_REMOTE_REGEX = %r{https?://github\.com/(?<owner>[\w.-]+)/(?<repository>[\w.-]+)}.freeze
15
+ SSH_REMOTE_REGEX = %r{git@github\.com:(?<owner>[\w.-]+)/(?<repository>[\w.-]+)}.freeze
15
16
  ASCII = File.read(File.expand_path('github/ASCII', __dir__))
16
17
 
17
18
  attr_reader :owner, :repository
@@ -30,14 +31,15 @@ class Repofetch
30
31
  end
31
32
 
32
33
  def stats
33
- [url, stargazers, subscribers, forks, created, updated, size, issues, pull_requests]
34
+ stats = [http_clone_url, ssh_clone_url, stargazers, subscribers, forks, created, updated, size, issues,
35
+ pull_requests]
36
+ stats.each { |stat| stat.style_label!(:bold) }
34
37
  end
35
38
 
36
39
  # Detects that the repository is a GitHub repository.
37
40
  def self.matches_repo?(git)
38
41
  default_remote = Repofetch.default_remote(git)
39
- url = default_remote&.url
40
- matches_remote?(url)
42
+ matches_remote?(default_remote&.url)
41
43
  end
42
44
 
43
45
  # Detects that the remote URL is for a GitHub repository.
@@ -48,8 +50,7 @@ class Repofetch
48
50
  # Gets the owner and repository from a GitHub local repository.
49
51
  def self.repo_identifiers(git)
50
52
  default_remote = Repofetch.default_remote(git)
51
- url = default_remote&.url
52
- remote_identifiers(url)
53
+ remote_identifiers(default_remote&.url)
53
54
  end
54
55
 
55
56
  # Gets the owner and repository from a GitHub remote URL.
@@ -65,10 +66,9 @@ class Repofetch
65
66
 
66
67
  # Creates an instance from a +Git::Base+ instance.
67
68
  #
68
- # @raise [ArgumentError] if this plugin was selected *and* arguments were passed.
69
+ # @raise [Repofetch::PluginUsageError] if this plugin was selected *and* arguments were passed.
69
70
  def self.from_git(git, args)
70
- # TODO: Raise a better exception than ArgumentError
71
- raise ArgumentError, 'Explicitly activate this plugin to CLI arguments' unless args.empty?
71
+ raise Repofetch::PluginUsageError, 'Explicitly activate this plugin to CLI arguments' unless args.empty?
72
72
 
73
73
  owner, repository = repo_identifiers(git)
74
74
 
@@ -77,7 +77,7 @@ class Repofetch
77
77
 
78
78
  # Creates an instance from CLI args and configuration.
79
79
  #
80
- # @raise [ArgumentError] if +args+ couldn't be parsed.
80
+ # @raise [Repofetch::PluginUsageError] if +args+ couldn't be parsed.
81
81
  def self.from_args(args)
82
82
  parser = OptionParser.new do |opts|
83
83
  opts.banner = 'Usage: <plugin activation> -- [options] OWNER/REPOSITORY'
@@ -87,8 +87,7 @@ class Repofetch
87
87
  parser.parse(args)
88
88
  split = args[0]&.split('/')
89
89
 
90
- # TODO: Raise a better exception than ArgumentError
91
- raise ArgumentError, parser.to_s unless split&.length == 2
90
+ raise Repofetch::PluginUsageError, parser.to_s unless split&.length == 2
92
91
 
93
92
  new(*split)
94
93
  end
@@ -108,48 +107,48 @@ class Repofetch
108
107
  @repo_stats
109
108
  end
110
109
 
111
- def url
112
- Repofetch::Stat.new('URL', repo_stats['clone_url'], emoji: '🌐', theme: theme)
110
+ def http_clone_url
111
+ Repofetch::Stat.new('HTTP(S)', repo_stats['clone_url'], emoji: '🌐')
112
+ end
113
+
114
+ def ssh_clone_url
115
+ Repofetch::Stat.new('SSH', repo_stats['ssh_url'], emoji: '🔑')
113
116
  end
114
117
 
115
118
  def stargazers
116
- Repofetch::Stat.new('stargazers', repo_stats['stargazers_count'], emoji: '⭐', theme: theme)
119
+ Repofetch::Stat.new('stargazers', repo_stats['stargazers_count'], emoji: '⭐')
117
120
  end
118
121
 
119
122
  def subscribers
120
- Repofetch::Stat.new('subscribers', repo_stats['subscribers_count'], emoji: '👀', theme: theme)
123
+ Repofetch::Stat.new('subscribers', repo_stats['subscribers_count'], emoji: '👀')
121
124
  end
122
125
 
123
126
  def forks
124
- Repofetch::Stat.new('forks', repo_stats['forks_count'], emoji: '🔱', theme: theme)
127
+ Repofetch::Stat.new('forks', repo_stats['forks_count'], emoji: '🔱')
125
128
  end
126
129
 
127
130
  def created
128
- Repofetch::TimespanStat.new('created', repo_stats['created_at'], emoji: '🐣', theme: theme)
131
+ Repofetch::TimespanStat.new('created', repo_stats['created_at'], emoji: '🐣')
129
132
  end
130
133
 
131
134
  def updated
132
- Repofetch::TimespanStat.new('updated', repo_stats['updated_at'], emoji: '📤', theme: theme)
135
+ Repofetch::TimespanStat.new('updated', repo_stats['updated_at'], emoji: '📤')
133
136
  end
134
137
 
135
138
  def size
136
- byte_size = number_to_human_size(
137
- (repo_stats['size'] || 0) * 1024,
138
- precision: 2,
139
- significant: false,
140
- strip_insignificant_zeros: false
141
- )
142
- Repofetch::Stat.new('size', byte_size, emoji: '💽', theme: theme)
139
+ byte_size = number_to_human_size((repo_stats['size'] || 0) * 1024, precision: 2, significant: false,
140
+ strip_insignificant_zeros: false)
141
+ Repofetch::Stat.new('size', byte_size, emoji: '💽')
143
142
  end
144
143
 
145
144
  def issues
146
145
  @issue_search = @client.search_issues("repo:#{repo_id} is:issue", per_page: 1, page: 0) if @issue_search.nil?
147
- Repofetch::Stat.new('issues', @issue_search['total_count'], emoji: '❗', theme: theme)
146
+ Repofetch::Stat.new('issues', @issue_search['total_count'], emoji: '❗')
148
147
  end
149
148
 
150
149
  def pull_requests
151
150
  @pr_search = @client.search_issues("repo:#{repo_id} is:pr", per_page: 1, page: 0) if @pr_search.nil?
152
- Repofetch::Stat.new('pull requests', @pr_search['total_count'], emoji: '🔀', theme: theme)
151
+ Repofetch::Stat.new('pull requests', @pr_search['total_count'], emoji: '🔀')
153
152
  end
154
153
  end
155
154
  end
@@ -0,0 +1,17 @@
1
+ %{red} OOO OOO
2
+ %{red} OOOOOO OOOOOO
3
+ %{red} OOOOOOOO OOOOOOOO
4
+ %{red} OOOOOOOOOO OOOOOOOOOO
5
+ %{red} OOOOOOOOOOOO OOOOOOOOOOOO
6
+ %{red} OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
7
+ %{red} OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
8
+ %{yellow} ...%{red}OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO%{yellow}...
9
+ %{yellow} ......%{red}OOOOOOOOOOOOOOOOOOOOOOOOOO%{yellow}......
10
+ %{yellow} ........%{red}OOOOOOOOOOOOOOOOOOOO%{yellow}........
11
+ %{yellow} ..........%{red}OOOOOOOOOOOOOO%{yellow}..........
12
+ %{yellow} ............%{red}OOOOOOOO%{yellow}............
13
+ %{yellow} .............%{red}OO%{yellow}..............
14
+ %{yellow} .......%{yellow}@@@@@@@@%{yellow}........
15
+ %{yellow} ..%{yellow}@@@@@@@@@@@@@@@%{yellow}..
16
+ %{yellow} @@@@@@@@@@@@@
17
+ %{yellow} @@@@@@@
@@ -0,0 +1,144 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cgi'
4
+ require 'repofetch'
5
+ require 'repofetch/exceptions'
6
+ require 'sawyer'
7
+
8
+ class Repofetch
9
+ # Adds support for GitLab repositories.
10
+ class Gitlab < Repofetch::Plugin
11
+ HTTP_REMOTE_REGEX = %r{https?://gitlab\.com/(?<path>[\w.-][\w.\-/]+)}.freeze
12
+ SSH_REMOTE_REGEX = %r{git@gitlab\.com:(?<path>[\w.-][\w.\-/]+)}.freeze
13
+ ASCII = File.read(File.expand_path('gitlab/ASCII', __dir__))
14
+
15
+ attr_reader :repo_identifier
16
+
17
+ # @param repo_identifier [String] The repository identifier (either the ID number or the namespaced repo name).
18
+ def initialize(repo_identifier)
19
+ super
20
+
21
+ @repo_identifier = CGI.escape(repo_identifier)
22
+ end
23
+
24
+ def header
25
+ "#{header_format(repo_data['name_with_namespace'])} @ #{header_format('GitLab')}"
26
+ end
27
+
28
+ def header_format(text)
29
+ theme.format(:bold, theme.format(:red, text))
30
+ end
31
+
32
+ def stats
33
+ stats = [http_clone_url, ssh_clone_url, stars, forks, created, updated]
34
+
35
+ # NOTE: Stats that require authentication
36
+ stats << open_issues unless token.nil?
37
+
38
+ stats.each { |stat| %i[bold red].each { |style| stat.style_label!(style) } }
39
+ end
40
+
41
+ def ascii
42
+ ASCII
43
+ end
44
+
45
+ def agent
46
+ @agent ||= Sawyer::Agent.new('https://gitlab.com/api/v4', links_parser: Sawyer::LinkParsers::Simple.new) do |http|
47
+ http.headers['Authorization'] = "Bearer #{token}" unless token.nil?
48
+ end
49
+ end
50
+
51
+ def token
52
+ ENV.fetch('GITLAB_TOKEN', nil)
53
+ end
54
+
55
+ def repo_data
56
+ @repo_data ||= agent.call(:get, "projects/#{@repo_identifier}").data
57
+ end
58
+
59
+ def http_clone_url
60
+ Repofetch::Stat.new('HTTP(S)', repo_data['http_url_to_repo'], emoji: '🌐')
61
+ end
62
+
63
+ def ssh_clone_url
64
+ Repofetch::Stat.new('SSH', repo_data['ssh_url_to_repo'], emoji: '🔑')
65
+ end
66
+
67
+ def stars
68
+ Repofetch::Stat.new('stars', repo_data['star_count'], emoji: '⭐')
69
+ end
70
+
71
+ def forks
72
+ Repofetch::Stat.new('forks', repo_data['forks_count'], emoji: '🔱')
73
+ end
74
+
75
+ def created
76
+ Repofetch::TimespanStat.new('created', repo_data['created_at'], emoji: '🐣')
77
+ end
78
+
79
+ def updated
80
+ Repofetch::TimespanStat.new('updated', repo_data['last_activity_at'], emoji: '📤')
81
+ end
82
+
83
+ def open_issues
84
+ # NOTE: It seems like the auth token must be set to get the open issues count.
85
+ Repofetch::Stat.new('open issues', repo_data['open_issues_count'], emoji: '❗')
86
+ end
87
+
88
+ # Gets the path (+owner/subproject/repo+) of the repository.
89
+ def self.repo_identifier(git)
90
+ default_remote = Repofetch.default_remote(git)
91
+ url = default_remote&.url
92
+ remote_identifier(url)
93
+ end
94
+
95
+ # Gets the path (+owner/subproject/repo+) of the repository.
96
+ #
97
+ # Returns nil if there is no match.
98
+ def self.remote_identifier(remote)
99
+ match = HTTP_REMOTE_REGEX.match(remote)
100
+ match = SSH_REMOTE_REGEX.match(remote) if match.nil?
101
+ raise "Remote #{remote.inspect} doesn't look like a GitLab remote" if match.nil?
102
+
103
+ match[:path].delete_suffix('.git')
104
+ end
105
+
106
+ # Detects that the repository is a GitHub repository.
107
+ def self.matches_repo?(git)
108
+ default_remote = Repofetch.default_remote(git)
109
+ url = default_remote&.url
110
+ matches_remote?(url)
111
+ end
112
+
113
+ # Detects that the remote URL is for a GitHub repository.
114
+ def self.matches_remote?(remote)
115
+ HTTP_REMOTE_REGEX.match?(remote) || SSH_REMOTE_REGEX.match?(remote)
116
+ end
117
+
118
+ # Creates an instance from a +Git::Base+ instance.
119
+ #
120
+ # @raise [Repofetch::PluginUsageError] if this plugin was selected *and* arguments were passed.
121
+ def self.from_git(git, args)
122
+ raise Repofetch::PluginUsageError, 'Explicitly activate this plugin to CLI arguments' unless args.empty?
123
+
124
+ path = repo_identifier(git)
125
+
126
+ new(path)
127
+ end
128
+
129
+ def self.from_args(args)
130
+ parser = OptionParser.new do |opts|
131
+ opts.banner = 'Usage: <plugin activation> -- [options] OWNER/PROJECT/SUBPROJECT'
132
+ opts.separator ''
133
+ opts.separator 'This plugin can use the GITLAB_TOKEN environment variable to fetch more data'
134
+ end
135
+ parser.parse(args)
136
+
137
+ raise Repofetch::PluginUsageError, parser.to_s unless args.length == 1
138
+
139
+ new(args[0])
140
+ end
141
+ end
142
+ end
143
+
144
+ Repofetch::Gitlab.register
@@ -28,6 +28,9 @@ class Repofetch
28
28
 
29
29
  attr_reader :styles
30
30
 
31
+ # Initializes a new theme.
32
+ #
33
+ # @param [Hash] styles A Hash of styles to use. Is merged with the default styles.
31
34
  def initialize(styles = {})
32
35
  @styles = DEFAULT_STYLES.merge(styles)
33
36
  end
data/lib/repofetch.rb CHANGED
@@ -193,9 +193,20 @@ class Repofetch
193
193
  []
194
194
  end
195
195
 
196
+ # Adds +theme+ to the stats, mutating those stats.
197
+ #
198
+ # @return [Array<Stat>]
199
+ def theme_stats!
200
+ stats.each do |stat|
201
+ stat.theme = theme if stat.respond_to?(:theme=)
202
+ end
203
+ end
204
+
196
205
  # Makes an array of stat lines, including the header and separator.
197
- def stat_lines
198
- [header, separator, *stats.map(&:to_s)]
206
+ #
207
+ # Mutates +stats+ to add the +theme+.
208
+ def stat_lines!
209
+ [header, separator, *theme_stats!.map(&:to_s)]
199
210
  end
200
211
 
201
212
  # Zips ASCII lines with stat lines.
@@ -203,6 +214,7 @@ class Repofetch
203
214
  # If there are more of one than the other, than the zip will be padded with empty strings.
204
215
  def zipped_lines
205
216
  ascii_lines = ascii.lines.map(&:chomp)
217
+ stat_lines = stat_lines!
206
218
  if ascii_lines.length > stat_lines.length
207
219
  ascii_lines.zip(stat_lines)
208
220
  else
@@ -214,23 +226,40 @@ class Repofetch
214
226
  # Base class for stats.
215
227
  class Stat
216
228
  attr_reader :label, :value, :emoji
229
+ attr_writer :theme
217
230
 
218
231
  # Creates a stat
219
232
  #
220
233
  # @param [String] label The label of the stat
221
234
  # @param value The value of the stat
222
235
  # @param [String] emoji An optional emoji for the stat
223
- def initialize(label, value, emoji: nil, theme: nil)
236
+ def initialize(label, value, emoji: nil)
224
237
  @label = label
225
238
  @value = value
226
239
  @emoji = emoji
227
- @theme = theme
240
+ @label_styles = []
228
241
  end
229
242
 
230
243
  def to_s
231
244
  emoji = @emoji
232
245
  emoji = nil unless Repofetch.config.nil? || Repofetch.config.emojis?
233
- "#{emoji}#{@theme.nil? ? @label : @theme.format(:bold, @label)}: #{format_value}"
246
+ "#{emoji}#{format_label}: #{format_value}"
247
+ end
248
+
249
+ # Adds a style for the label
250
+ #
251
+ # @param [Symbol] style The theme's style to add
252
+ def style_label!(style)
253
+ @label_styles << style
254
+ end
255
+
256
+ # Formats the label, including styles.
257
+ #
258
+ # @return [String]
259
+ def format_label
260
+ return @label if @theme.nil?
261
+
262
+ @label_styles.inject(@label) { |label, style| @theme.format(style, label) }
234
263
  end
235
264
 
236
265
  # Formats the value
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: repofetch
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.2.pre.rc.3
4
+ version: 0.4.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Spenser Black
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-12-16 00:00:00.000000000 Z
11
+ date: 2023-01-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: actionview
@@ -92,6 +92,20 @@ dependencies:
92
92
  - - ">="
93
93
  - !ruby/object:Gem::Version
94
94
  version: 6.0.1
95
+ - !ruby/object:Gem::Dependency
96
+ name: sawyer
97
+ requirement: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :runtime
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
95
109
  - !ruby/object:Gem::Dependency
96
110
  name: bundler
97
111
  requirement: !ruby/object:Gem::Requirement
@@ -106,6 +120,20 @@ dependencies:
106
120
  - - "~>"
107
121
  - !ruby/object:Gem::Version
108
122
  version: '2.0'
123
+ - !ruby/object:Gem::Dependency
124
+ name: os
125
+ requirement: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - "~>"
128
+ - !ruby/object:Gem::Version
129
+ version: '1.1'
130
+ type: :development
131
+ prerelease: false
132
+ version_requirements: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - "~>"
135
+ - !ruby/object:Gem::Version
136
+ version: '1.1'
109
137
  - !ruby/object:Gem::Dependency
110
138
  name: overcommit
111
139
  requirement: !ruby/object:Gem::Requirement
@@ -268,12 +296,16 @@ files:
268
296
  - exe/repofetch
269
297
  - lib/repofetch.rb
270
298
  - lib/repofetch/DEFAULT_CONFIG
299
+ - lib/repofetch/bitbucketcloud.rb
300
+ - lib/repofetch/bitbucketcloud/ASCII
271
301
  - lib/repofetch/cli.rb
272
302
  - lib/repofetch/config.rb
273
303
  - lib/repofetch/env.rb
274
304
  - lib/repofetch/exceptions.rb
275
305
  - lib/repofetch/github.rb
276
306
  - lib/repofetch/github/ASCII
307
+ - lib/repofetch/gitlab.rb
308
+ - lib/repofetch/gitlab/ASCII
277
309
  - lib/repofetch/theme.rb
278
310
  - lib/repofetch/util.rb
279
311
  homepage: https://github.com/spenserblack/repofetch
@@ -297,9 +329,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
297
329
  version: 2.7.0
298
330
  required_rubygems_version: !ruby/object:Gem::Requirement
299
331
  requirements:
300
- - - ">"
332
+ - - ">="
301
333
  - !ruby/object:Gem::Version
302
- version: 1.3.1
334
+ version: '0'
303
335
  requirements: []
304
336
  rubygems_version: 3.1.6
305
337
  signing_key: