gist 4.6.0 → 6.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 306aea3698bda142b6130a3160a10c7cfc816142
4
- data.tar.gz: b5cc50975bfa626326ac4c865f2d1849d3b95d50
2
+ SHA256:
3
+ metadata.gz: 78e3d009d0e3e3a535d20e8d7e0582705c6ea61308958c3f59a4d4a2e365fa3c
4
+ data.tar.gz: 23df778298090151ce7e2b8f3dbb8f97786bc7d576f218be1aeb454123090e91
5
5
  SHA512:
6
- metadata.gz: 4f65b5b05ce628788db1fc1324191fa51c56f39827474efb44c0cbcef690001a89be5626b490a0111989ccea4a882fcb2e1d360cc75742d58fb2846e285a2129
7
- data.tar.gz: 339e1387cb0f267a8a49e37963fc01fc353d6e77c7e1546ad029234c21672580ffac2ce0366e9443d22a020b9f92e5f4ac7c9cb30decc18ac3bb5ff5436fe3d6
6
+ metadata.gz: 70506cc45562b725085e2f10d5627a3ac4c700fa6e599e39a6625629fbd2c63903b2d42a4a95d9642f0ac01893bd8de27d72acb7e4622eaa44ab4ea96b08840c
7
+ data.tar.gz: b3476f46cb844603a169e3a67fae3bb37e5399494ddec6c78178f4bb664a90cda642c5be780ea007d12e96cc96312f695b86827872bc4947603c092a4e3d6774
data/README.md CHANGED
@@ -21,6 +21,11 @@ upload content to https://gist.github.com/.
21
21
 
22
22
  brew install gist
23
23
 
24
+ ‌For FreeBSD, gist lives in ports
25
+
26
+ pkg install gist
27
+
28
+
24
29
  ## Command
25
30
 
26
31
  ‌To upload the contents of `a.rb` just:
@@ -79,12 +84,29 @@ To read a gist and print it to STDOUT
79
84
 
80
85
  ## Login
81
86
 
82
- If you want to associate your gists with your GitHub account, you need to login
83
- with gist. It doesn't store your username and password, it just uses them to get
84
- an OAuth2 token (with the "gist" permission).
87
+ Before you use `gist` for the first time you will need to log in. There are two supported login flows:
88
+
89
+ 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.
90
+ 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`.
91
+
92
+ ### The device-code flow
93
+
94
+ 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.
85
95
 
86
96
  gist --login
87
- Obtaining OAuth2 access_token from github.
97
+ Requesting login parameters...
98
+ Please sign in at https://github.com/login/device
99
+ and enter code: XXXX-XXXX
100
+ Success! https://github.com/settings/connections/applications/4f7ec0d4eab38e74384e
101
+
102
+ 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.
103
+
104
+ ### The username-password flow
105
+
106
+ 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.
107
+
108
+ gist --login
109
+ Obtaining OAuth2 access_token from GitHub.
88
110
  GitHub username: ConradIrwin
89
111
  GitHub password:
90
112
  2-factor auth code:
@@ -94,9 +116,21 @@ This token is stored in `~/.gist` and used for all future gisting. If you need t
94
116
  you can revoke it from https://github.com/settings/tokens, or just delete the
95
117
  file.
96
118
 
97
- ‌After you've done this, you can still upload gists anonymously with `-a`.
119
+ #### Password-less login
120
+
121
+ If you have a complicated authorization requirement you can manually create a
122
+ token file by pasting a GitHub token with `gist` scope (and maybe the `user:email`
123
+ for GitHub Enterprise) into a file called `~/.gist`. You can create one from
124
+ https://github.com/settings/tokens
125
+
126
+ This file should contain only the token (~40 hex characters), and to make it
127
+ easier to edit, can optionally have a final newline (`\n` or `\r\n`).
128
+
129
+ For example, one way to create this file would be to run:
130
+
131
+ (umask 0077 && echo MY_SECRET_TOKEN > ~/.gist)
98
132
 
99
- gist -a a.rb
133
+ The `umask` ensures that the file is only accessible from your user account.
100
134
 
101
135
  ### GitHub Enterprise
102
136
 
@@ -106,10 +140,10 @@ you need to export the `GITHUB_URL` environment variable (usually done in your `
106
140
  export GITHUB_URL=http://github.internal.example.com/
