hubba 0.6.1 → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Manifest.txt +4 -2
- data/README.md +248 -1
- data/lib/hubba/folio.rb +60 -0
- data/lib/hubba/github.rb +138 -65
- data/lib/hubba/{reposet.rb → hubba.rb} +15 -88
- data/lib/hubba/stats.rb +161 -26
- data/lib/hubba/update.rb +44 -0
- data/lib/hubba/update_traffic.rb +52 -0
- data/lib/hubba/version.rb +1 -1
- data/lib/hubba.rb +4 -2
- metadata +6 -4
- data/lib/hubba/client.rb +0 -82
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0c7c4d9d7af183756855559bc03b8910bdcb1ace
|
4
|
+
data.tar.gz: 4cfd725ee9c1a3138370cbcb61e1bd900d689012
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a6cb53226a5a73777bc9ad86d82a22a39ea56710514233d5530b489d9fcce1bd9cc67b65f6d41f488d3ee4a266e0be5cbcbeef9231cef9007427928d60785214
|
7
|
+
data.tar.gz: 38395c69b0e45a174a1629029803f41b3e5d1ca7dba27d893dfcebd70f59ed4f17e60e6ec1715c331b61d8e49ef727de564aba14a03b0dfe75a6c15850e3bdc8
|
data/Manifest.txt
CHANGED
@@ -3,12 +3,14 @@ Manifest.txt
|
|
3
3
|
README.md
|
4
4
|
Rakefile
|
5
5
|
lib/hubba.rb
|
6
|
-
lib/hubba/client.rb
|
7
6
|
lib/hubba/config.rb
|
7
|
+
lib/hubba/folio.rb
|
8
8
|
lib/hubba/github.rb
|
9
|
+
lib/hubba/hubba.rb
|
9
10
|
lib/hubba/reports.rb
|
10
|
-
lib/hubba/reposet.rb
|
11
11
|
lib/hubba/stats.rb
|
12
|
+
lib/hubba/update.rb
|
13
|
+
lib/hubba/update_traffic.rb
|
12
14
|
lib/hubba/version.rb
|
13
15
|
test/helper.rb
|
14
16
|
test/stats/jekyll~minima.json
|
data/README.md
CHANGED
@@ -10,8 +10,255 @@ hubba gem - (yet) another (lite) GitHub HTTP API client / library
|
|
10
10
|
|
11
11
|
## Usage
|
12
12
|
|
13
|
-
TBD
|
14
13
|
|
14
|
+
### Step 0: Secrets, Secrets, Secrets - Your Authentication Token
|
15
|
+
|
16
|
+
Note: Set your GitHub env credentials (personal access token preferred) e.g.
|
17
|
+
|
18
|
+
```
|
19
|
+
set HUBBA_TOKEN=abcdef0123456789
|
20
|
+
# - or -
|
21
|
+
set HUBBA_USER=you
|
22
|
+
set HUBBA_PASSWORD=topsecret
|
23
|
+
```
|
24
|
+
|
25
|
+
|
26
|
+
### Step 1: Get a list of all your repos
|
27
|
+
|
28
|
+
Use the GitHub API to get a list of all your repos:
|
29
|
+
|
30
|
+
``` ruby
|
31
|
+
require 'hubba'
|
32
|
+
|
33
|
+
h = Hubba.reposet( 'geraldb' )
|
34
|
+
pp h
|
35
|
+
|
36
|
+
File.open( './repos.yml', 'w' ) { |f| f.write( h.to_yaml ) }
|
37
|
+
```
|
38
|
+
|
39
|
+
resulting in:
|
40
|
+
|
41
|
+
``` yaml
|
42
|
+
geraldb (11):
|
43
|
+
- austria
|
44
|
+
- catalog
|
45
|
+
- chelitas
|
46
|
+
- geraldb.github.io
|
47
|
+
- logos
|
48
|
+
- sandbox
|
49
|
+
- talks
|
50
|
+
- web-proxy-win
|
51
|
+
- webcomponents
|
52
|
+
- webpub-reader
|
53
|
+
- wine.db.tools
|
54
|
+
|
55
|
+
openfootball (41):
|
56
|
+
- africa-cup
|
57
|
+
- austria
|
58
|
+
- club-world-cup
|
59
|
+
- clubs
|
60
|
+
- confed-cup
|
61
|
+
- copa-america
|
62
|
+
- copa-libertadores
|
63
|
+
- copa-sudamericana
|
64
|
+
- deutschland
|
65
|
+
# ...
|
66
|
+
```
|
67
|
+
|
68
|
+
|
69
|
+
Note: If you have more than one account (e.g. an extra robot account or such)
|
70
|
+
you can add them along e.g.
|
71
|
+
|
72
|
+
|
73
|
+
``` ruby
|
74
|
+
h = Hubba.reposet( 'geraldb', 'yorobot' )
|
75
|
+
pp h
|
76
|
+
```
|
77
|
+
|
78
|
+
|
79
|
+
Note: By default all your repos from organizations get auto-included -
|
80
|
+
use the `orgs: false` option to turn off auto-inclusion.
|
81
|
+
|
82
|
+
Note: By default all (personal) repos, that is, repos in your primary (first)
|
83
|
+
account that are forks get auto-excluded.
|
84
|
+
|
85
|
+
|
86
|
+
|
87
|
+
### Step 2: Update repo statistics (daily / weekly / monthly)
|
88
|
+
|
89
|
+
|
90
|
+
Use `update_stats` to
|
91
|
+
to get the latest commit, star count and more for all your repos
|
92
|
+
listed in `./repos.yml` via the GitHub API:
|
93
|
+
|
94
|
+
``` ruby
|
95
|
+
Hubba.update_stats( './repos.yml' )
|
96
|
+
```
|
97
|
+
|
98
|
+
Note: By default the datafiles (one per repo)
|
99
|
+
get stored in the `./data` directory.
|
100
|
+
|
101
|
+
|
102
|
+
|
103
|
+
|
104
|
+
### Step 3: Generate some statistics / reports
|
105
|
+
|
106
|
+
|
107
|
+
Hubba has four built-in reports (for now):
|
108
|
+
|
109
|
+
- `ReportSummary` - A-Z list of your repos by orgs with stars and size in kb
|
110
|
+
- `ReportStars` - your repos ranked by stars
|
111
|
+
- `ReportTimeline` - your repos in reverse chronological order by creation
|
112
|
+
- `ReportUpdates` - your repos in reverse chronological order by last commit
|
113
|
+
|
114
|
+
|
115
|
+
If you only generate a single report, use:
|
116
|
+
|
117
|
+
``` ruby
|
118
|
+
report = Hubba::ReportSummary.new( './repos.yml' )
|
119
|
+
report.save( './SUMMARY.md' )
|
120
|
+
```
|
121
|
+
|
122
|
+
|
123
|
+
If you generate more reports, (re)use the in-memory statistics cache / object:
|
124
|
+
|
125
|
+
``` ruby
|
126
|
+
stats = Hubba.stats( './repos.yml' )
|
127
|
+
|
128
|
+
report = Hubba::ReportSummary.new( stats )
|
129
|
+
report.save( './SUMMARY.md' )
|
130
|
+
|
131
|
+
report = Hubba::ReportStars.new( stats )
|
132
|
+
report.save( './STARS.md' )
|
133
|
+
|
134
|
+
report = Hubba::ReportTimeline.new( stats )
|
135
|
+
report.save( './TIMELINE.md' )
|
136
|
+
|
137
|
+
report = Hubba::ReportUpdates.new( stats )
|
138
|
+
report.save( './UPDATES.md' )
|
139
|
+
```
|
140
|
+
|
141
|
+
|
142
|
+
### Report Examples
|
143
|
+
|
144
|
+
#### Report Example - Summary
|
145
|
+
|
146
|
+
A-Z list of your repos by orgs with stars and size in kb.
|
147
|
+
Results in:
|
148
|
+
|
149
|
+
---
|
150
|
+
|
151
|
+
> 593 repos @ 83 orgs
|
152
|
+
>
|
153
|
+
> ### geraldb _(11)_
|
154
|
+
>
|
155
|
+
> **austria** ★1 (552 kb) · **catalog** ★3 (156 kb) · **chelitas** ★1 (168 kb) · **geraldb.github.io** ★1 (520 kb) · **logos** ★1 (363 kb) · **sandbox** ★2 (529 kb) · **talks** ★200 (16203 kb) · **web-proxy-win** ★8 (152 kb) · **webcomponents** ★1 (164 kb) · **webpub-reader** ★3 (11 kb) · **wine.db.tools** ★1 (252 kb)
|
156
|
+
>
|
157
|
+
> ...
|
158
|
+
|
159
|
+
---
|
160
|
+
|
161
|
+
(Live Example - [`SUMMARY.md`](https://github.com/yorobot/backup/blob/master/SUMMARY.md))
|
162
|
+
|
163
|
+
|
164
|
+
#### Report Example - Stars
|
165
|
+
|
166
|
+
Your repos ranked by stars. Results in:
|
167
|
+
|
168
|
+
---
|
169
|
+
|
170
|
+
> 593 repos @ 83 orgs
|
171
|
+
>
|
172
|
+
> 1. ★2936 **openblockchains/awesome-blockchains** (2514 kb)
|
173
|
+
> 2. ★851 **planetjekyll/awesome-jekyll-plugins** (148 kb)
|
174
|
+
> 3. ★604 **factbook/factbook.json** (7355 kb)
|
175
|
+
> 4. ★593 **openfootball/football.json** (2135 kb)
|
176
|
+
> 5. ★570 **openmundi/world.db** (1088 kb)
|
177
|
+
> 6. ★552 **openblockchains/programming-blockchains** (552 kb)
|
178
|
+
> 7. ★547 **mundimark/awesome-markdown** (83 kb)
|
179
|
+
> 8. ★532 **planetjekyll/awesome-jekyll** (110 kb)
|
180
|
+
> 9. ★489 **cryptocopycats/awesome-cryptokitties** (4154 kb)
|
181
|
+
> 10. ★445 **openfootball/world-cup** (638 kb)
|
182
|
+
>
|
183
|
+
> ...
|
184
|
+
|
185
|
+
---
|
186
|
+
|
187
|
+
(Live Example: [`STARS.md`](https://github.com/yorobot/backup/blob/master/STARS.md))
|
188
|
+
|
189
|
+
|
190
|
+
#### Report Example - Timeline
|
191
|
+
|
192
|
+
Your repos in reverse chronological order by creation.
|
193
|
+
Results in:
|
194
|
+
|
195
|
+
---
|
196
|
+
|
197
|
+
> 593 repos @ 83 orgs
|
198
|
+
>
|
199
|
+
> ## 2020
|
200
|
+
>
|
201
|
+
> ### 9
|
202
|
+
>
|
203
|
+
> - 2020-09-18 ★1 **yorobot/workflow** (83 kb)
|
204
|
+
>
|
205
|
+
> ### 6
|
206
|
+
>
|
207
|
+
> - 2020-06-27 ★2 **yorobot/sport.db.more** (80 kb)
|
208
|
+
> - 2020-06-24 ★1 **yorobot/stage** (554 kb)
|
209
|
+
> - 2020-06-11 ★1 **yorobot/cache.csv** (336 kb)
|
210
|
+
>
|
211
|
+
> ...
|
212
|
+
|
213
|
+
---
|
214
|
+
|
215
|
+
(Live Example: [`TIMELINE.md`](https://github.com/yorobot/backup/blob/master/TIMELINE.md))
|
216
|
+
|
217
|
+
|
218
|
+
|
219
|
+
#### Report Example - Updates
|
220
|
+
|
221
|
+
Your repos in reverse chronological order by last commit. Results in:
|
222
|
+
|
223
|
+
---
|
224
|
+
|
225
|
+
> 593 repos @ 83 orgs
|
226
|
+
>
|
227
|
+
> committed / pushed / updated / created
|
228
|
+
>
|
229
|
+
> - (1d) **yorobot/backup** ★4 - 2020-10-08 (=/=) / 2020-10-08 (=) / 2020-10-08 / 2015-04-04 - ‹› (1595 kb)
|
230
|
+
> - (1d) **yorobot/logs** ★1 - 2020-10-08 (=/=) / 2020-10-08 (=) / 2020-10-08 / 2016-09-13 - ‹› (172 kb)
|
231
|
+
> - (1d) **rubycoco/git** ★9 - 2020-10-08 (=/=) / 2020-10-08 (=) / 2020-10-08 / 2015-11-16 - ‹› (88 kb)
|
232
|
+
> - (1d) **openfootball/football.json** ★593 - 2020-10-08 (=/=) / 2020-10-08 (=) / 2020-10-08 / 2015-09-17 - ‹› (2135 kb)
|
233
|
+
> - (2d) **yorobot/workflow** ★1 - 2020-10-07 (=/=) / 2020-10-07 (=) / 2020-10-07 / 2020-09-18 - ‹› (83 kb)
|
234
|
+
> - (2d) **rubycoco/webclient** ★5 - 2020-10-07 (=/=) / 2020-10-07 (=) / 2020-10-07 / 2012-06-02 - ‹› (39 kb)
|
235
|
+
> - (3d) **footballcsv/belgium** ★1 - 2020-10-06 (=/=) / 2020-10-06 (=) / 2020-10-06 / 2014-07-25 - ‹› (314 kb)
|
236
|
+
> - (3d) **footballcsv/england** ★105 - 2020-10-06 (=/=) / 2020-10-06 (=) / 2020-10-06 / 2014-07-23 - ‹› (8666 kb)
|
237
|
+
> - (3d) **footballcsv/austria** ★1 - 2020-10-06 (=/=) / 2020-10-06 (=) / 2020-10-06 / 2018-07-16 - ‹› (91 kb)
|
238
|
+
> - (3d) **footballcsv/espana** ★15 - 2020-10-06 (=/=) / 2020-10-06 (=) / 2020-10-06 / 2014-07-23 - ‹› (1107 kb)
|
239
|
+
> - (3d) **footballcsv/deutschland** ★5 - 2020-10-06 (=/=) / 2020-10-06 (=) / 2020-10-06 / 2014-07-25 - ‹› (1343 kb)
|
240
|
+
>
|
241
|
+
> ...
|
242
|
+
|
243
|
+
---
|
244
|
+
|
245
|
+
(Live Example: [`UPDATES.md`](https://github.com/yorobot/backup/blob/master/UPDATES.md))
|
246
|
+
|
247
|
+
|
248
|
+
|
249
|
+
That's all for now.
|
250
|
+
|
251
|
+
|
252
|
+
|
253
|
+
## Installation
|
254
|
+
|
255
|
+
Use
|
256
|
+
|
257
|
+
gem install hubba
|
258
|
+
|
259
|
+
or add the gem to your Gemfile
|
260
|
+
|
261
|
+
gem 'hubba'
|
15
262
|
|
16
263
|
|
17
264
|
## License
|
data/lib/hubba/folio.rb
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
module Hubba
|
2
|
+
|
3
|
+
class Folio # todo/check: use a different name e.g (Port)Folio, Cache, Summary, (Data)Base, Census, Catalog, Collection, Index, Register or such???
|
4
|
+
class Repo ## (nested) class
|
5
|
+
|
6
|
+
attr_reader :owner,
|
7
|
+
:name
|
8
|
+
|
9
|
+
def initialize( owner, name )
|
10
|
+
@owner = owner ## rename to login, username - why? why not?
|
11
|
+
@name = name ## rename to reponame ??
|
12
|
+
end
|
13
|
+
|
14
|
+
def full_name() "#{owner}/#{name}"; end
|
15
|
+
|
16
|
+
def stats
|
17
|
+
## note: load stats on demand only (first access) for now - why? why not?
|
18
|
+
@stats ||= begin
|
19
|
+
stats = Stats.new( full_name )
|
20
|
+
stats.read
|
21
|
+
stats
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def diff
|
26
|
+
@diff ||= stats.calc_diff_stars( samples: 3, days: 30 )
|
27
|
+
end
|
28
|
+
end # (nested) class Repo
|
29
|
+
|
30
|
+
|
31
|
+
attr_reader :orgs, :repos
|
32
|
+
|
33
|
+
def initialize( h )
|
34
|
+
@orgs = [] # orgs and users -todo/check: use better name - logins or owners? why? why not?
|
35
|
+
@repos = []
|
36
|
+
add( h )
|
37
|
+
|
38
|
+
puts "#{@repos.size} repos @ #{@orgs.size} orgs"
|
39
|
+
end
|
40
|
+
|
41
|
+
#############
|
42
|
+
## private helpes
|
43
|
+
def add( h ) ## add repos.yml set
|
44
|
+
h.each do |org_with_counter, names|
|
45
|
+
## remove optional number from key e.g.
|
46
|
+
## mrhydescripts (3) => mrhydescripts
|
47
|
+
## footballjs (4) => footballjs
|
48
|
+
## etc.
|
49
|
+
org = org_with_counter.sub( /\([0-9]+\)/, '' ).strip
|
50
|
+
repos = []
|
51
|
+
names.each do |name|
|
52
|
+
repo = Repo.new( org, name )
|
53
|
+
repos << repo
|
54
|
+
end
|
55
|
+
@orgs << [org, repos]
|
56
|
+
@repos += repos
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end # class Folio
|
60
|
+
end # module Hubba
|
data/lib/hubba/github.rb
CHANGED
@@ -1,45 +1,59 @@
|
|
1
1
|
module Hubba
|
2
2
|
|
3
3
|
|
4
|
-
class
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
class Github
|
5
|
+
BASE_URL = 'https://api.github.com'
|
6
|
+
|
7
|
+
###############
|
8
|
+
## (nested) classes for "wrapped" response (parsed json body)
|
9
|
+
class Resource
|
10
|
+
attr_reader :data
|
11
|
+
def initialize( data )
|
12
|
+
@data = data
|
13
|
+
end
|
8
14
|
end
|
9
|
-
end
|
10
15
|
|
11
|
-
class Repos < Resource
|
12
|
-
|
13
|
-
|
14
|
-
|
16
|
+
class Repos < Resource
|
17
|
+
def names
|
18
|
+
## sort by name
|
19
|
+
data.map { |item| item['name'] }.sort
|
20
|
+
end
|
15
21
|
end
|
16
|
-
end
|
17
22
|
|
18
|
-
class Orgs < Resource
|
19
|
-
|
20
|
-
|
21
|
-
|
23
|
+
class Orgs < Resource
|
24
|
+
def logins
|
25
|
+
## sort by name
|
26
|
+
data.map { |item| item['login'] }.sort
|
27
|
+
end
|
28
|
+
alias_method :names, :logins ## add name alias - why? why not?
|
22
29
|
end
|
23
|
-
alias_method :names, :logins ## add name alias - why? why not?
|
24
|
-
end
|
25
|
-
|
26
30
|
|
27
31
|
|
28
|
-
class Github
|
29
32
|
|
30
|
-
def initialize
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
33
|
+
def initialize( token: nil,
|
34
|
+
user: nil,
|
35
|
+
password: nil )
|
36
|
+
@token = nil
|
37
|
+
@user = nil
|
38
|
+
@password = nil
|
39
|
+
|
40
|
+
if token ## 1a) give preference to passed in token
|
41
|
+
@token = token
|
42
|
+
elsif user && password ## 1b) or passed in user/password credentials
|
43
|
+
@user = user
|
44
|
+
@password = password
|
45
|
+
elsif Hubba.config.token ## 2a) followed by configured (or env) token
|
46
|
+
@token = Hubba.config.token
|
47
|
+
elsif Hubba.config.user && Hubba.config.password ## 2b)
|
48
|
+
@user = Hubba.config.user
|
49
|
+
@password = Hubba.config.password
|
50
|
+
else ## 3)
|
51
|
+
## no token or credentials passed in or configured
|
52
|
+
end
|
40
53
|
end
|
41
54
|
|
42
55
|
|
56
|
+
|
43
57
|
def user( name )
|
44
58
|
Resource.new( get "/users/#{name}" )
|
45
59
|
end
|
@@ -77,61 +91,120 @@ def repo( full_name ) ## full_name (handle) e.g. henrythemes/jekyll-starter-th
|
|
77
91
|
Resource.new( get "/repos/#{full_name}" )
|
78
92
|
end
|
79
93
|
|
94
|
+
def repo_languages( full_name )
|
95
|
+
Resource.new( get "/repos/#{full_name}/languages" )
|
96
|
+
end
|
97
|
+
|
98
|
+
def repo_topics( full_name )
|
99
|
+
## note: requires "api preview" accept headers (overwrites default v3+json)
|
100
|
+
## e.g. application/vnd.github.mercy-preview+json
|
101
|
+
Resource.new( get( "/repos/#{full_name}/topics", preview: 'mercy' ) )
|
102
|
+
end
|
103
|
+
|
104
|
+
|
80
105
|
def repo_commits( full_name )
|
81
106
|
Resource.new( get "/repos/#{full_name}/commits" )
|
82
107
|
end
|
83
108
|
|
84
109
|
|
110
|
+
def repo_traffic_clones( full_name )
|
111
|
+
# Get repository clones
|
112
|
+
# Get the total number of clones and breakdown per day or week
|
113
|
+
# for the last 14 days.
|
114
|
+
# Timestamps are aligned to UTC midnight of the beginning of the day or week.
|
115
|
+
# Week begins on Monday.
|
116
|
+
Resource.new( get "/repos/#{full_name}/traffic/clones" )
|
117
|
+
end
|
85
118
|
|
86
|
-
|
87
|
-
#
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
repo = repo( full_name )
|
94
|
-
puts "[update 2/2] fetching repo >#{full_name}< commits ..."
|
95
|
-
commits = repo_commits( full_name )
|
96
|
-
|
97
|
-
stats.update( repo, commits )
|
98
|
-
else
|
99
|
-
raise ArgumentError, "unknown source object passed in - expected Hubba::Stats; got #{obj.class.name}"
|
100
|
-
end
|
119
|
+
def repo_traffic_views( full_name )
|
120
|
+
# Get page views
|
121
|
+
# Get the total number of views and breakdown per day or week
|
122
|
+
# for the last 14 days.
|
123
|
+
# Timestamps are aligned to UTC midnight of the beginning of the day or week.
|
124
|
+
# Week begins on Monday.
|
125
|
+
Resource.new( get "/repos/#{full_name}/traffic/views" )
|
101
126
|
end
|
102
127
|
|
103
128
|
|
104
|
-
def
|
105
|
-
|
129
|
+
def repo_traffic_popular_paths( full_name )
|
130
|
+
# Get top referral paths
|
131
|
+
# Get the top 10 popular contents over the last 14 days.
|
132
|
+
Resource.new( get "/repos/#{full_name}/traffic/popular/paths" )
|
133
|
+
end
|
106
134
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
135
|
+
def repo_traffic_popular_referrers( full_name )
|
136
|
+
# Get top referral sources
|
137
|
+
# Get the top 10 referrers over the last 14 days.
|
138
|
+
Resource.new( get "/repos/#{full_name}/traffic/popular/referrers" )
|
139
|
+
end
|
111
140
|
|
112
|
-
org = org_with_counter.sub( /\([0-9]+\)/, '' ).strip
|
113
141
|
|
114
|
-
## puts " -- #{key_with_counter} [#{key}] --"
|
115
142
|
|
116
|
-
names.each do |name|
|
117
|
-
full_name = "#{org}/#{name}"
|
118
143
|
|
119
|
-
|
120
|
-
|
121
|
-
|
144
|
+
private
|
145
|
+
def get( request_uri, preview: nil )
|
146
|
+
|
147
|
+
puts "GET #{request_uri}"
|
148
|
+
|
149
|
+
## note: request_uri ALWAYS starts with leading /, thus use + for now!!!
|
150
|
+
# e.g. /users/geraldb
|
151
|
+
# /users/geraldb/repos
|
152
|
+
url = BASE_URL + request_uri
|
153
|
+
|
154
|
+
|
155
|
+
headers = {}
|
156
|
+
## add default headers if nothing (custom) set / passed-in
|
157
|
+
headers['User-Agent'] = "ruby/hubba v#{VERSION}" ## required by GitHub API
|
158
|
+
headers['Accept'] = if preview # e.g. mercy or ???
|
159
|
+
"application/vnd.github.#{preview}-preview+json"
|
160
|
+
else
|
161
|
+
'application/vnd.github.v3+json' ## default - recommend by GitHub API
|
162
|
+
end
|
163
|
+
|
164
|
+
auth = []
|
165
|
+
## check if credentials (user/password) present - if yes, use basic auth
|
166
|
+
if @token
|
167
|
+
puts " using (personal access) token - starting with: #{@token[0..6]}**********"
|
168
|
+
headers['Authorization'] = "token #{@token}"
|
169
|
+
## token works like:
|
170
|
+
## curl -H 'Authorization: token my_access_token' https://api.github.com/user/repos
|
171
|
+
elsif @user && @password
|
172
|
+
puts " using basic auth - user: #{@user}, password: ***"
|
173
|
+
## use credential auth "tuple" (that is, array with two string items) for now
|
174
|
+
## or use Webclient::HttpBasicAuth or something - why? why not?
|
175
|
+
auth = [@user, @password]
|
176
|
+
# req.basic_auth( @user, @password )
|
177
|
+
else
|
178
|
+
puts " using no credentials (no token, no user/password)"
|
179
|
+
end
|
122
180
|
|
123
|
-
|
181
|
+
res = Webclient.get( url,
|
182
|
+
headers: headers,
|
183
|
+
auth: auth )
|
124
184
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
end
|
185
|
+
# Get specific header
|
186
|
+
# response["content-type"]
|
187
|
+
# => "text/html; charset=UTF-8"
|
129
188
|
|
189
|
+
# Iterate all response headers.
|
190
|
+
# puts "HTTP HEADERS:"
|
191
|
+
# res.headers.each do |key, value|
|
192
|
+
# puts " #{key}: >#{value}<"
|
193
|
+
# end
|
194
|
+
# puts
|
130
195
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
196
|
+
# => "location => http://www.google.com/"
|
197
|
+
# => "content-type => text/html; charset=UTF-8"
|
198
|
+
# ...
|
199
|
+
|
200
|
+
if res.status.ok?
|
201
|
+
res.json
|
202
|
+
else
|
203
|
+
puts "!! HTTP ERROR: #{res.status.code} #{res.status.message}:"
|
204
|
+
pp res.raw
|
205
|
+
exit 1
|
206
|
+
end
|
207
|
+
end # method get
|
135
208
|
|
136
209
|
end # class Github
|
137
210
|
end # module Hubba
|
@@ -1,100 +1,13 @@
|
|
1
1
|
module Hubba
|
2
2
|
|
3
3
|
|
4
|
-
def self.update_stats( host_or_path='./repos.yml' ) ## move to reposet e.g. Reposet#update_status!!!!
|
5
|
-
h = if hash_or_path.is_a?( String ) ## assume it is a file path!!!
|
6
|
-
path = hash_or_path
|
7
|
-
YAML.load_file( path )
|
8
|
-
else
|
9
|
-
hash_or_path # assume its a hash / reposet already!!!
|
10
|
-
end
|
11
|
-
|
12
|
-
gh = Github.new
|
13
|
-
gh.update_stats( h )
|
14
|
-
end
|
15
|
-
|
16
|
-
|
17
|
-
def self.stats( hash_or_path='./repos.yml' ) ## use read_stats or such - why? why not?
|
18
|
-
h = if hash_or_path.is_a?( String ) ## assume it is a file path!!!
|
19
|
-
path = hash_or_path
|
20
|
-
YAML.load_file( path )
|
21
|
-
else
|
22
|
-
hash_or_path # assume its a hash / reposet already!!!
|
23
|
-
end
|
24
|
-
|
25
|
-
Summary.new( h ) ## wrap in "easy-access" facade / wrapper
|
26
|
-
end
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
class Summary # todo/check: use a different name e.g (Data)Base, Census, Catalog, Collection, Index, Register or such???
|
31
|
-
|
32
|
-
class Repo ## (nested) class
|
33
|
-
|
34
|
-
attr_reader :owner,
|
35
|
-
:name
|
36
|
-
|
37
|
-
def initialize( owner, name )
|
38
|
-
@owner = owner ## rename to login, username - why? why not?
|
39
|
-
@name = name ## rename to reponame ??
|
40
|
-
end
|
41
|
-
|
42
|
-
def full_name() "#{owner}/#{name}"; end
|
43
|
-
|
44
|
-
def stats
|
45
|
-
## note: load stats on demand only (first access) for now - why? why not?
|
46
|
-
@stats ||= begin
|
47
|
-
stats = Stats.new( full_name )
|
48
|
-
stats.read
|
49
|
-
stats
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
def diff
|
54
|
-
@diff ||= stats.calc_diff_stars( samples: 3, days: 30 )
|
55
|
-
end
|
56
|
-
end # (nested) class Repo
|
57
|
-
|
58
|
-
|
59
|
-
attr_reader :orgs, :repos
|
60
|
-
|
61
|
-
def initialize( hash )
|
62
|
-
@orgs = [] # orgs and users -todo/check: use better name - logins or owners? why? why not?
|
63
|
-
@repos = []
|
64
|
-
add( hash )
|
65
|
-
|
66
|
-
puts "#{@repos.size} repos @ #{@orgs.size} orgs"
|
67
|
-
end
|
68
|
-
|
69
|
-
#############
|
70
|
-
## private helpes
|
71
|
-
def add( hash ) ## add repos.yml set
|
72
|
-
hash.each do |org_with_counter, names|
|
73
|
-
## remove optional number from key e.g.
|
74
|
-
## mrhydescripts (3) => mrhydescripts
|
75
|
-
## footballjs (4) => footballjs
|
76
|
-
## etc.
|
77
|
-
org = org_with_counter.sub( /\([0-9]+\)/, '' ).strip
|
78
|
-
repos = []
|
79
|
-
names.each do |name|
|
80
|
-
repo = Repo.new( org, name )
|
81
|
-
repos << repo
|
82
|
-
end
|
83
|
-
@orgs << [org, repos]
|
84
|
-
@repos += repos
|
85
|
-
end
|
86
|
-
end
|
87
|
-
end # class Summary
|
88
|
-
|
89
|
-
|
90
|
-
|
91
4
|
## orgs - include repos form org(anizations) too
|
92
5
|
## cache - save json response to cache_dir - change to/use debug/tmp_dir? - why? why not?
|
93
6
|
def self.reposet( *users, orgs: true,
|
94
7
|
cache: false )
|
95
8
|
# users = [users] if users.is_a?( String ) ### wrap in array if single user
|
96
9
|
|
97
|
-
gh =
|
10
|
+
gh = Github.new
|
98
11
|
|
99
12
|
forks = []
|
100
13
|
|
@@ -166,5 +79,19 @@ def self.reposet( *users, orgs: true,
|
|
166
79
|
|
167
80
|
h
|
168
81
|
end ## method reposet
|
82
|
+
|
83
|
+
|
84
|
+
def self.stats( hash_or_path='./repos.yml' ) ## use read_stats or such - why? why not?
|
85
|
+
h = if hash_or_path.is_a?( String ) ## assume it is a file path!!!
|
86
|
+
path = hash_or_path
|
87
|
+
YAML.load_file( path )
|
88
|
+
else
|
89
|
+
hash_or_path # assume its a hash / reposet already!!!
|
90
|
+
end
|
91
|
+
|
92
|
+
Folio.new( h ) ## wrap in "easy-access" facade / wrapper
|
93
|
+
end ## method stats
|
94
|
+
|
95
|
+
|
169
96
|
end # module Hubba
|
170
97
|
|
data/lib/hubba/stats.rb
CHANGED
@@ -223,12 +223,105 @@ module Hubba
|
|
223
223
|
|
224
224
|
|
225
225
|
|
226
|
-
|
227
|
-
##
|
226
|
+
##################
|
227
|
+
## update
|
228
|
+
|
229
|
+
def update_traffic( clones: nil,
|
230
|
+
views: nil,
|
231
|
+
paths: nil,
|
232
|
+
referrers: nil )
|
233
|
+
|
234
|
+
traffic = @data[ 'traffic' ] ||= {}
|
235
|
+
|
236
|
+
summary = traffic['summary'] ||= {}
|
237
|
+
history = traffic['history'] ||= {}
|
238
|
+
|
239
|
+
|
240
|
+
if views
|
241
|
+
raise ArgumentError, "Github::Resource expected; got #{views.class.name}" unless views.is_a?( Github::Resource )
|
242
|
+
=begin
|
243
|
+
{"count"=>1526,
|
244
|
+
"uniques"=>287,
|
245
|
+
"views"=>
|
246
|
+
[{"timestamp"=>"2020-09-27T00:00:00Z", "count"=>52, "uniques"=>13},
|
247
|
+
{"timestamp"=>"2020-09-28T00:00:00Z", "count"=>108, "uniques"=>28},
|
248
|
+
...
|
249
|
+
]}>
|
250
|
+
=end
|
251
|
+
|
252
|
+
## keep lastest (summary) record of last two weeks (14 days)
|
253
|
+
summary['views'] = { 'count' => views.data['count'],
|
254
|
+
'uniques' => views.data['uniques'] }
|
255
|
+
|
256
|
+
## update history / day-by-day items / timeline
|
257
|
+
views.data['views'].each do |view|
|
258
|
+
# e.g. "2020-09-27T00:00:00Z"
|
259
|
+
timestamp = DateTime.strptime( view['timestamp'], '%Y-%m-%dT%H:%M:%S%z' )
|
260
|
+
|
261
|
+
item = history[ timestamp.strftime( '%Y-%m-%d' ) ] ||= {} ## e.g. 2016-09-27
|
262
|
+
## note: merge "in-place"
|
263
|
+
item.merge!( { 'views' => { 'count' => view['count'],
|
264
|
+
'uniques' => view['uniques'] }} )
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
if clones
|
269
|
+
raise ArgumentError, "Github::Resource expected; got #{clones.class.name}" unless clones.is_a?( Github::Resource )
|
270
|
+
=begin
|
271
|
+
{"count"=>51,
|
272
|
+
"uniques"=>17,
|
273
|
+
"clones"=>
|
274
|
+
[{"timestamp"=>"2020-09-26T00:00:00Z", "count"=>1, "uniques"=>1},
|
275
|
+
{"timestamp"=>"2020-09-27T00:00:00Z", "count"=>2, "uniques"=>1},
|
276
|
+
...
|
277
|
+
]}
|
278
|
+
=end
|
279
|
+
|
280
|
+
## keep lastest (summary) record of last two weeks (14 days)
|
281
|
+
summary['clones'] = { 'count' => clones.data['count'],
|
282
|
+
'uniques' => clones.data['uniques'] }
|
283
|
+
|
284
|
+
## update history / day-by-day items / timeline
|
285
|
+
clones.data['clones'].each do |clone|
|
286
|
+
# e.g. "2020-09-27T00:00:00Z"
|
287
|
+
timestamp = DateTime.strptime( clone['timestamp'], '%Y-%m-%dT%H:%M:%S%z' )
|
288
|
+
|
289
|
+
item = history[ timestamp.strftime( '%Y-%m-%d' ) ] ||= {} ## e.g. 2016-09-27
|
290
|
+
## note: merge "in-place"
|
291
|
+
item.merge!( { 'clones' => { 'count' => clone['count'],
|
292
|
+
'uniques' => clone['uniques'] }} )
|
293
|
+
end
|
294
|
+
end
|
228
295
|
|
229
|
-
|
230
|
-
|
231
|
-
|
296
|
+
if paths
|
297
|
+
raise ArgumentError, "Github::Resource expected; got #{paths.class.name}" unless paths.is_a?( Github::Resource )
|
298
|
+
=begin
|
299
|
+
[{"path"=>"/openfootball/england",
|
300
|
+
"title"=>
|
301
|
+
"openfootball/england: Free open public domain football data for England (and ...",
|
302
|
+
"count"=>394,
|
303
|
+
"uniques"=>227},
|
304
|
+
=end
|
305
|
+
summary['paths'] = paths.data
|
306
|
+
end
|
307
|
+
|
308
|
+
if referrers
|
309
|
+
raise ArgumentError, "Github::Resource expected; got #{referrers.class.name}" unless referrers.is_a?( Github::Resource )
|
310
|
+
=begin
|
311
|
+
[{"referrer"=>"github.com", "count"=>327, "uniques"=>198},
|
312
|
+
{"referrer"=>"openfootball.github.io", "count"=>71, "uniques"=>54},
|
313
|
+
{"referrer"=>"Google", "count"=>5, "uniques"=>5},
|
314
|
+
{"referrer"=>"reddit.com", "count"=>4, "uniques"=>4}]
|
315
|
+
=end
|
316
|
+
summary['referrers'] = referrers.data
|
317
|
+
end
|
318
|
+
end # method update_traffic
|
319
|
+
|
320
|
+
|
321
|
+
def update( repo,
|
322
|
+
commits: nil,
|
323
|
+
topics: nil ) ## update stats / fetch data from github via api
|
324
|
+
raise ArgumentError, "Github::Resource expected; got #{repo.class.name}" unless repo.is_a?( Github::Resource )
|
232
325
|
|
233
326
|
## e.g. 2015-05-11T20:21:43Z
|
234
327
|
## puts Time.iso8601( repo.data['created_at'] )
|
@@ -236,42 +329,82 @@ module Hubba
|
|
236
329
|
@data['updated_at'] = repo.data['updated_at']
|
237
330
|
@data['pushed_at'] = repo.data['pushed_at']
|
238
331
|
|
239
|
-
@data['size'] = repo.data['size'] # size in kb (kilobyte)
|
332
|
+
@data['size'] = repo.data['size'] # note: size in kb (kilobyte)
|
333
|
+
|
334
|
+
@data['description'] = repo.data['description']
|
335
|
+
@data['language'] = repo.data['language'] ## note: might be nil!!!
|
240
336
|
|
337
|
+
|
338
|
+
|
339
|
+
########################################
|
340
|
+
#### history / by date record
|
241
341
|
rec = {}
|
242
342
|
|
243
|
-
puts "stargazers_count"
|
244
|
-
puts repo.data['stargazers_count']
|
245
343
|
rec['stargazers_count'] = repo.data['stargazers_count']
|
344
|
+
rec['forks_count'] = repo.data['forks_count']
|
345
|
+
|
246
346
|
|
247
347
|
today = Date.today.strftime( '%Y-%m-%d' ) ## e.g. 2016-09-27
|
248
348
|
puts "add record #{today} to history..."
|
249
349
|
pp rec # check if stargazers_count is a number (NOT a string)
|
250
350
|
|
251
|
-
@data[ 'history' ] ||= {}
|
252
|
-
|
351
|
+
history = @data[ 'history' ] ||= {}
|
352
|
+
item = history[ today ] ||= {}
|
353
|
+
## note: merge "in-place" (overwrite with new - but keep other key/value pairs if any e.g. pageviews, clones, etc.)
|
354
|
+
item.merge!( rec )
|
355
|
+
|
356
|
+
|
253
357
|
|
254
358
|
##########################
|
255
359
|
## also check / keep track of (latest) commit
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
360
|
+
if commits
|
361
|
+
raise ArgumentError, "Github::Resource expected; got #{commits.class.name}" unless commits.is_a?( Github::Resource )
|
362
|
+
|
363
|
+
puts "update - last commit:"
|
364
|
+
## pp commits
|
365
|
+
commit = {
|
366
|
+
'committer' => {
|
367
|
+
'date' => commits.data[0]['commit']['committer']['date'],
|
368
|
+
'name' => commits.data[0]['commit']['committer']['name']
|
369
|
+
},
|
370
|
+
'author' => {
|
371
|
+
'date' => commits.data[0]['commit']['author']['date'],
|
372
|
+
'name' => commits.data[0]['commit']['author']['name']
|
373
|
+
},
|
374
|
+
'message' => commits.data[0]['commit']['message']
|
375
|
+
}
|
376
|
+
|
377
|
+
## for now store only the latest commit (e.g. a single commit in an array)
|
378
|
+
@data[ 'commits' ] = [commit]
|
379
|
+
end
|
380
|
+
|
381
|
+
if topics
|
382
|
+
raise ArgumentError, "Github::Resource expected; got #{topics.class.name}" unless topics.is_a?( Github::Resource )
|
383
|
+
|
384
|
+
puts "update - topics:"
|
385
|
+
## e.g.
|
386
|
+
# {"names"=>
|
387
|
+
# ["opendata",
|
388
|
+
# "football",
|
389
|
+
# "seriea",
|
390
|
+
# "italia",
|
391
|
+
# "italy",
|
392
|
+
# "juve",
|
393
|
+
# "inter",
|
394
|
+
# "napoli",
|
395
|
+
# "roma",
|
396
|
+
# "sqlite"]}
|
397
|
+
#
|
398
|
+
# {"names"=>[]}
|
399
|
+
|
400
|
+
@data[ 'topics' ] = topics.data['names']
|
401
|
+
end
|
402
|
+
|
272
403
|
|
273
404
|
pp @data
|
274
405
|
|
406
|
+
|
407
|
+
|
275
408
|
## reset (invalidate) cached values from data hash
|
276
409
|
## use after reading or fetching
|
277
410
|
@cache = {}
|
@@ -280,6 +413,8 @@ module Hubba
|
|
280
413
|
end
|
281
414
|
|
282
415
|
|
416
|
+
########################################
|
417
|
+
## read / write methods / helpers
|
283
418
|
|
284
419
|
def write
|
285
420
|
basename = full_name.gsub( '/', '~' ) ## e.g. poole/hyde become poole~hyde
|
data/lib/hubba/update.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
module Hubba
|
2
|
+
|
3
|
+
def self.update_stats( hash_or_path='./repos.yml' ) ## move to reposet e.g. Reposet#update_status!!!!
|
4
|
+
h = if hash_or_path.is_a?( String ) ## assume it is a file path!!!
|
5
|
+
path = hash_or_path
|
6
|
+
YAML.load_file( path )
|
7
|
+
else
|
8
|
+
hash_or_path # assume its a hash / reposet already!!!
|
9
|
+
end
|
10
|
+
|
11
|
+
gh = Github.new
|
12
|
+
|
13
|
+
h.each do |org_with_counter,names|
|
14
|
+
|
15
|
+
## remove optional number from key e.g.
|
16
|
+
## mrhydescripts (3) => mrhydescripts
|
17
|
+
## footballjs (4) => footballjs
|
18
|
+
## etc.
|
19
|
+
org = org_with_counter.sub( /\([0-9]+\)/, '' ).strip
|
20
|
+
|
21
|
+
## puts " -- #{key_with_counter} [#{key}] --"
|
22
|
+
|
23
|
+
names.each do |name|
|
24
|
+
full_name = "#{org}/#{name}"
|
25
|
+
|
26
|
+
## puts " fetching stats #{count+1}/#{repo_count} - >#{full_name}<..."
|
27
|
+
stats = Stats.new( full_name )
|
28
|
+
stats.read
|
29
|
+
|
30
|
+
puts "update >#{full_name}< [1/3] - fetching repo..."
|
31
|
+
repo = gh.repo( full_name )
|
32
|
+
puts "update >#{full_name}< [2/3] - fetching repo commits ..."
|
33
|
+
commits = gh.repo_commits( full_name )
|
34
|
+
puts "update >#{full_name}< [3/3] - fetching repo topics ..."
|
35
|
+
topics = gh.repo_topics( full_name )
|
36
|
+
|
37
|
+
stats.update( repo,
|
38
|
+
commits: commits,
|
39
|
+
topics: topics )
|
40
|
+
stats.write
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end # module Hubba
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Hubba
|
2
|
+
|
3
|
+
|
4
|
+
###
|
5
|
+
## note: keep update traffic separate from update (basic) stats
|
6
|
+
## traffic stats require (personal access) token with push access!!
|
7
|
+
|
8
|
+
def self.update_traffic( hash_or_path='./repos.yml' ) ## move to reposet e.g. Reposet#update_status!!!!
|
9
|
+
h = if hash_or_path.is_a?( String ) ## assume it is a file path!!!
|
10
|
+
path = hash_or_path
|
11
|
+
YAML.load_file( path )
|
12
|
+
else
|
13
|
+
hash_or_path # assume its a hash / reposet already!!!
|
14
|
+
end
|
15
|
+
|
16
|
+
gh = Github.new
|
17
|
+
|
18
|
+
h.each do |org_with_counter,names|
|
19
|
+
|
20
|
+
## remove optional number from key e.g.
|
21
|
+
## mrhydescripts (3) => mrhydescripts
|
22
|
+
## footballjs (4) => footballjs
|
23
|
+
## etc.
|
24
|
+
org = org_with_counter.sub( /\([0-9]+\)/, '' ).strip
|
25
|
+
|
26
|
+
## puts " -- #{key_with_counter} [#{key}] --"
|
27
|
+
|
28
|
+
names.each do |name|
|
29
|
+
full_name = "#{org}/#{name}"
|
30
|
+
|
31
|
+
## puts " fetching stats #{count+1}/#{repo_count} - >#{full_name}<..."
|
32
|
+
stats = Stats.new( full_name )
|
33
|
+
stats.read
|
34
|
+
|
35
|
+
puts "update >#{full_name}< [1/4] - fetching repo traffic clones..."
|
36
|
+
clones = gh.repo_traffic_clones( full_name )
|
37
|
+
puts "update >#{full_name}< [2/4] - fetching repo traffic views..."
|
38
|
+
views = gh.repo_traffic_views( full_name )
|
39
|
+
puts "update >#{full_name}< [3/4] - fetching repo traffic popular paths..."
|
40
|
+
paths = gh.repo_traffic_popular_paths( full_name )
|
41
|
+
puts "update >#{full_name}< [4/4] - fetching repo traffic popular referrers..."
|
42
|
+
referrers = gh.repo_traffic_popular_referrers( full_name )
|
43
|
+
|
44
|
+
stats.update_traffic( clones: clones,
|
45
|
+
views: views,
|
46
|
+
paths: paths,
|
47
|
+
referrers: referrers )
|
48
|
+
stats.write
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end # module Hubba
|
data/lib/hubba/version.rb
CHANGED
data/lib/hubba.rb
CHANGED
@@ -22,12 +22,14 @@ end
|
|
22
22
|
# our own code
|
23
23
|
require 'hubba/version' # note: let version always go first
|
24
24
|
require 'hubba/config'
|
25
|
-
require 'hubba/client'
|
26
25
|
require 'hubba/github'
|
27
26
|
require 'hubba/stats'
|
28
27
|
|
29
28
|
## "higher level" porcelain services / helpers for easy (re)use
|
30
|
-
require 'hubba/
|
29
|
+
require 'hubba/folio' ## "access layer" for reports
|
30
|
+
require 'hubba/hubba'
|
31
|
+
require 'hubba/update'
|
32
|
+
require 'hubba/update_traffic'
|
31
33
|
|
32
34
|
require 'hubba/reports'
|
33
35
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hubba
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.6.
|
4
|
+
version: 0.6.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gerald Bauer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-10-
|
11
|
+
date: 2020-10-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: webclient
|
@@ -72,12 +72,14 @@ files:
|
|
72
72
|
- README.md
|
73
73
|
- Rakefile
|
74
74
|
- lib/hubba.rb
|
75
|
-
- lib/hubba/client.rb
|
76
75
|
- lib/hubba/config.rb
|
76
|
+
- lib/hubba/folio.rb
|
77
77
|
- lib/hubba/github.rb
|
78
|
+
- lib/hubba/hubba.rb
|
78
79
|
- lib/hubba/reports.rb
|
79
|
-
- lib/hubba/reposet.rb
|
80
80
|
- lib/hubba/stats.rb
|
81
|
+
- lib/hubba/update.rb
|
82
|
+
- lib/hubba/update_traffic.rb
|
81
83
|
- lib/hubba/version.rb
|
82
84
|
- test/helper.rb
|
83
85
|
- test/stats/jekyll~minima.json
|
data/lib/hubba/client.rb
DELETED
@@ -1,82 +0,0 @@
|
|
1
|
-
module Hubba
|
2
|
-
|
3
|
-
|
4
|
-
class Client
|
5
|
-
|
6
|
-
BASE_URL = 'https://api.github.com'
|
7
|
-
|
8
|
-
|
9
|
-
def initialize( token: nil,
|
10
|
-
user: nil, password: nil )
|
11
|
-
## add support for (personal access) token
|
12
|
-
@token = token
|
13
|
-
|
14
|
-
## add support for basic auth - defaults to no auth (nil/nil)
|
15
|
-
## remove - deprecated (use token) - why? why not?
|
16
|
-
@user = user ## use login like Oktokit - why? why not?
|
17
|
-
@password = password
|
18
|
-
end # method initialize
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
def get( request_uri )
|
23
|
-
puts "GET #{request_uri}"
|
24
|
-
|
25
|
-
## note: request_uri ALWAYS starts with leading /, thus use + for now!!!
|
26
|
-
# e.g. /users/geraldb
|
27
|
-
# /users/geraldb/repos
|
28
|
-
url = BASE_URL + request_uri
|
29
|
-
|
30
|
-
headers = {}
|
31
|
-
headers['User-Agent'] = 'ruby/hubba' ## required by GitHub API
|
32
|
-
headers['Accept'] = 'application/vnd.github.v3+json' ## recommend by GitHub API
|
33
|
-
|
34
|
-
auth = []
|
35
|
-
## check if credentials (user/password) present - if yes, use basic auth
|
36
|
-
if @token
|
37
|
-
puts " using (personal access) token - starting with: #{@token[0..6]}**********"
|
38
|
-
headers['Authorization'] = "token #{@token}"
|
39
|
-
## token works like:
|
40
|
-
## curl -H 'Authorization: token my_access_token' https://api.github.com/user/repos
|
41
|
-
elsif @user && @password
|
42
|
-
puts " using basic auth - user: #{@user}, password: ***"
|
43
|
-
## use credential auth "tuple" (that is, array with two string items) for now
|
44
|
-
## or use Webclient::HttpBasicAuth or something - why? why not?
|
45
|
-
auth = [@user, @password]
|
46
|
-
# req.basic_auth( @user, @password )
|
47
|
-
else
|
48
|
-
puts " using no credentials (no token, no user/password)"
|
49
|
-
end
|
50
|
-
|
51
|
-
res = Webclient.get( url,
|
52
|
-
headers: headers,
|
53
|
-
auth: auth )
|
54
|
-
|
55
|
-
# Get specific header
|
56
|
-
# response["content-type"]
|
57
|
-
# => "text/html; charset=UTF-8"
|
58
|
-
|
59
|
-
# Iterate all response headers.
|
60
|
-
# puts "HTTP HEADERS:"
|
61
|
-
# res.headers.each do |key, value|
|
62
|
-
# puts " #{key}: >#{value}<"
|
63
|
-
# end
|
64
|
-
# puts
|
65
|
-
|
66
|
-
# => "location => http://www.google.com/"
|
67
|
-
# => "content-type => text/html; charset=UTF-8"
|
68
|
-
# ...
|
69
|
-
|
70
|
-
if res.status.ok?
|
71
|
-
res.json
|
72
|
-
else
|
73
|
-
puts "!! HTTP ERROR: #{res.status.code} #{res.status.message}:"
|
74
|
-
pp res.raw
|
75
|
-
exit 1
|
76
|
-
end
|
77
|
-
end # methdo get
|
78
|
-
|
79
|
-
end ## class Client
|
80
|
-
|
81
|
-
|
82
|
-
end # module Hubba
|