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 +4 -4
- data/CONTRIBUTING.md +7 -0
- data/Gemfile.lock +22 -21
- data/README.md +22 -2
- data/RELEASE_NOTES +28 -4
- data/Rakefile +8 -1
- data/lib/repofetch/DEFAULT_CONFIG +2 -0
- data/lib/repofetch/bitbucketcloud/ASCII +17 -0
- data/lib/repofetch/bitbucketcloud.rb +125 -0
- data/lib/repofetch/cli.rb +5 -3
- data/lib/repofetch/exceptions.rb +6 -1
- data/lib/repofetch/github.rb +28 -29
- data/lib/repofetch/gitlab/ASCII +17 -0
- data/lib/repofetch/gitlab.rb +144 -0
- data/lib/repofetch/theme.rb +3 -0
- data/lib/repofetch.rb +34 -5
- metadata +36 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4fcfbdc976dd0c5345c0b46b617491569f862dfca0b770f7e8ba7a140580a799
|
4
|
+
data.tar.gz: 6367a3ec9d1604ca2f9ca3bcc1916a4550cc9c05044f580233f62a0583b57f9e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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.
|
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.
|
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.
|
50
|
+
json (2.6.3)
|
50
51
|
loofah (2.19.1)
|
51
52
|
crass (~> 1.0.2)
|
52
53
|
nokogiri (>= 1.5.9)
|
53
|
-
|
54
|
-
|
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.
|
66
|
+
parser (3.2.0.0)
|
67
67
|
ast (~> 2.4.1)
|
68
68
|
public_suffix (5.0.1)
|
69
|
-
racc (1.6.
|
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.
|
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.
|
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.
|
96
|
+
rubocop (1.43.0)
|
97
97
|
json (~> 2.3)
|
98
98
|
parallel (~> 1.10)
|
99
|
-
parser (>= 3.
|
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.
|
103
|
+
rubocop-ast (>= 1.24.1, < 2.0)
|
104
104
|
ruby-progressbar (~> 1.7)
|
105
|
-
unicode-display_width (>=
|
106
|
-
rubocop-ast (1.24.
|
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.
|
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.
|
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.
|
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
|
-
|
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.
|
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
|
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/
|
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.
|
1
|
+
0.4.3
|
2
2
|
|
3
|
-
##
|
3
|
+
## Added
|
4
4
|
|
5
|
-
-
|
6
|
-
-
|
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
|
@@ -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
|
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
|
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('-
|
73
|
+
opts.on('-P', '--plugin PLUGIN', 'Use the specified plugin.') do |plugin|
|
72
74
|
@plugin = available_plugins[plugin]
|
73
75
|
end
|
74
76
|
|
data/lib/repofetch/exceptions.rb
CHANGED
@@ -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
|
data/lib/repofetch/github.rb
CHANGED
@@ -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
|
14
|
-
SSH_REMOTE_REGEX = %r{git@github\.com:(?<owner>[\w
|
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
|
-
[
|
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
|
-
|
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
|
-
|
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 [
|
69
|
+
# @raise [Repofetch::PluginUsageError] if this plugin was selected *and* arguments were passed.
|
69
70
|
def self.from_git(git, args)
|
70
|
-
|
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 [
|
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
|
-
|
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
|
112
|
-
Repofetch::Stat.new('
|
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: '⭐'
|
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: '👀'
|
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: '🔱'
|
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: '🐣'
|
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: '📤'
|
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
|
-
|
138
|
-
|
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: '❗'
|
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: '🔀'
|
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
|
data/lib/repofetch/theme.rb
CHANGED
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
|
-
|
198
|
-
|
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
|
236
|
+
def initialize(label, value, emoji: nil)
|
224
237
|
@label = label
|
225
238
|
@value = value
|
226
239
|
@emoji = emoji
|
227
|
-
@
|
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}#{
|
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.
|
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:
|
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:
|
334
|
+
version: '0'
|
303
335
|
requirements: []
|
304
336
|
rubygems_version: 3.1.6
|
305
337
|
signing_key:
|