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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b81d6d7f5378a97ea2110a82b03f703ef3e288ac038c233428e0413149b5bb34
4
- data.tar.gz: 2f01fcdd837e72af4a7ed7c53120b1605ad73fd15d15a4e8d4dd709d43775f7e
3
+ metadata.gz: a81ce3967b1095ead6ba1ca877a9c3f6de348de6a30b0f3d43489bbde1e82d2f
4
+ data.tar.gz: adc00b10a3514227ecfe8250a0a22af8df929e3cbffec78d33b0ec3cbf52dc2c
5
5
  SHA512:
6
- metadata.gz: b5c0ae73ff4b52e917a485ad0e95b4a5520ba8212b39557a82ccc3cc8bf309a34ddfceedc67fe24222069ffd485fa857776238b05a57fc0c52ec7a215c6ca297
7
- data.tar.gz: 9ded7356c613f6130bbb9b3c8859723c12a0c897073c6c6149c37038d3d3dd285a98dd2b773ba7dc036ba520e4b8440eec76c9468a22ff8a0213e70e0d8ddc52
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
- If you want to associate your gists with your GitHub account, you need to login
88
- with gist. It doesn't store your username and password, it just uses them to get
89
- an OAuth2 token (with the "gist" permission).
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 only the `gist` permission into a
106
- file called `~/.gist`. You can create one from https://github.com/settings/tokens
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 "gist" scope.
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
@@ -173,7 +173,8 @@ begin
173
173
 
174
174
  if options.key? :read
175
175
  file_name = ARGV.first
176
- Gist.read_gist(options[:read], file_name)
176
+ output = Gist.read_gist(options[:read], file_name)
177
+ puts output if output
177
178
  exit
178
179
  end
179
180
 
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 = '5.1.0'
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 contianing a git.io url that redirects to html_url
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
- access_token = auth_token()
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
- puts file["content"]
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 login!(credentials={})
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
- Net::HTTP::Proxy(proxy.host, proxy.port).new(uri.host, uri.port)
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::VERIFY_NONE
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}://#{MOCK_USER}:#{MOCK_PASSWORD}@#{MOCK_GHE_HOST}/api/v3/"
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, /#{MOCK_AUTHZ_GITHUB_URL}authorizations/).
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, /#{MOCK_AUTHZ_GITHUB_URL}authorizations/)
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, /#{MOCK_AUTHZ_GITHUB_URL}authorizations/)
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, horicontal tab, lf, cr
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 matchin class and create_id was found. This option
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
- # Ouputs _objs_ to STDOUT as JSON strings in a pretty format, with
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: 5.1.0
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: 2020-02-07 00:00:00.000000000 Z
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
data/.gitignore DELETED
@@ -1,8 +0,0 @@
1
- .rvmrc
2
- Gemfile.lock
3
-
4
- # OS X
5
- .DS_Store
6
- .yardoc
7
- doc
8
- *.gem