107
141
 
108
142
  Once you've done this and restarted your terminal (or run `source ~/.bashrc`), gist will
109
- automatically use github enterprise instead of the public github.com
143
+ automatically use GitHub Enterprise instead of the public github.com
110
144
 
111
145
  Your token for GitHub Enterprise will be stored in `.gist.<protocol>.<server.name>[.<port>]` (e.g.
112
- `~.gist.http.github.internal.example.com` for the GITHUB_URL example above) instead of `~/.gist`.
146
+ `~/.gist.http.github.internal.example.com` for the GITHUB_URL example above) instead of `~/.gist`.
113
147
 
114
148
  If you have multiple servers or use Enterprise and public GitHub often, you can work around this by creating scripts
115
149
  that set the env var and then run `gist`. Keep in mind that to use the public GitHub you must unset the env var. Just
@@ -137,11 +171,10 @@ If you need more advanced features you can also pass:
137
171
  * `:public` if you want your gist to have a guessable url.
138
172
  * `:description` to add a description to your gist.
139
173
  * `:update` to update an existing gist (can be a URL or an id).
140
- * `:anonymous` to submit an anonymous gist (default is false).
141
174
  * `:copy` to copy the resulting URL to the clipboard (default is false).
142
175
  * `:open` to open the resulting URL in a browser (default is false).
143
176
 
144
- NOTE: The access_token must have the "gist" scope.
177
+ NOTE: The access_token must have the `gist` scope and may also require the `user:email` scope.
145
178
 
146
179
  ‌If you want to upload multiple files in the same gist, you can:
147
180
 
data/Rakefile CHANGED
@@ -23,9 +23,7 @@ task :standalone do
23
23
  f.puts "#!/usr/bin/env ruby"
24
24
  f.puts "# This is generated from https://github.com/defunkt/gist using 'rake standalone'"
25
25
  f.puts "# any changes will be overwritten."
26
- f.puts File.read("lib/gist.rb")
27
- .split("require 'json'\n").join(File.read("vendor/json.rb"))
28
- .split("require 'netrc'\n").join(File.read("vendor/netrc.rb"))
26
+ f.puts File.read("lib/gist.rb").split("require 'json'\n").join(File.read("vendor/json.rb"))
29
27
 
30
28
  f.puts File.read("bin/gist").gsub(/^require.*gist.*\n/, '');
31
29
  end
data/bin/gist CHANGED
@@ -24,16 +24,12 @@ specified STDIN will be read. The default filename for STDIN is "a.rb", and all
24
24
  filenames can be overridden by repeating the "-f" flag. The most useful reason
25
25
  to do this is to change the syntax highlighting.
26
26
 
27
- If you'd like your gists to be associated with your GitHub account, so that you
28
- can edit them and find them in future, first use `gist --login` to obtain an
29
- Oauth2 access token. This is stored and used by gist in the future.
27
+ All gists must to be associated with a GitHub account, so you will need to login with
28
+ `gist --login` to obtain an OAuth2 access token. This is stored and used by gist in the future.
30
29
 
31
30
  Private gists do not have guessable URLs and can be created with "-p", you can
32
31
  also set the description at the top of the gist by passing "-d".
33
32
 
34
- Anonymous gists are not associated with your GitHub account, they can be created
35
- with "-a" even after you have used "gist --login".
36
-
37
33
  If you would like to shorten the resulting gist URL, use the -s flag. This will
38
34
  use GitHub's URL shortener, git.io. You can also use -R to get the link to the
39
35
  raw gist.
@@ -47,7 +43,11 @@ Instead of creating a new gist, you can update an existing one by passing its ID
47
43
  or URL with "-u". For this to work, you must be logged in, and have created the
48
44
  original gist with the same GitHub account.
49
45
 
50
- Usage: #{executable_name} [-o|-c|-e] [-p] [-s] [-R] [-d DESC] [-a] [-u URL] [-P] [-f NAME|-t EXT]* FILE*
46
+ If you want to skip empty files, use the --skip-empty flag. If all files are
47
+ empty no gist will be created.
48
+
49
+ Usage: #{executable_name} [-o|-c|-e] [-p] [-s] [-R] [-d DESC] [-u URL]
50
+ [--skip-empty] [-P] [-f NAME|-t EXT]* FILE*
51
51
  #{executable_name} --login
