gist 5.1.0 → 6.1.0
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/README.md +29 -6
- data/bin/gist +2 -1
- data/gist.gemspec +1 -1
- data/lib/gist.rb +86 -21
- data/spec/ghe_spec.rb +12 -5
- data/vendor/json.rb +3 -3
- metadata +3 -6
- data/.gitignore +0 -8
- data/build/gist +0 -2091
- data/build/gist.1 +0 -277
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a81ce3967b1095ead6ba1ca877a9c3f6de348de6a30b0f3d43489bbde1e82d2f
|
|
4
|
+
data.tar.gz: adc00b10a3514227ecfe8250a0a22af8df929e3cbffec78d33b0ec3cbf52dc2c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 450ee25c8f289acb02aa085ffe140801ed32ead7679f98d2e426315e2586c63224304dc4660820a80d75c3c6b011794cc53b6815d8754bddf4ba592b950197e7
|
|
7
|
+
data.tar.gz: 5831bd45e6d92a280e91ff4c7b28b635920480de9c663d473b37d3510cb5fe881d47583b976a5d7531e54df00cfdfa42ffe44ccb3be55ffb0f4829b38a2068ab
|
data/README.md
CHANGED
|
@@ -25,6 +25,11 @@ upload content to https://gist.github.com/.
|
|
|
25
25
|
|
|
26
26
|
pkg install gist
|
|
27
27
|
|
|
28
|
+
For Ubuntu/Debian
|
|
29
|
+
|
|
30
|
+
apt install gist
|
|
31
|
+
|
|
32
|
+
Note: Debian renames the binary to `gist-paste` to avoid a name conflict.
|
|
28
33
|
|
|
29
34
|
## Command
|
|
30
35
|
|
|
@@ -84,9 +89,26 @@ To read a gist and print it to STDOUT
|
|
|
84
89
|
|
|
85
90
|
## Login
|
|
86
91
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
an
|
|
92
|
+
Before you use `gist` for the first time you will need to log in. There are two supported login flows:
|
|
93
|
+
|
|
94
|
+
1. The Github device-code Oauth flow. This is the default for authenticating to github.com, and can be enabled for Github Enterprise by creating an Oauth app, and exporting the environment variable `GIST_CLIENT_ID` with the client id of the Oauth app.
|
|
95
|
+
2. The (deprecated) username and password token exchange flow. This is the default for GitHub Enterprise, and can be used to log into github.com by exporting the environment variable `GIST_USE_USERNAME_AND_PASSWORD`.
|
|
96
|
+
|
|
97
|
+
### The device-code flow
|
|
98
|
+
|
|
99
|
+
This flow allows you to obtain a token by logging into GitHub in the browser and typing a verification code. This is the preferred mechanism.
|
|
100
|
+
|
|
101
|
+
gist --login
|
|
102
|
+
Requesting login parameters...
|
|
103
|
+
Please sign in at https://github.com/login/device
|
|
104
|
+
and enter code: XXXX-XXXX
|
|
105
|
+
Success! https://github.com/settings/connections/applications/4f7ec0d4eab38e74384e
|
|
106
|
+
|
|
107
|
+
The returned access_token is stored in `~/.gist` and used for all future gisting. If you need to you can revoke access from https://github.com/settings/connections/applications/4f7ec0d4eab38e74384e.
|
|
108
|
+
|
|
109
|
+
### The username-password flow
|
|
110
|
+
|
|
111
|
+
This flow asks for your GitHub username and password (and 2FA code), and exchanges them for a token with the "gist" permission (your username and password are not stored). This mechanism is deprecated by GitHub, but may still work with GitHub Enterprise.
|
|
90
112
|
|
|
91
113
|
gist --login
|
|
92
114
|
Obtaining OAuth2 access_token from GitHub.
|
|
@@ -102,8 +124,9 @@ file.
|
|
|
102
124
|
#### Password-less login
|
|
103
125
|
|
|
104
126
|
If you have a complicated authorization requirement you can manually create a
|
|
105
|
-
token file by pasting a GitHub token with
|
|
106
|
-
file called `~/.gist`. You can create one from
|
|
127
|
+
token file by pasting a GitHub token with `gist` scope (and maybe the `user:email`
|
|
128
|
+
for GitHub Enterprise) into a file called `~/.gist`. You can create one from
|
|
129
|
+
https://github.com/settings/tokens
|
|
107
130
|
|
|
108
131
|
This file should contain only the token (~40 hex characters), and to make it
|
|
109
132
|
easier to edit, can optionally have a final newline (`\n` or `\r\n`).
|
|
@@ -156,7 +179,7 @@ If you need more advanced features you can also pass:
|
|
|
156
179
|
* `:copy` to copy the resulting URL to the clipboard (default is false).
|
|
157
180
|
* `:open` to open the resulting URL in a browser (default is false).
|
|
158
181
|
|
|
159
|
-
NOTE: The access_token must have the
|
|
182
|
+
NOTE: The access_token must have the `gist` scope and may also require the `user:email` scope.
|
|
160
183
|
|
|
161
184
|
If you want to upload multiple files in the same gist, you can:
|
|
162
185
|
|
data/bin/gist
CHANGED
data/gist.gemspec
CHANGED
|
@@ -9,7 +9,7 @@ Gem::Specification.new do |s|
|
|
|
9
9
|
s.email = ['conrad.irwin@gmail.com', 'rkingist@sharpsaw.org']
|
|
10
10
|
s.authors = ['Conrad Irwin', '☈king']
|
|
11
11
|
s.license = 'MIT'
|
|
12
|
-
s.files = `git ls-files`.split("\n")
|
|
12
|
+
s.files = `git ls-files`.split("\n") - Dir.glob("build/*") - [".gitignore"]
|
|
13
13
|
s.require_paths = ["lib"]
|
|
14
14
|
|
|
15
15
|
s.executables << 'gist'
|
data/lib/gist.rb
CHANGED
|
@@ -12,7 +12,7 @@ end
|
|
|
12
12
|
module Gist
|
|
13
13
|
extend self
|
|
14
14
|
|
|
15
|
-
VERSION = '
|
|
15
|
+
VERSION = '6.1.0'
|
|
16
16
|
|
|
17
17
|
# A list of clipboard commands with copy and paste support.
|
|
18
18
|
CLIPBOARD_COMMANDS = {
|
|
@@ -23,12 +23,16 @@ module Gist
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
GITHUB_API_URL = URI("https://api.github.com/")
|
|
26
|
+
GITHUB_URL = URI("https://github.com/")
|
|
26
27
|
GIT_IO_URL = URI("https://git.io")
|
|
27
28
|
|
|
28
29
|
GITHUB_BASE_PATH = ""
|
|
29
30
|
GHE_BASE_PATH = "/api/v3"
|
|
30
31
|
|
|
32
|
+
GITHUB_CLIENT_ID = '4f7ec0d4eab38e74384e'
|
|
33
|
+
|
|
31
34
|
URL_ENV_NAME = "GITHUB_URL"
|
|
35
|
+
CLIENT_ID_ENV_NAME = "GIST_CLIENT_ID"
|
|
32
36
|
|
|
33
37
|
USER_AGENT = "gist/#{VERSION} (Net::HTTP, #{RUBY_DESCRIPTION})"
|
|
34
38
|
|
|
@@ -44,7 +48,7 @@ module Gist
|
|
|
44
48
|
module AuthTokenFile
|
|
45
49
|
def self.filename
|
|
46
50
|
if ENV.key?(URL_ENV_NAME)
|
|
47
|
-
File.expand_path "~/.gist.#{ENV[URL_ENV_NAME].gsub(/:/, '.').gsub(/[^a-z0-9
|
|
51
|
+
File.expand_path "~/.gist.#{ENV[URL_ENV_NAME].gsub(/:/, '.').gsub(/[^a-z0-9.-]/, '')}"
|
|
48
52
|
else
|
|
49
53
|
File.expand_path "~/.gist"
|
|
50
54
|
end
|
|
@@ -99,7 +103,7 @@ module Gist
|
|
|
99
103
|
# @option options [Boolean] :skip_empty (false) Skip gisting empty files.
|
|
100
104
|
# @option options [Symbol] :output (:all) The type of return value you'd like:
|
|
101
105
|
# :html_url gives a String containing the url to the gist in a browser
|
|
102
|
-
# :short_url gives a String
|
|
106
|
+
# :short_url gives a String containing a git.io url that redirects to html_url
|
|
103
107
|
# :javascript gives a String containing a script tag suitable for embedding the gist
|
|
104
108
|
# :all gives a Hash containing the parsed json response from the server
|
|
105
109
|
#
|
|
@@ -200,25 +204,18 @@ module Gist
|
|
|
200
204
|
url = "#{base_path}"
|
|
201
205
|
|
|
202
206
|
if user == ""
|
|
203
|
-
|
|
204
|
-
if access_token.to_s != ''
|
|
205
|
-
url << "/gists?per_page=100"
|
|
206
|
-
get_gist_pages(url, access_token)
|
|
207
|
-
else
|
|
208
|
-
raise Error, "Not authenticated. Use 'gist --login' to login or 'gist -l username' to view public gists."
|
|
209
|
-
end
|
|
210
|
-
|
|
207
|
+
url << "/gists?per_page=100"
|
|
211
208
|
else
|
|
212
209
|
url << "/users/#{user}/gists?per_page=100"
|
|
213
|
-
get_gist_pages(url)
|
|
214
210
|
end
|
|
215
211
|
|
|
212
|
+
get_gist_pages(url, auth_token())
|
|
216
213
|
end
|
|
217
214
|
|
|
218
|
-
def read_gist(id, file_name=nil)
|
|
215
|
+
def read_gist(id, file_name=nil, options={})
|
|
219
216
|
url = "#{base_path}/gists/#{id}"
|
|
220
217
|
|
|
221
|
-
access_token = auth_token()
|
|
218
|
+
access_token = (options[:access_token] || auth_token())
|
|
222
219
|
|
|
223
220
|
request = Net::HTTP::Get.new(url)
|
|
224
221
|
request['Authorization'] = "token #{access_token}" if access_token.to_s != ''
|
|
@@ -235,7 +232,7 @@ module Gist
|
|
|
235
232
|
file = files.values.first
|
|
236
233
|
end
|
|
237
234
|
|
|
238
|
-
|
|
235
|
+
file["content"]
|
|
239
236
|
else
|
|
240
237
|
raise Error, "Gist with id of #{id} does not exist."
|
|
241
238
|
end
|
|
@@ -336,15 +333,71 @@ module Gist
|
|
|
336
333
|
|
|
337
334
|
# Log the user into gist.
|
|
338
335
|
#
|
|
336
|
+
def login!(credentials={})
|
|
337
|
+
if (login_url == GITHUB_URL || ENV.key?(CLIENT_ID_ENV_NAME)) && credentials.empty? && !ENV.key?('GIST_USE_USERNAME_AND_PASSWORD')
|
|
338
|
+
device_flow_login!
|
|
339
|
+
else
|
|
340
|
+
access_token_login!(credentials)
|
|
341
|
+
end
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
def device_flow_login!
|
|
345
|
+
puts "Requesting login parameters..."
|
|
346
|
+
request = Net::HTTP::Post.new("/login/device/code")
|
|
347
|
+
request.body = JSON.dump({
|
|
348
|
+
:scope => 'gist',
|
|
349
|
+
:client_id => client_id,
|
|
350
|
+
})
|
|
351
|
+
request.content_type = 'application/json'
|
|
352
|
+
request['accept'] = "application/json"
|
|
353
|
+
response = http(login_url, request)
|
|
354
|
+
|
|
355
|
+
if response.code != '200'
|
|
356
|
+
raise Error, "HTTP #{response.code}: #{response.body}"
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
body = JSON.parse(response.body)
|
|
360
|
+
|
|
361
|
+
puts "Please sign in at #{body['verification_uri']}"
|
|
362
|
+
puts " and enter code: #{body['user_code']}"
|
|
363
|
+
device_code = body['device_code']
|
|
364
|
+
interval = body['interval']
|
|
365
|
+
|
|
366
|
+
loop do
|
|
367
|
+
sleep(interval.to_i)
|
|
368
|
+
request = Net::HTTP::Post.new("/login/oauth/access_token")
|
|
369
|
+
request.body = JSON.dump({
|
|
370
|
+
:client_id => client_id,
|
|
371
|
+
:grant_type => 'urn:ietf:params:oauth:grant-type:device_code',
|
|
372
|
+
:device_code => device_code
|
|
373
|
+
})
|
|
374
|
+
request.content_type = 'application/json'
|
|
375
|
+
request['Accept'] = 'application/json'
|
|
376
|
+
response = http(login_url, request)
|
|
377
|
+
if response.code != '200'
|
|
378
|
+
raise Error, "HTTP #{response.code}: #{response.body}"
|
|
379
|
+
end
|
|
380
|
+
body = JSON.parse(response.body)
|
|
381
|
+
break unless body['error'] == 'authorization_pending'
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
if body['error']
|
|
385
|
+
raise Error, body['error_description']
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
AuthTokenFile.write JSON.parse(response.body)['access_token']
|
|
389
|
+
|
|
390
|
+
puts "Success! #{ENV[URL_ENV_NAME] || "https://github.com/"}settings/connections/applications/#{client_id}"
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
# Logs the user into gist.
|
|
394
|
+
#
|
|
339
395
|
# This method asks the user for a username and password, and tries to obtain
|
|
340
396
|
# and OAuth2 access token, which is then stored in ~/.gist
|
|
341
397
|
#
|
|
342
398
|
# @raise [Gist::Error] if something went wrong
|
|
343
|
-
# @param [Hash] credentials login details
|
|
344
|
-
# @option credentials [String] :username
|
|
345
|
-
# @option credentials [String] :password
|
|
346
399
|
# @see http://developer.github.com/v3/oauth/
|
|
347
|
-
def
|
|
400
|
+
def access_token_login!(credentials={})
|
|
348
401
|
puts "Obtaining OAuth2 access_token from GitHub."
|
|
349
402
|
loop do
|
|
350
403
|
print "GitHub username: "
|
|
@@ -401,13 +454,17 @@ module Gist
|
|
|
401
454
|
env = ENV['http_proxy'] || ENV['HTTP_PROXY']
|
|
402
455
|
connection = if env
|
|
403
456
|
proxy = URI(env)
|
|
404
|
-
|
|
457
|
+
if proxy.user
|
|
458
|
+
Net::HTTP::Proxy(proxy.host, proxy.port, proxy.user, proxy.password).new(uri.host, uri.port)
|
|
459
|
+
else
|
|
460
|
+
Net::HTTP::Proxy(proxy.host, proxy.port).new(uri.host, uri.port)
|
|
461
|
+
end
|
|
405
462
|
else
|
|
406
463
|
Net::HTTP.new(uri.host, uri.port)
|
|
407
464
|
end
|
|
408
465
|
if uri.scheme == "https"
|
|
409
466
|
connection.use_ssl = true
|
|
410
|
-
connection.verify_mode = OpenSSL::SSL::
|
|
467
|
+
connection.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
|
411
468
|
end
|
|
412
469
|
connection.open_timeout = 10
|
|
413
470
|
connection.read_timeout = 10
|
|
@@ -555,11 +612,19 @@ Could not find copy command, tried:
|
|
|
555
612
|
ENV.key?(URL_ENV_NAME) ? GHE_BASE_PATH : GITHUB_BASE_PATH
|
|
556
613
|
end
|
|
557
614
|
|
|
615
|
+
def login_url
|
|
616
|
+
ENV.key?(URL_ENV_NAME) ? URI(ENV[URL_ENV_NAME]) : GITHUB_URL
|
|
617
|
+
end
|
|
618
|
+
|
|
558
619
|
# Get the API URL
|
|
559
620
|
def api_url
|
|
560
621
|
ENV.key?(URL_ENV_NAME) ? URI(ENV[URL_ENV_NAME]) : GITHUB_API_URL
|
|
561
622
|
end
|
|
562
623
|
|
|
624
|
+
def client_id
|
|
625
|
+
ENV.key?(CLIENT_ID_ENV_NAME) ? URI(ENV[CLIENT_ID_ENV_NAME]) : GITHUB_CLIENT_ID
|
|
626
|
+
end
|
|
627
|
+
|
|
563
628
|
def legacy_private_gister?
|
|
564
629
|
return unless which('git')
|
|
565
630
|
`git config --global gist.private` =~ /\Ayes|1|true|on\z/i
|
data/spec/ghe_spec.rb
CHANGED
|
@@ -5,10 +5,10 @@ describe '...' do
|
|
|
5
5
|
MOCK_USER = 'foo'
|
|
6
6
|
MOCK_PASSWORD = 'bar'
|
|
7
7
|
|
|
8
|
-
MOCK_AUTHZ_GHE_URL = "#{MOCK_GHE_PROTOCOL}://#{
|
|
8
|
+
MOCK_AUTHZ_GHE_URL = "#{MOCK_GHE_PROTOCOL}://#{MOCK_GHE_HOST}/api/v3/"
|
|
9
9
|
MOCK_GHE_URL = "#{MOCK_GHE_PROTOCOL}://#{MOCK_GHE_HOST}/api/v3/"
|
|
10
|
-
MOCK_AUTHZ_GITHUB_URL = "https://#{MOCK_USER}:#{MOCK_PASSWORD}@api.github.com/"
|
|
11
10
|
MOCK_GITHUB_URL = "https://api.github.com/"
|
|
11
|
+
MOCK_LOGIN_URL = "https://github.com/"
|
|
12
12
|
|
|
13
13
|
before do
|
|
14
14
|
@saved_env = ENV[Gist::URL_ENV_NAME]
|
|
@@ -20,8 +20,15 @@ describe '...' do
|
|
|
20
20
|
# stub requests for /authorizations
|
|
21
21
|
stub_request(:post, /#{MOCK_AUTHZ_GHE_URL}authorizations/).
|
|
22
22
|
to_return(:status => 201, :body => '{"token": "asdf"}')
|
|
23
|
-
stub_request(:post, /#{
|
|
23
|
+
stub_request(:post, /#{MOCK_GITHUB_URL}authorizations/).
|
|
24
|
+
with(headers: {'Authorization'=>'Basic Zm9vOmJhcg=='}).
|
|
24
25
|
to_return(:status => 201, :body => '{"token": "asdf"}')
|
|
26
|
+
|
|
27
|
+
stub_request(:post, /#{MOCK_LOGIN_URL}login\/device\/code/).
|
|
28
|
+
to_return(:status => 200, :body => '{"interval": "0.1", "user_code":"XXXX-XXXX", "device_code": "xxxx", "verification_uri": "https://github.com/login/device"}')
|
|
29
|
+
|
|
30
|
+
stub_request(:post, /#{MOCK_LOGIN_URL}login\/oauth\/access_token/).
|
|
31
|
+
to_return(:status => 200, :body => '{"access_token":"zzzz"}')
|
|
25
32
|
end
|
|
26
33
|
|
|
27
34
|
after do
|
|
@@ -48,7 +55,7 @@ describe '...' do
|
|
|
48
55
|
|
|
49
56
|
Gist.login!
|
|
50
57
|
|
|
51
|
-
assert_requested(:post, /#{
|
|
58
|
+
assert_requested(:post, /#{MOCK_LOGIN_URL}login\/oauth\/access_token/)
|
|
52
59
|
end
|
|
53
60
|
|
|
54
61
|
it "should access to #{MOCK_GHE_HOST} when $#{Gist::URL_ENV_NAME} was set" do
|
|
@@ -65,7 +72,7 @@ describe '...' do
|
|
|
65
72
|
$stdin = StringIO.new "#{MOCK_USER}_wrong\n#{MOCK_PASSWORD}_wrong\n"
|
|
66
73
|
Gist.login! :username => MOCK_USER, :password => MOCK_PASSWORD
|
|
67
74
|
|
|
68
|
-
assert_requested(:post, /#{
|
|
75
|
+
assert_requested(:post, /#{MOCK_GITHUB_URL}authorizations/)
|
|
69
76
|
end
|
|
70
77
|
|
|
71
78
|
end
|
data/vendor/json.rb
CHANGED
|
@@ -44,7 +44,7 @@ module JSON
|
|
|
44
44
|
/(?=\*/) # single slash before this comment's end
|
|
45
45
|
)*
|
|
46
46
|
\*/ # the End of this comment
|
|
47
|
-
|[ \t\r\n]+ # whitespaces: space,
|
|
47
|
+
|[ \t\r\n]+ # whitespaces: space, horizontal tab, lf, cr
|
|
48
48
|
)+
|
|
49
49
|
)mx
|
|
50
50
|
|
|
@@ -64,7 +64,7 @@ module JSON
|
|
|
64
64
|
# (keys) in a JSON object. Otherwise strings are returned, which is also
|
|
65
65
|
# the default.
|
|
66
66
|
# * *create_additions*: If set to false, the Parser doesn't create
|
|
67
|
-
# additions even if a
|
|
67
|
+
# additions even if a matching class and create_id was found. This option
|
|
68
68
|
# defaults to true.
|
|
69
69
|
# * *object_class*: Defaults to Hash
|
|
70
70
|
# * *array_class*: Defaults to Array
|
|
@@ -1265,7 +1265,7 @@ module ::Kernel
|
|
|
1265
1265
|
nil
|
|
1266
1266
|
end
|
|
1267
1267
|
|
|
1268
|
-
#
|
|
1268
|
+
# Outputs _objs_ to STDOUT as JSON strings in a pretty format, with
|
|
1269
1269
|
# indentation and over many lines.
|
|
1270
1270
|
def jj(*objs)
|
|
1271
1271
|
objs.each do |obj|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: gist
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 6.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Conrad Irwin
|
|
@@ -9,7 +9,7 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date:
|
|
12
|
+
date: 2026-06-19 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: rake
|
|
@@ -76,15 +76,12 @@ executables:
|
|
|
76
76
|
extensions: []
|
|
77
77
|
extra_rdoc_files: []
|
|
78
78
|
files:
|
|
79
|
-
- ".gitignore"
|
|
80
79
|
- ".rspec"
|
|
81
80
|
- Gemfile
|
|
82
81
|
- LICENSE.MIT
|
|
83
82
|
- README.md
|
|
84
83
|
- Rakefile
|
|
85
84
|
- bin/gist
|
|
86
|
-
- build/gist
|
|
87
|
-
- build/gist.1
|
|
88
85
|
- gist.gemspec
|
|
89
86
|
- lib/gist.rb
|
|
90
87
|
- spec/auth_token_file_spec.rb
|
|
@@ -115,7 +112,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
115
112
|
- !ruby/object:Gem::Version
|
|
116
113
|
version: '0'
|
|
117
114
|
requirements: []
|
|
118
|
-
rubygems_version: 3.0.3
|
|
115
|
+
rubygems_version: 3.0.3.1
|
|
119
116
|
signing_key:
|
|
120
117
|
specification_version: 4
|
|
121
118
|
summary: Just allows you to upload gists
|