gist 4.5.0 → 6.0.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 +5 -5
- data/README.md +75 -23
- data/bin/gist +28 -12
- data/gist.gemspec +1 -1
- data/lib/gist.rb +140 -34
- data/spec/auth_token_file_spec.rb +2 -2
- data/spec/ghe_spec.rb +12 -5
- data/spec/rawify_spec.rb +10 -1
- data/spec/shorten_spec.rb +17 -4
- data/spec/spec_helper.rb +1 -1
- metadata +3 -8
- data/.gitignore +0 -8
- data/build/gist +0 -2034
- data/build/gist.1 +0 -230
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 78e3d009d0e3e3a535d20e8d7e0582705c6ea61308958c3f59a4d4a2e365fa3c
|
4
|
+
data.tar.gz: 23df778298090151ce7e2b8f3dbb8f97786bc7d576f218be1aeb454123090e91
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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:
|
@@ -70,30 +75,88 @@ upload content to https://gist.github.com/.
|
|
70
75
|
gist -l : all gists for authed user
|
71
76
|
gist -l defunkt : list defunkt's public gists
|
72
77
|
|
78
|
+
To read a gist and print it to STDOUT
|
79
|
+
|
80
|
+
gist -r GIST_ID
|
81
|
+
gist -r 374130
|
82
|
+
|
73
83
|
See `gist --help` for more detail.
|
74
84
|
|
75
85
|
## Login
|
76
86
|
|
77
|
-
|
78
|
-
|
79
|
-
an
|
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.
|
80
95
|
|
81
96
|
gist --login
|
82
|
-
|
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.
|
83
110
|
GitHub username: ConradIrwin
|
84
111
|
GitHub password:
|
85
112
|
2-factor auth code:
|
86
|
-
Success! https://github.com/settings/
|
113
|
+
Success! https://github.com/settings/tokens
|
87
114
|
|
88
115
|
This token is stored in `~/.gist` and used for all future gisting. If you need to
|
89
|
-
you can revoke it from https://github.com/settings/
|
90
|
-
file.
|
91
|
-
|
92
|
-
|
116
|
+
you can revoke it from https://github.com/settings/tokens, or just delete the
|
117
|
+
file.
|
118
|
+
|
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`).
|
93
128
|
|
94
|
-
|
129
|
+
For example, one way to create this file would be to run:
|
95
130
|
|
96
|
-
|
131
|
+
(umask 0077 && echo MY_SECRET_TOKEN > ~/.gist)
|
132
|
+
|
133
|
+
The `umask` ensures that the file is only accessible from your user account.
|
134
|
+
|
135
|
+
### GitHub Enterprise
|
136
|
+
|
137
|
+
If you'd like `gist` to use your locally installed [GitHub Enterprise](https://enterprise.github.com/),
|
138
|
+
you need to export the `GITHUB_URL` environment variable (usually done in your `~/.bashrc`).
|
139
|
+
|
140
|
+
export GITHUB_URL=http://github.internal.example.com/
|
141
|
+
|
142
|
+
Once you've done this and restarted your terminal (or run `source ~/.bashrc`), gist will
|
143
|
+
automatically use GitHub Enterprise instead of the public github.com
|
144
|
+
|
145
|
+
Your token for GitHub Enterprise will be stored in `.gist.<protocol>.<server.name>[.<port>]` (e.g.
|
146
|
+
`~/.gist.http.github.internal.example.com` for the GITHUB_URL example above) instead of `~/.gist`.
|
147
|
+
|
148
|
+
If you have multiple servers or use Enterprise and public GitHub often, you can work around this by creating scripts
|
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
|
150
|
+
setting it to the public URL will not work. Use `unset GITHUB_URL`
|
151
|
+
|
152
|
+
### Token file format
|
153
|
+
|
154
|
+
If you cannot use passwords, as most Enterprise installations do, you can generate the token via the web interface
|
155
|
+
and then simply save the string in the correct file. Avoid line breaks or you might see:
|
156
|
+
```
|
157
|
+
$ gist -l
|
158
|
+
Error: Bad credentials
|
159
|
+
```
|
97
160
|
|
98
161
|
# Library
|
99
162
|
|
@@ -108,11 +171,10 @@ If you need more advanced features you can also pass:
|
|
108
171
|
* `:public` if you want your gist to have a guessable url.
|
109
172
|
* `:description` to add a description to your gist.
|
110
173
|
* `:update` to update an existing gist (can be a URL or an id).
|
111
|
-
* `:anonymous` to submit an anonymous gist (default is false).
|
112
174
|
* `:copy` to copy the resulting URL to the clipboard (default is false).
|
113
175
|
* `:open` to open the resulting URL in a browser (default is false).
|
114
176
|
|
115
|
-
NOTE: The access_token must have the
|
177
|
+
NOTE: The access_token must have the `gist` scope and may also require the `user:email` scope.
|
116
178
|
|
117
179
|
If you want to upload multiple files in the same gist, you can:
|
118
180
|
|
@@ -126,16 +188,6 @@ NOTE: The access_token must have the "gist" scope.
|
|
126
188
|
This will take them through the process of obtaining an OAuth2 token, and storing it
|
127
189
|
in `~/.gist`, where it can later be read by `Gist.gist`
|
128
190
|
|
129
|
-
## GitHub enterprise
|
130
|
-
|
131
|
-
If you'd like `gist` to use your locally installed [GitHub Enterprise](https://enterprise.github.com/),
|
132
|
-
you need to export the `GITHUB_URL` environment variable in your `~/.bashrc`.
|
133
|
-
|
134
|
-
export GITHUB_URL=http://github.internal.example.com/
|
135
|
-
|
136
|
-
Once you've done this and restarted your terminal (or run `source ~/.bashrc`), gist will
|
137
|
-
automatically use github enterprise instead of the public github.com
|
138
|
-
|
139
191
|
## Configuration
|
140
192
|
|
141
193
|
If you'd like `-o` or `-c` to be the default when you use the gist executable, add an
|
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
|
-
|
28
|
-
|
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
|
-
|
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
|
@@ -123,6 +123,10 @@ Usage: #{executable_name} [-o|-c|-e] [-p] [-s] [-R] [-d DESC] [-a] [-u URL] [-P]
|
|
123
123
|
options[:read] = id
|
124
124
|
end
|
125
125
|
|
126
|
+
opts.on("--delete [ URL | ID ]", "Delete a gist") do |id|
|
127
|
+
options[:delete] = id
|
128
|
+
end
|
129
|
+
|
126
130
|
opts.on_tail("-h","--help", "Show this message.") do
|
127
131
|
puts opts
|
128
132
|
exit
|
@@ -136,6 +140,12 @@ Usage: #{executable_name} [-o|-c|-e] [-p] [-s] [-R] [-d DESC] [-a] [-u URL] [-P]
|
|
136
140
|
end.parse!
|
137
141
|
|
138
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
|
+
|
139
149
|
options[:output] = if options[:embed] && options[:shorten]
|
140
150
|
raise Gist::Error, "--embed does not make sense with --shorten"
|
141
151
|
elsif options[:embed]
|
@@ -167,6 +177,11 @@ begin
|
|
167
177
|
exit
|
168
178
|
end
|
169
179
|
|
180
|
+
if options.key? :delete
|
181
|
+
Gist.delete_gist(options[:delete])
|
182
|
+
exit
|
183
|
+
end
|
184
|
+
|
170
185
|
if options[:paste]
|
171
186
|
puts Gist.gist(Gist.paste, options)
|
172
187
|
else
|
@@ -186,7 +201,8 @@ begin
|
|
186
201
|
end
|
187
202
|
end
|
188
203
|
|
189
|
-
|
204
|
+
output = Gist.multi_gist(files, options)
|
205
|
+
puts output if output
|
190
206
|
end
|
191
207
|
|
192
208
|
rescue Gist::Error => e
|
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,23 +12,27 @@ end
|
|
12
12
|
module Gist
|
13
13
|
extend self
|
14
14
|
|
15
|
-
VERSION = '
|
15
|
+
VERSION = '6.0.0'
|
16
16
|
|
17
17
|
# A list of clipboard commands with copy and paste support.
|
18
18
|
CLIPBOARD_COMMANDS = {
|
19
|
+
'pbcopy' => 'pbpaste',
|
19
20
|
'xclip' => 'xclip -o',
|
20
21
|
'xsel -i' => 'xsel -o',
|
21
|
-
'
|
22
|
-
'putclip' => 'getclip'
|
22
|
+
'putclip' => 'getclip',
|
23
23
|
}
|
24
24
|
|
25
25
|
GITHUB_API_URL = URI("https://api.github.com/")
|
26
|
-
|
26
|
+
GITHUB_URL = URI("https://github.com/")
|
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(/[^a-
|
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
|
@@ -76,10 +80,14 @@ module Gist
|
|
76
80
|
#
|
77
81
|
# @see http://developer.github.com/v3/gists/
|
78
82
|
def gist(content, options = {})
|
79
|
-
filename = options[:filename] ||
|
83
|
+
filename = options[:filename] || default_filename
|
80
84
|
multi_gist({filename => content}, options)
|
81
85
|
end
|
82
86
|
|
87
|
+
def default_filename
|
88
|
+
"gistfile1.txt"
|
89
|
+
end
|
90
|
+
|
83
91
|
# Upload a gist to https://gist.github.com
|
84
92
|
#
|
85
93
|
# @param [Hash] files the code you'd like to gist: filename => content
|
@@ -92,6 +100,7 @@ module Gist
|
|
92
100
|
# @option options [String] :update the URL or id of a gist to update
|
93
101
|
# @option options [Boolean] :copy (false) Copy resulting URL to clipboard, if successful.
|
94
102
|
# @option options [Boolean] :open (false) Open the resulting URL in a browser.
|
103
|
+
# @option options [Boolean] :skip_empty (false) Skip gisting empty files.
|
95
104
|
# @option options [Symbol] :output (:all) The type of return value you'd like:
|
96
105
|
# :html_url gives a String containing the url to the gist in a browser
|
97
106
|
# :short_url gives a String contianing a git.io url that redirects to html_url
|
@@ -103,6 +112,13 @@ module Gist
|
|
103
112
|
#
|
104
113
|
# @see http://developer.github.com/v3/gists/
|
105
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
|
+
|
106
122
|
json = {}
|
107
123
|
|
108
124
|
json[:description] = options[:description] if options[:description]
|
@@ -110,22 +126,23 @@ module Gist
|
|
110
126
|
json[:files] = {}
|
111
127
|
|
112
128
|
files.each_pair do |(name, content)|
|
113
|
-
|
114
|
-
|
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
|
115
135
|
end
|
116
136
|
|
137
|
+
return if json[:files].empty? && options[:skip_empty]
|
138
|
+
|
117
139
|
existing_gist = options[:update].to_s.split("/").last
|
118
|
-
if options[:anonymous]
|
119
|
-
access_token = nil
|
120
|
-
else
|
121
|
-
access_token = (options[:access_token] || auth_token())
|
122
|
-
end
|
123
140
|
|
124
141
|
url = "#{base_path}/gists"
|
125
142
|
url << "/" << CGI.escape(existing_gist) if existing_gist.to_s != ''
|
126
|
-
url << "?access_token=" << CGI.escape(access_token) if access_token.to_s != ''
|
127
143
|
|
128
144
|
request = Net::HTTP::Post.new(url)
|
145
|
+
request['Authorization'] = "token #{access_token}" if access_token.to_s != ''
|
129
146
|
request.body = JSON.dump(json)
|
130
147
|
request.content_type = 'application/json'
|
131
148
|
|
@@ -161,9 +178,10 @@ module Gist
|
|
161
178
|
if user == ""
|
162
179
|
access_token = auth_token()
|
163
180
|
if access_token.to_s != ''
|
164
|
-
url << "/gists
|
181
|
+
url << "/gists"
|
165
182
|
|
166
183
|
request = Net::HTTP::Get.new(url)
|
184
|
+
request['Authorization'] = "token #{access_token}"
|
167
185
|
response = http(api_url, request)
|
168
186
|
|
169
187
|
pretty_gist(response)
|
@@ -186,24 +204,21 @@ module Gist
|
|
186
204
|
url = "#{base_path}"
|
187
205
|
|
188
206
|
if user == ""
|
189
|
-
|
190
|
-
if access_token.to_s != ''
|
191
|
-
url << "/gists?per_page=100&access_token=" << CGI.escape(access_token)
|
192
|
-
get_gist_pages(url)
|
193
|
-
else
|
194
|
-
raise Error, "Not authenticated. Use 'gist --login' to login or 'gist -l username' to view public gists."
|
195
|
-
end
|
196
|
-
|
207
|
+
url << "/gists?per_page=100"
|
197
208
|
else
|
198
209
|
url << "/users/#{user}/gists?per_page=100"
|
199
|
-
get_gist_pages(url)
|
200
210
|
end
|
201
211
|
|
212
|
+
get_gist_pages(url, auth_token())
|
202
213
|
end
|
203
214
|
|
204
215
|
def read_gist(id, file_name=nil)
|
205
216
|
url = "#{base_path}/gists/#{id}"
|
217
|
+
|
218
|
+
access_token = auth_token()
|
219
|
+
|
206
220
|
request = Net::HTTP::Get.new(url)
|
221
|
+
request['Authorization'] = "token #{access_token}" if access_token.to_s != ''
|
207
222
|
response = http(api_url, request)
|
208
223
|
|
209
224
|
if response.code == '200'
|
@@ -223,9 +238,30 @@ module Gist
|
|
223
238
|
end
|
224
239
|
end
|
225
240
|
|
226
|
-
def
|
241
|
+
def delete_gist(id)
|
242
|
+
id = id.split("/").last
|
243
|
+
url = "#{base_path}/gists/#{id}"
|
244
|
+
|
245
|
+
access_token = auth_token()
|
246
|
+
if access_token.to_s != ''
|
247
|
+
request = Net::HTTP::Delete.new(url)
|
248
|
+
request["Authorization"] = "token #{access_token}"
|
249
|
+
response = http(api_url, request)
|
250
|
+
else
|
251
|
+
raise Error, "Not authenticated. Use 'gist --login' to login."
|
252
|
+
end
|
253
|
+
|
254
|
+
if response.code == '204'
|
255
|
+
puts "Deleted!"
|
256
|
+
else
|
257
|
+
raise Error, "Gist with id of #{id} does not exist."
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
def get_gist_pages(url, access_token = "")
|
227
262
|
|
228
263
|
request = Net::HTTP::Get.new(url)
|
264
|
+
request['Authorization'] = "token #{access_token}" if access_token.to_s != ''
|
229
265
|
response = http(api_url, request)
|
230
266
|
pretty_gist(response)
|
231
267
|
|
@@ -233,7 +269,7 @@ module Gist
|
|
233
269
|
|
234
270
|
if link_header
|
235
271
|
links = Hash[ link_header.gsub(/(<|>|")/, "").split(',').map { |link| link.split('; rel=') } ].invert
|
236
|
-
get_gist_pages(links['next']) if links['next']
|
272
|
+
get_gist_pages(links['next'], access_token) if links['next']
|
237
273
|
end
|
238
274
|
|
239
275
|
end
|
@@ -263,10 +299,12 @@ module Gist
|
|
263
299
|
# @param [String] url
|
264
300
|
# @return [String] shortened url, or long url if shortening fails
|
265
301
|
def shorten(url)
|
266
|
-
request = Net::HTTP::Post.new("/")
|
302
|
+
request = Net::HTTP::Post.new("/create")
|
267
303
|
request.set_form_data(:url => url)
|
268
304
|
response = http(GIT_IO_URL, request)
|
269
305
|
case response.code
|
306
|
+
when "200"
|
307
|
+
URI.join(GIT_IO_URL, response.body).to_s
|
270
308
|
when "201"
|
271
309
|
response['Location']
|
272
310
|
else
|
@@ -295,16 +333,72 @@ module Gist
|
|
295
333
|
|
296
334
|
# Log the user into gist.
|
297
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
|
+
#
|
298
395
|
# This method asks the user for a username and password, and tries to obtain
|
299
396
|
# and OAuth2 access token, which is then stored in ~/.gist
|
300
397
|
#
|
301
398
|
# @raise [Gist::Error] if something went wrong
|
302
|
-
# @param [Hash] credentials login details
|
303
|
-
# @option credentials [String] :username
|
304
|
-
# @option credentials [String] :password
|
305
399
|
# @see http://developer.github.com/v3/oauth/
|
306
|
-
def
|
307
|
-
puts "Obtaining OAuth2 access_token from
|
400
|
+
def access_token_login!(credentials={})
|
401
|
+
puts "Obtaining OAuth2 access_token from GitHub."
|
308
402
|
loop do
|
309
403
|
print "GitHub username: "
|
310
404
|
username = credentials[:username] || $stdin.gets.strip
|
@@ -339,7 +433,7 @@ module Gist
|
|
339
433
|
|
340
434
|
if Net::HTTPCreated === response
|
341
435
|
AuthTokenFile.write JSON.parse(response.body)['token']
|
342
|
-
puts "Success! #{ENV[URL_ENV_NAME] || "https://github.com/"}settings/
|
436
|
+
puts "Success! #{ENV[URL_ENV_NAME] || "https://github.com/"}settings/tokens"
|
343
437
|
return
|
344
438
|
elsif Net::HTTPUnauthorized === response
|
345
439
|
puts "Error: #{JSON.parse(response.body)['message']}"
|
@@ -360,7 +454,11 @@ module Gist
|
|
360
454
|
env = ENV['http_proxy'] || ENV['HTTP_PROXY']
|
361
455
|
connection = if env
|
362
456
|
proxy = URI(env)
|
363
|
-
|
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
|
364
462
|
else
|
365
463
|
Net::HTTP.new(uri.host, uri.port)
|
366
464
|
end
|
@@ -514,11 +612,19 @@ Could not find copy command, tried:
|
|
514
612
|
ENV.key?(URL_ENV_NAME) ? GHE_BASE_PATH : GITHUB_BASE_PATH
|
515
613
|
end
|
516
614
|
|
615
|
+
def login_url
|
616
|
+
ENV.key?(URL_ENV_NAME) ? URI(ENV[URL_ENV_NAME]) : GITHUB_URL
|
617
|
+
end
|
618
|
+
|
517
619
|
# Get the API URL
|
518
620
|
def api_url
|
519
621
|
ENV.key?(URL_ENV_NAME) ? URI(ENV[URL_ENV_NAME]) : GITHUB_API_URL
|
520
622
|
end
|
521
623
|
|
624
|
+
def client_id
|
625
|
+
ENV.key?(CLIENT_ID_ENV_NAME) ? URI(ENV[CLIENT_ID_ENV_NAME]) : GITHUB_CLIENT_ID
|
626
|
+
end
|
627
|
+
|
522
628
|
def legacy_private_gister?
|
523
629
|
return unless which('git')
|
524
630
|
`git config --global gist.private` =~ /\Ayes|1|true|on\z/i
|