52
52
  #{executable_name} [-l|-r]
53
53
 
@@ -88,10 +88,6 @@ Usage: #{executable_name} [-o|-c|-e] [-p] [-s] [-R] [-d DESC] [-a] [-u URL] [-P]
88
88
  options[:update] = update
89
89
  end
90
90
 
91
- opts.on("-a", "--anonymous", "Create an anonymous gist.") do
92
- options[:anonymous] = true
93
- end
94
-
95
91
  opts.on("-c", "--copy", "Copy the resulting URL to the clipboard") do
96
92
  options[:copy] = true
97
93
  end
@@ -107,6 +103,10 @@ Usage: #{executable_name} [-o|-c|-e] [-p] [-s] [-R] [-d DESC] [-a] [-u URL] [-P]
107
103
 
108
104
  opts.on("--no-open")
109
105
 
106
+ opts.on("--skip-empty", "Skip gisting empty files") do
107
+ options[:skip_empty] = true
108
+ end
109
+
110
110
  opts.on("-P", "--paste", "Paste from the clipboard to gist") do
111
111
  options[:paste] = true
112
112
  end
@@ -140,6 +140,12 @@ Usage: #{executable_name} [-o|-c|-e] [-p] [-s] [-R] [-d DESC] [-a] [-u URL] [-P]
140
140
  end.parse!
141
141
 
142
142
  begin
143
+ if Gist.auth_token.nil?
144
+ puts 'Please log in with `gist --login`. ' \
145
+ '(GitHub now requires credentials to gist https://bit.ly/2GBBxKw)'
146
+ exit(1)
147
+ end
148
+
143
149
  options[:output] = if options[:embed] && options[:shorten]
144
150
  raise Gist::Error, "--embed does not make sense with --shorten"
145
151
  elsif options[:embed]
@@ -195,7 +201,8 @@ begin
195
201
  end
196
202
  end
197
203
 
198
- puts Gist.multi_gist(files, options)
204
+ output = Gist.multi_gist(files, options)
205
+ puts output if output
199
206
  end
200
207
 
201
208
  rescue Gist::Error => e
@@ -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'
@@ -8,17 +8,11 @@ rescue LoadError
8
8
  require File.join File.dirname(File.dirname(__FILE__)), 'vendor', 'json.rb'
9
9
  end
10
10
 
11
- begin
12
- require 'netrc'
13
- rescue LoadError
14
- require File.join File.dirname(File.dirname(__FILE__)), 'vendor', 'netrc.rb'
15
- end
16
-
17
11
  # It just gists.
18
12
  module Gist
19
13
  extend self
20
14
 
21
- VERSION = '4.6.0'
15
+ VERSION = '6.0.0'
22
16
 
23
17
  # A list of clipboard commands with copy and paste support.
24
18
  CLIPBOARD_COMMANDS = {
@@ -29,12 +23,16 @@ module Gist
29
23
  }
30
24
 
31
25
  GITHUB_API_URL = URI("https://api.github.com/")
26
+ GITHUB_URL = URI("https://github.com/")
32
27
  GIT_IO_URL = URI("https://git.io")
33
28
 
34
29
  GITHUB_BASE_PATH = ""
35
30
  GHE_BASE_PATH = "/api/v3"
36
31
 
32
+ GITHUB_CLIENT_ID = '4f7ec0d4eab38e74384e'
33
+
37
34
  URL_ENV_NAME = "GITHUB_URL"
35
+ CLIENT_ID_ENV_NAME = "GIST_CLIENT_ID"
38
36
 
39
37
  USER_AGENT = "gist/#{VERSION} (Net::HTTP, #{RUBY_DESCRIPTION})"
40
38
 
@@ -50,7 +48,7 @@ module Gist
50
48
  module AuthTokenFile
51
49
  def self.filename
52
50
  if ENV.key?(URL_ENV_NAME)
53
- 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.-]/, '')}"
54
52
  else
55
53
  File.expand_path "~/.gist"
56
54
  end
@@ -71,7 +69,7 @@ module Gist
71
69
  #
72
70
  # @return [String] string value of access token or `nil`, if not found
73
71
  def auth_token
74
- @token ||= AuthTokenFile.read rescue nil || Netrc.read[self.api_url.host][1]
72
+ @token ||= AuthTokenFile.read rescue nil
75
73
  end
76
74
 
77
75
  # Upload a gist to https://gist.github.com
@@ -82,10 +80,14 @@ module Gist
82
80
  #
83
81
  # @see http://developer.github.com/v3/gists/
84
82
  def gist(content, options = {})
85
- filename = options[:filename] || "a.rb"
83
+ filename = options[:filename] || default_filename
86
84
  multi_gist({filename => content}, options)
87
85
  end
88
86
 
87
+ def default_filename
88
+ "gistfile1.txt"
89
+ end
90
+
89
91
  # Upload a gist to https://gist.github.com
90
92
  #
91
93
  # @param [Hash] files the code you'd like to gist: filename => content
@@ -98,6 +100,7 @@ module Gist
98
100
  # @option options [String] :update the URL or id of a gist to update
99
101
  # @option options [Boolean] :copy (false) Copy resulting URL to clipboard, if successful.
100
102
  # @option options [Boolean] :open (false) Open the resulting URL in a browser.
103
+ # @option options [Boolean] :skip_empty (false) Skip gisting empty files.
101
104
  # @option options [Symbol] :output (:all) The type of return value you'd like:
102
105
  # :html_url gives a String containing the url to the gist in a browser
103
106
  # :short_url gives a String contianing a git.io url that redirects to html_url
@@ -109,6 +112,13 @@ module Gist
109
112
  #
110
113
  # @see http://developer.github.com/v3/gists/
111
114
  def multi_gist(files, options={})
115
+ if options[:anonymous]
116
+ raise 'Anonymous gists are no longer supported. Please log in with `gist --login`. ' \
117
+ '(GitHub now requires credentials to gist https://bit.ly/2GBBxKw)'
118
+ else
119
+ access_token = (options[:access_token] || auth_token())
120
+ end
121
+
112
122
  json = {}
113
123
 
114
124
  json[:description] = options[:description] if options[:description]
@@ -116,22 +126,23 @@ module Gist
116
126
  json[:files] = {}
117
127
 
118
128
  files.each_pair do |(name, content)|
119
- raise "Cannot gist empty files" if content.to_s.strip == ""
120
- json[:files][File.basename(name)] = {:content => content}
129
+ if content.to_s.strip == ""
130
+ raise "Cannot gist empty files" unless options[:skip_empty]
131
+ else
132
+ name = name == "-" ? default_filename : File.basename(name)
133
+ json[:files][name] = {:content => content}
134
+ end
121
135
  end
122
136
 
137
+ return if json[:files].empty? && options[:skip_empty]
138
+
123
139
  existing_gist = options[:update].to_s.split("/").last
124
- if options[:anonymous]
125
- access_token = nil
126
- else
127
- access_token = (options[:access_token] || auth_token())
128
- end
129
140
 
130
141
  url = "#{base_path}/gists"
131
142
  url << "/" << CGI.escape(existing_gist) if existing_gist.to_s != ''
132
- url << "?access_token=" << CGI.escape(access_token) if access_token.to_s != ''
133
143
 
134
144
  request = Net::HTTP::Post.new(url)
145
+ request['Authorization'] = "token #{access_token}" if access_token.to_s != ''
135
146
  request.body = JSON.dump(json)
136
147
  request.content_type = 'application/json'
137
148
 
@@ -167,9 +178,10 @@ module Gist
167
178
  if user == ""
168
179
  access_token = auth_token()
169
180
  if access_token.to_s != ''
170
- url << "/gists?access_token=" << CGI.escape(access_token)
181
+ url << "/gists"
171
182
 
172
183
  request = Net::HTTP::Get.new(url)
184
+ request['Authorization'] = "token #{access_token}"
173
185
  response = http(api_url, request)
174
186
 
175
187
  pretty_gist(response)
@@ -192,24 +204,21 @@ module Gist
192
204
  url = "#{base_path}"
193
205
 
194
206
  if user == ""
195
- access_token = auth_token()
196
- if access_token.to_s != ''
197
- url << "/gists?per_page=100&access_token=" << CGI.escape(access_token)
198
- get_gist_pages(url)
199
- else
200
- raise Error, "Not authenticated. Use 'gist --login' to login or 'gist -l username' to view public gists."
201
- end
202
-
207
+ url << "/gists?per_page=100"
203
208
  else
204
209
  url << "/users/#{user}/gists?per_page=100"
205
- get_gist_pages(url)
206
210
  end
207
211
 
212
+ get_gist_pages(url, auth_token())
208
213
  end
209
214
 
210
215
  def read_gist(id, file_name=nil)
211
216
  url = "#{base_path}/gists/#{id}"
217
+
218
+ access_token = auth_token()
219
+
212
220
  request = Net::HTTP::Get.new(url)
221
+ request['Authorization'] = "token #{access_token}" if access_token.to_s != ''
213
222
  response = http(api_url, request)
214
223
 
215
224
  if response.code == '200'
@@ -235,9 +244,8 @@ module Gist
235
244
 
236
245
  access_token = auth_token()
237
246
  if access_token.to_s != ''
238
- url << "?access_token=" << CGI.escape(access_token)
239
-
240
247
  request = Net::HTTP::Delete.new(url)
248
+ request["Authorization"] = "token #{access_token}"
241
249
  response = http(api_url, request)
242
250
  else
243
251
  raise Error, "Not authenticated. Use 'gist --login' to login."
@@ -250,9 +258,10 @@ module Gist
250
258
  end
251
259
  end
252
260
 
253
- def get_gist_pages(url)
261
+ def get_gist_pages(url, access_token = "")
254
262
 
255
263
  request = Net::HTTP::Get.new(url)
264
+ request['Authorization'] = "token #{access_token}" if access_token.to_s != ''
256
265
  response = http(api_url, request)
257
266
  pretty_gist(response)
258
267
 
@@ -260,7 +269,7 @@ module Gist
260
269
 
261
270
  if link_header
262
271
  links = Hash[ link_header.gsub(/(<|>|")/, "").split(',').map { |link| link.split('; rel=') } ].invert
263
- get_gist_pages(links['next']) if links['next']
272
+ get_gist_pages(links['next'], access_token) if links['next']
264
273
  end
265
274
 
266
275
  end
@@ -324,16 +333,72 @@ module Gist
324
333
 
325
334
  # Log the user into gist.
326
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
+ #
327
395
  # This method asks the user for a username and password, and tries to obtain
328
396
  # and OAuth2 access token, which is then stored in ~/.gist
329
397
  #
330
398
  # @raise [Gist::Error] if something went wrong
331
- # @param [Hash] credentials login details
332
- # @option credentials [String] :username
333
- # @option credentials [String] :password
334
399
  # @see http://developer.github.com/v3/oauth/
335
- def login!(credentials={})
336
- puts "Obtaining OAuth2 access_token from github."
400
+ def access_token_login!(credentials={})
401
+ puts "Obtaining OAuth2 access_token from GitHub."
337
402
  loop do
338
403
  print "GitHub username: "
339
404
  username = credentials[:username] || $stdin.gets.strip
@@ -389,7 +454,11 @@ module Gist
389
454
  env = ENV['http_proxy'] || ENV['HTTP_PROXY']
390
455
  connection = if env
391
456
  proxy = URI(env)
392
- 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
393
462
  else
394
463
  Net::HTTP.new(uri.host, uri.port)
395
464
  end
@@ -543,11 +612,19 @@ Could not find copy command, tried:
543
612
  ENV.key?(URL_ENV_NAME) ? GHE_BASE_PATH : GITHUB_BASE_PATH
544
613
  end
545
614
 
615
+ def login_url
616
+ ENV.key?(URL_ENV_NAME) ? URI(ENV[URL_ENV_NAME]) : GITHUB_URL
617
+ end
618
+
546
619
  # Get the API URL
547
620
  def api_url
548
621
  ENV.key?(URL_ENV_NAME) ? URI(ENV[URL_ENV_NAME]) : GITHUB_API_URL
549
622
  end
550
623
 
624
+ def client_id
625
+ ENV.key?(CLIENT_ID_ENV_NAME) ? URI(ENV[CLIENT_ID_ENV_NAME]) : GITHUB_CLIENT_ID
626
+ end
627
+
551
628
  def legacy_private_gister?
552
629
  return unless which('git')
553
630
  `git config --global gist.private` =~ /\Ayes|1|true|on\z/i