gist 4.2.1 → 4.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/README.md +54 -25
- data/bin/gist +50 -3
- data/build/gist +240 -25
- data/build/gist.1 +62 -25
- data/gist.gemspec +4 -3
- data/lib/gist.rb +190 -22
- data/spec/auth_token_file_spec.rb +61 -0
- data/spec/ghe_spec.rb +1 -1
- data/spec/gist_spec.rb +4 -4
- data/spec/shorten_spec.rb +8 -4
- data/spec/spec_helper.rb +7 -3
- metadata +21 -21
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8dcb4ad16178e41476596aa05e8819ed3431e5f2
|
|
4
|
+
data.tar.gz: 6698bd0bda9e583700e5bec695d7e6b5c1b6fba5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7ca4af1b0e24281a929ea6a39e5e67a3b2a2c5bc7aed06039adac829665f5e5d013061a65b097a3be0a68bc623c43299f5f9b148b2a28113644aff267cd86036
|
|
7
|
+
data.tar.gz: c4bde01c49fe0182422071f8afa445b3da2af1d829b7d4f36df957eef059ddb097f4e248f232c5bebf06888e22327d7b44bb18f93cb2456965f9f03e8f031f32
|
data/README.md
CHANGED
|
@@ -50,7 +50,8 @@ upload content to https://gist.github.com/.
|
|
|
50
50
|
|
|
51
51
|
You can update existing gists with `-u`:
|
|
52
52
|
|
|
53
|
-
gist
|
|
53
|
+
gist -u GIST_ID FILE_NAME
|
|
54
|
+
gist -u 42f2c239d2eb57299408 test.txt
|
|
54
55
|
|
|
55
56
|
If you'd like to copy the resulting URL to your clipboard, use `-c`.
|
|
56
57
|
|
|
@@ -64,6 +65,16 @@ upload content to https://gist.github.com/.
|
|
|
64
65
|
|
|
65
66
|
gist -o <a.rb
|
|
66
67
|
|
|
68
|
+
To list (public gists or all gists for authed user) gists for user
|
|
69
|
+
|
|
70
|
+
gist -l : all gists for authed user
|
|
71
|
+
gist -l defunkt : list defunkt's public gists
|
|
72
|
+
|
|
73
|
+
To read a gist and print it to STDOUT
|
|
74
|
+
|
|
75
|
+
gist -r GIST_ID
|
|
76
|
+
gist -r 374130
|
|
77
|
+
|
|
67
78
|
See `gist --help` for more detail.
|
|
68
79
|
|
|
69
80
|
## Login
|
|
@@ -77,27 +88,55 @@ an OAuth2 token (with the "gist" permission).
|
|
|
77
88
|
GitHub username: ConradIrwin
|
|
78
89
|
GitHub password:
|
|
79
90
|
2-factor auth code:
|
|
80
|
-
Success! https://github.com/settings/
|
|
81
|
-
|
|
82
|
-
You can read the 2-factor auth code from an sms or the authentification app,
|
|
83
|
-
depending on how you [set your account up](https://github.com/settings/admin).
|
|
84
|
-
|
|
85
|
-
Note: 2-factor authentication
|
|
86
|
-
[just appeared recently](https://github.com/blog/1614-two-factor-authentication),
|
|
87
|
-
so if you run into errors, update the gist gem.
|
|
88
|
-
|
|
89
|
-
gem update gist
|
|
91
|
+
Success! https://github.com/settings/tokens
|
|
90
92
|
|
|
91
93
|
This token is stored in `~/.gist` and used for all future gisting. If you need to
|
|
92
|
-
you can revoke it from https://github.com/settings/
|
|
93
|
-
file.
|
|
94
|
-
you can save your Github Enterprise token in `~/.gist.github.example.com` where
|
|
95
|
-
"github.example.com" is the URL for your Github Enterprise instance.
|
|
94
|
+
you can revoke it from https://github.com/settings/tokens, or just delete the
|
|
95
|
+
file.
|
|
96
96
|
|
|
97
97
|
After you've done this, you can still upload gists anonymously with `-a`.
|
|
98
98
|
|
|
99
99
|
gist -a a.rb
|
|
100
100
|
|
|
101
|
+
#### Password-less login
|
|
102
|
+
|
|
103
|
+
If you have a complicated authorization requirement you can manually create a
|
|
104
|
+
token file by pasting a Github token with only the `gist` permission into a
|
|
105
|
+
file called `~/.gist`. You can create one from https://github.com/settings/tokens
|
|
106
|
+
|
|
107
|
+
This file should contain only the token (~40 hex characters), and to make it
|
|
108
|
+
easier to edit, can optionally have a final newline (\n or \r\n).
|
|
109
|
+
|
|
110
|
+
For example, one way to create this file would be to run:
|
|
111
|
+
|
|
112
|
+
echo MY_SECRET_TOKEN > ~/.gist
|
|
113
|
+
|
|
114
|
+
### GitHub Enterprise
|
|
115
|
+
|
|
116
|
+
If you'd like `gist` to use your locally installed [GitHub Enterprise](https://enterprise.github.com/),
|
|
117
|
+
you need to export the `GITHUB_URL` environment variable (usually done in your `~/.bashrc`).
|
|
118
|
+
|
|
119
|
+
export GITHUB_URL=http://github.internal.example.com/
|
|
120
|
+
|
|
121
|
+
Once you've done this and restarted your terminal (or run `source ~/.bashrc`), gist will
|
|
122
|
+
automatically use github enterprise instead of the public github.com
|
|
123
|
+
|
|
124
|
+
Your token for GitHub Enterprise will be stored in `.gist.<protocol>.<server.name>[.<port>]` (e.g.
|
|
125
|
+
`~/.gist.http.github.internal.example.com` for the GITHUB_URL example above) instead of `~/.gist`.
|
|
126
|
+
|
|
127
|
+
If you have multiple servers or use Enterprise and public GitHub often, you can work around this by creating scripts
|
|
128
|
+
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
|
|
129
|
+
setting it to the public URL will not work. Use `unset GITHUB_URL`
|
|
130
|
+
|
|
131
|
+
### Token file format
|
|
132
|
+
|
|
133
|
+
If you cannot use passwords, as most Enterprise installations do, you can generate the token via the web interface
|
|
134
|
+
and then simply save the string in the correct file. Avoid line breaks or you might see:
|
|
135
|
+
```
|
|
136
|
+
$ gist -l
|
|
137
|
+
Error: Bad credentials
|
|
138
|
+
```
|
|
139
|
+
|
|
101
140
|
# Library
|
|
102
141
|
|
|
103
142
|
You can also use Gist as a library from inside your ruby code:
|
|
@@ -129,16 +168,6 @@ NOTE: The access_token must have the "gist" scope.
|
|
|
129
168
|
This will take them through the process of obtaining an OAuth2 token, and storing it
|
|
130
169
|
in `~/.gist`, where it can later be read by `Gist.gist`
|
|
131
170
|
|
|
132
|
-
## GitHub enterprise
|
|
133
|
-
|
|
134
|
-
If you'd like `gist` to use your locally installed [GitHub Enterprise](https://enterprise.github.com/),
|
|
135
|
-
you need to export the `GITHUB_URL` environment variable in your `~/.bashrc`.
|
|
136
|
-
|
|
137
|
-
export GITHUB_URL=http://github.internal.example.com/
|
|
138
|
-
|
|
139
|
-
Once you've done this and restarted your terminal (or run `source ~/.bashrc`), gist will
|
|
140
|
-
automatically use github enterprise instead of the public github.com
|
|
141
|
-
|
|
142
171
|
## Configuration
|
|
143
172
|
|
|
144
173
|
If you'd like `-o` or `-c` to be the default when you use the gist executable, add an
|
data/bin/gist
CHANGED
|
@@ -3,6 +3,10 @@
|
|
|
3
3
|
# Silence Ctrl-C's
|
|
4
4
|
trap('INT'){ exit 1 }
|
|
5
5
|
|
|
6
|
+
if Signal.list.include? 'PIPE'
|
|
7
|
+
trap('PIPE', 'EXIT')
|
|
8
|
+
end
|
|
9
|
+
|
|
6
10
|
require 'optparse'
|
|
7
11
|
require 'gist'
|
|
8
12
|
|
|
@@ -43,8 +47,13 @@ Instead of creating a new gist, you can update an existing one by passing its ID
|
|
|
43
47
|
or URL with "-u". For this to work, you must be logged in, and have created the
|
|
44
48
|
original gist with the same GitHub account.
|
|
45
49
|
|
|
46
|
-
|
|
50
|
+
If you want to skip empty files, use the --skip-empty flag. If all files are
|
|
51
|
+
empty no gist will be created.
|
|
52
|
+
|
|
53
|
+
Usage: #{executable_name} [-o|-c|-e] [-p] [-s] [-R] [-d DESC] [-a] [-u URL]
|
|
54
|
+
[--skip-empty] [-P] [-f NAME|-t EXT]* FILE*
|
|
47
55
|
#{executable_name} --login
|
|
56
|
+
#{executable_name} [-l|-r]
|
|
48
57
|
|
|
49
58
|
EOS
|
|
50
59
|
|
|
@@ -102,14 +111,30 @@ Usage: #{executable_name} [-o|-c|-e] [-p] [-s] [-R] [-d DESC] [-a] [-u URL] [-P]
|
|
|
102
111
|
|
|
103
112
|
opts.on("--no-open")
|
|
104
113
|
|
|
114
|
+
opts.on("--skip-empty", "Skip gisting empty files") do
|
|
115
|
+
options[:skip_empty] = true
|
|
116
|
+
end
|
|
117
|
+
|
|
105
118
|
opts.on("-P", "--paste", "Paste from the clipboard to gist") do
|
|
106
119
|
options[:paste] = true
|
|
107
120
|
end
|
|
108
121
|
|
|
109
|
-
opts.on("-R", "--raw", "
|
|
122
|
+
opts.on("-R", "--raw", "Display raw URL of the new gist") do
|
|
110
123
|
options[:raw] = true
|
|
111
124
|
end
|
|
112
125
|
|
|
126
|
+
opts.on("-l", "--list [USER]", "List all gists for user") do |user|
|
|
127
|
+
options[:list] = user
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
opts.on("-r", "--read ID [FILENAME]", "Read a gist and print out the contents") do |id|
|
|
131
|
+
options[:read] = id
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
opts.on("--delete [ URL | ID ]", "Delete a gist") do |id|
|
|
135
|
+
options[:delete] = id
|
|
136
|
+
end
|
|
137
|
+
|
|
113
138
|
opts.on_tail("-h","--help", "Show this message.") do
|
|
114
139
|
puts opts
|
|
115
140
|
exit
|
|
@@ -139,6 +164,26 @@ begin
|
|
|
139
164
|
|
|
140
165
|
options[:public] = Gist.should_be_public?(options)
|
|
141
166
|
|
|
167
|
+
if options.key? :list
|
|
168
|
+
if options[:list]
|
|
169
|
+
Gist.list_all_gists(options[:list])
|
|
170
|
+
else
|
|
171
|
+
Gist.list_all_gists
|
|
172
|
+
end
|
|
173
|
+
exit
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
if options.key? :read
|
|
177
|
+
file_name = ARGV.first
|
|
178
|
+
Gist.read_gist(options[:read], file_name)
|
|
179
|
+
exit
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
if options.key? :delete
|
|
183
|
+
Gist.delete_gist(options[:delete])
|
|
184
|
+
exit
|
|
185
|
+
end
|
|
186
|
+
|
|
142
187
|
if options[:paste]
|
|
143
188
|
puts Gist.gist(Gist.paste, options)
|
|
144
189
|
else
|
|
@@ -158,8 +203,10 @@ begin
|
|
|
158
203
|
end
|
|
159
204
|
end
|
|
160
205
|
|
|
161
|
-
|
|
206
|
+
output = Gist.multi_gist(files, options)
|
|
207
|
+
puts output if output
|
|
162
208
|
end
|
|
209
|
+
|
|
163
210
|
rescue Gist::Error => e
|
|
164
211
|
puts "Error: #{e.message}"
|
|
165
212
|
exit 1
|
data/build/gist
CHANGED
|
@@ -1318,18 +1318,18 @@ end
|
|
|
1318
1318
|
module Gist
|
|
1319
1319
|
extend self
|
|
1320
1320
|
|
|
1321
|
-
VERSION = '4.2
|
|
1321
|
+
VERSION = '4.6.2'
|
|
1322
1322
|
|
|
1323
1323
|
# A list of clipboard commands with copy and paste support.
|
|
1324
1324
|
CLIPBOARD_COMMANDS = {
|
|
1325
|
-
'xclip' => 'xclip -o',
|
|
1326
|
-
'xsel -i' => 'xsel -o',
|
|
1327
1325
|
'pbcopy' => 'pbpaste',
|
|
1328
|
-
'
|
|
1326
|
+
'xclip' => 'xclip -o',
|
|
1327
|
+
'xsel -i' => 'xsel -o',
|
|
1328
|
+
'putclip' => 'getclip',
|
|
1329
1329
|
}
|
|
1330
1330
|
|
|
1331
1331
|
GITHUB_API_URL = URI("https://api.github.com/")
|
|
1332
|
-
GIT_IO_URL = URI("
|
|
1332
|
+
GIT_IO_URL = URI("https://git.io")
|
|
1333
1333
|
|
|
1334
1334
|
GITHUB_BASE_PATH = ""
|
|
1335
1335
|
GHE_BASE_PATH = "/api/v3"
|
|
@@ -1346,6 +1346,34 @@ module Gist
|
|
|
1346
1346
|
end
|
|
1347
1347
|
class ClipboardError < RuntimeError; include Error end
|
|
1348
1348
|
|
|
1349
|
+
# helper module for authentication token actions
|
|
1350
|
+
module AuthTokenFile
|
|
1351
|
+
def self.filename
|
|
1352
|
+
if ENV.key?(URL_ENV_NAME)
|
|
1353
|
+
File.expand_path "~/.gist.#{ENV[URL_ENV_NAME].gsub(/:/, '.').gsub(/[^a-z0-9.]/, '')}"
|
|
1354
|
+
else
|
|
1355
|
+
File.expand_path "~/.gist"
|
|
1356
|
+
end
|
|
1357
|
+
end
|
|
1358
|
+
|
|
1359
|
+
def self.read
|
|
1360
|
+
File.read(filename).chomp
|
|
1361
|
+
end
|
|
1362
|
+
|
|
1363
|
+
def self.write(token)
|
|
1364
|
+
File.open(filename, 'w', 0600) do |f|
|
|
1365
|
+
f.write token
|
|
1366
|
+
end
|
|
1367
|
+
end
|
|
1368
|
+
end
|
|
1369
|
+
|
|
1370
|
+
# auth token for authentication
|
|
1371
|
+
#
|
|
1372
|
+
# @return [String] string value of access token or `nil`, if not found
|
|
1373
|
+
def auth_token
|
|
1374
|
+
@token ||= AuthTokenFile.read rescue nil
|
|
1375
|
+
end
|
|
1376
|
+
|
|
1349
1377
|
# Upload a gist to https://gist.github.com
|
|
1350
1378
|
#
|
|
1351
1379
|
# @param [String] content the code you'd like to gist
|
|
@@ -1354,10 +1382,14 @@ module Gist
|
|
|
1354
1382
|
#
|
|
1355
1383
|
# @see http://developer.github.com/v3/gists/
|
|
1356
1384
|
def gist(content, options = {})
|
|
1357
|
-
filename = options[:filename] ||
|
|
1385
|
+
filename = options[:filename] || default_filename
|
|
1358
1386
|
multi_gist({filename => content}, options)
|
|
1359
1387
|
end
|
|
1360
1388
|
|
|
1389
|
+
def default_filename
|
|
1390
|
+
"gistfile1.txt"
|
|
1391
|
+
end
|
|
1392
|
+
|
|
1361
1393
|
# Upload a gist to https://gist.github.com
|
|
1362
1394
|
#
|
|
1363
1395
|
# @param [Hash] files the code you'd like to gist: filename => content
|
|
@@ -1370,6 +1402,7 @@ module Gist
|
|
|
1370
1402
|
# @option options [String] :update the URL or id of a gist to update
|
|
1371
1403
|
# @option options [Boolean] :copy (false) Copy resulting URL to clipboard, if successful.
|
|
1372
1404
|
# @option options [Boolean] :open (false) Open the resulting URL in a browser.
|
|
1405
|
+
# @option options [Boolean] :skip_empty (false) Skip gisting empty files.
|
|
1373
1406
|
# @option options [Symbol] :output (:all) The type of return value you'd like:
|
|
1374
1407
|
# :html_url gives a String containing the url to the gist in a browser
|
|
1375
1408
|
# :short_url gives a String contianing a git.io url that redirects to html_url
|
|
@@ -1388,15 +1421,21 @@ module Gist
|
|
|
1388
1421
|
json[:files] = {}
|
|
1389
1422
|
|
|
1390
1423
|
files.each_pair do |(name, content)|
|
|
1391
|
-
|
|
1392
|
-
|
|
1424
|
+
if content.to_s.strip == ""
|
|
1425
|
+
raise "Cannot gist empty files" unless options[:skip_empty]
|
|
1426
|
+
else
|
|
1427
|
+
name = name == "-" ? default_filename : File.basename(name)
|
|
1428
|
+
json[:files][name] = {:content => content}
|
|
1429
|
+
end
|
|
1393
1430
|
end
|
|
1394
1431
|
|
|
1432
|
+
return if json[:files].empty? && options[:skip_empty]
|
|
1433
|
+
|
|
1395
1434
|
existing_gist = options[:update].to_s.split("/").last
|
|
1396
1435
|
if options[:anonymous]
|
|
1397
1436
|
access_token = nil
|
|
1398
1437
|
else
|
|
1399
|
-
access_token = (options[:access_token] ||
|
|
1438
|
+
access_token = (options[:access_token] || auth_token())
|
|
1400
1439
|
end
|
|
1401
1440
|
|
|
1402
1441
|
url = "#{base_path}/gists"
|
|
@@ -1426,15 +1465,154 @@ module Gist
|
|
|
1426
1465
|
raise e.extend Error
|
|
1427
1466
|
end
|
|
1428
1467
|
|
|
1468
|
+
# List all gists(private also) for authenticated user
|
|
1469
|
+
# otherwise list public gists for given username (optional argument)
|
|
1470
|
+
#
|
|
1471
|
+
# @param [String] user
|
|
1472
|
+
# @deprecated
|
|
1473
|
+
#
|
|
1474
|
+
# see https://developer.github.com/v3/gists/#list-gists
|
|
1475
|
+
def list_gists(user = "")
|
|
1476
|
+
url = "#{base_path}"
|
|
1477
|
+
|
|
1478
|
+
if user == ""
|
|
1479
|
+
access_token = auth_token()
|
|
1480
|
+
if access_token.to_s != ''
|
|
1481
|
+
url << "/gists?access_token=" << CGI.escape(access_token)
|
|
1482
|
+
|
|
1483
|
+
request = Net::HTTP::Get.new(url)
|
|
1484
|
+
response = http(api_url, request)
|
|
1485
|
+
|
|
1486
|
+
pretty_gist(response)
|
|
1487
|
+
|
|
1488
|
+
else
|
|
1489
|
+
raise Error, "Not authenticated. Use 'gist --login' to login or 'gist -l username' to view public gists."
|
|
1490
|
+
end
|
|
1491
|
+
|
|
1492
|
+
else
|
|
1493
|
+
url << "/users/#{user}/gists"
|
|
1494
|
+
|
|
1495
|
+
request = Net::HTTP::Get.new(url)
|
|
1496
|
+
response = http(api_url, request)
|
|
1497
|
+
|
|
1498
|
+
pretty_gist(response)
|
|
1499
|
+
end
|
|
1500
|
+
end
|
|
1501
|
+
|
|
1502
|
+
def list_all_gists(user = "")
|
|
1503
|
+
url = "#{base_path}"
|
|
1504
|
+
|
|
1505
|
+
if user == ""
|
|
1506
|
+
access_token = auth_token()
|
|
1507
|
+
if access_token.to_s != ''
|
|
1508
|
+
url << "/gists?per_page=100&access_token=" << CGI.escape(access_token)
|
|
1509
|
+
get_gist_pages(url)
|
|
1510
|
+
else
|
|
1511
|
+
raise Error, "Not authenticated. Use 'gist --login' to login or 'gist -l username' to view public gists."
|
|
1512
|
+
end
|
|
1513
|
+
|
|
1514
|
+
else
|
|
1515
|
+
url << "/users/#{user}/gists?per_page=100"
|
|
1516
|
+
get_gist_pages(url)
|
|
1517
|
+
end
|
|
1518
|
+
|
|
1519
|
+
end
|
|
1520
|
+
|
|
1521
|
+
def read_gist(id, file_name=nil)
|
|
1522
|
+
url = "#{base_path}/gists/#{id}"
|
|
1523
|
+
|
|
1524
|
+
access_token = auth_token()
|
|
1525
|
+
if access_token.to_s != ''
|
|
1526
|
+
url << "?access_token=" << CGI.escape(access_token)
|
|
1527
|
+
end
|
|
1528
|
+
|
|
1529
|
+
request = Net::HTTP::Get.new(url)
|
|
1530
|
+
response = http(api_url, request)
|
|
1531
|
+
|
|
1532
|
+
if response.code == '200'
|
|
1533
|
+
body = JSON.parse(response.body)
|
|
1534
|
+
files = body["files"]
|
|
1535
|
+
|
|
1536
|
+
if file_name
|
|
1537
|
+
file = files[file_name]
|
|
1538
|
+
raise Error, "Gist with id of #{id} and file #{file_name} does not exist." unless file
|
|
1539
|
+
else
|
|
1540
|
+
file = files.values.first
|
|
1541
|
+
end
|
|
1542
|
+
|
|
1543
|
+
puts file["content"]
|
|
1544
|
+
else
|
|
1545
|
+
raise Error, "Gist with id of #{id} does not exist."
|
|
1546
|
+
end
|
|
1547
|
+
end
|
|
1548
|
+
|
|
1549
|
+
def delete_gist(id)
|
|
1550
|
+
id = id.split("/").last
|
|
1551
|
+
url = "#{base_path}/gists/#{id}"
|
|
1552
|
+
|
|
1553
|
+
access_token = auth_token()
|
|
1554
|
+
if access_token.to_s != ''
|
|
1555
|
+
url << "?access_token=" << CGI.escape(access_token)
|
|
1556
|
+
|
|
1557
|
+
request = Net::HTTP::Delete.new(url)
|
|
1558
|
+
response = http(api_url, request)
|
|
1559
|
+
else
|
|
1560
|
+
raise Error, "Not authenticated. Use 'gist --login' to login."
|
|
1561
|
+
end
|
|
1562
|
+
|
|
1563
|
+
if response.code == '204'
|
|
1564
|
+
puts "Deleted!"
|
|
1565
|
+
else
|
|
1566
|
+
raise Error, "Gist with id of #{id} does not exist."
|
|
1567
|
+
end
|
|
1568
|
+
end
|
|
1569
|
+
|
|
1570
|
+
def get_gist_pages(url)
|
|
1571
|
+
|
|
1572
|
+
request = Net::HTTP::Get.new(url)
|
|
1573
|
+
response = http(api_url, request)
|
|
1574
|
+
pretty_gist(response)
|
|
1575
|
+
|
|
1576
|
+
link_header = response.header['link']
|
|
1577
|
+
|
|
1578
|
+
if link_header
|
|
1579
|
+
links = Hash[ link_header.gsub(/(<|>|")/, "").split(',').map { |link| link.split('; rel=') } ].invert
|
|
1580
|
+
get_gist_pages(links['next']) if links['next']
|
|
1581
|
+
end
|
|
1582
|
+
|
|
1583
|
+
end
|
|
1584
|
+
|
|
1585
|
+
# return prettified string result of response body for all gists
|
|
1586
|
+
#
|
|
1587
|
+
# @params [Net::HTTPResponse] response
|
|
1588
|
+
# @return [String] prettified result of listing all gists
|
|
1589
|
+
#
|
|
1590
|
+
# see https://developer.github.com/v3/gists/#response
|
|
1591
|
+
def pretty_gist(response)
|
|
1592
|
+
body = JSON.parse(response.body)
|
|
1593
|
+
if response.code == '200'
|
|
1594
|
+
body.each do |gist|
|
|
1595
|
+
description = "#{gist['description'] || gist['files'].keys.join(" ")} #{gist['public'] ? '' : '(secret)'}"
|
|
1596
|
+
puts "#{gist['html_url']} #{description.tr("\n", " ")}\n"
|
|
1597
|
+
$stdout.flush
|
|
1598
|
+
end
|
|
1599
|
+
|
|
1600
|
+
else
|
|
1601
|
+
raise Error, body['message']
|
|
1602
|
+
end
|
|
1603
|
+
end
|
|
1604
|
+
|
|
1429
1605
|
# Convert long github urls into short git.io ones
|
|
1430
1606
|
#
|
|
1431
1607
|
# @param [String] url
|
|
1432
1608
|
# @return [String] shortened url, or long url if shortening fails
|
|
1433
1609
|
def shorten(url)
|
|
1434
|
-
request = Net::HTTP::Post.new("/")
|
|
1610
|
+
request = Net::HTTP::Post.new("/create")
|
|
1435
1611
|
request.set_form_data(:url => url)
|
|
1436
1612
|
response = http(GIT_IO_URL, request)
|
|
1437
1613
|
case response.code
|
|
1614
|
+
when "200"
|
|
1615
|
+
URI.join(GIT_IO_URL, response.body).to_s
|
|
1438
1616
|
when "201"
|
|
1439
1617
|
response['Location']
|
|
1440
1618
|
else
|
|
@@ -1506,10 +1684,8 @@ module Gist
|
|
|
1506
1684
|
end
|
|
1507
1685
|
|
|
1508
1686
|
if Net::HTTPCreated === response
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
end
|
|
1512
|
-
puts "Success! #{ENV[URL_ENV_NAME] || "https://github.com/"}settings/applications"
|
|
1687
|
+
AuthTokenFile.write JSON.parse(response.body)['token']
|
|
1688
|
+
puts "Success! #{ENV[URL_ENV_NAME] || "https://github.com/"}settings/tokens"
|
|
1513
1689
|
return
|
|
1514
1690
|
elsif Net::HTTPUnauthorized === response
|
|
1515
1691
|
puts "Error: #{JSON.parse(response.body)['message']}"
|
|
@@ -1689,14 +1865,6 @@ Could not find copy command, tried:
|
|
|
1689
1865
|
ENV.key?(URL_ENV_NAME) ? URI(ENV[URL_ENV_NAME]) : GITHUB_API_URL
|
|
1690
1866
|
end
|
|
1691
1867
|
|
|
1692
|
-
def auth_token_file
|
|
1693
|
-
if ENV.key?(URL_ENV_NAME)
|
|
1694
|
-
File.expand_path "~/.gist.#{ENV[URL_ENV_NAME].gsub(/[^a-z.]/, '')}"
|
|
1695
|
-
else
|
|
1696
|
-
File.expand_path "~/.gist"
|
|
1697
|
-
end
|
|
1698
|
-
end
|
|
1699
|
-
|
|
1700
1868
|
def legacy_private_gister?
|
|
1701
1869
|
return unless which('git')
|
|
1702
1870
|
`git config --global gist.private` =~ /\Ayes|1|true|on\z/i
|
|
@@ -1715,6 +1883,10 @@ end
|
|
|
1715
1883
|
# Silence Ctrl-C's
|
|
1716
1884
|
trap('INT'){ exit 1 }
|
|
1717
1885
|
|
|
1886
|
+
if Signal.list.include? 'PIPE'
|
|
1887
|
+
trap('PIPE', 'EXIT')
|
|
1888
|
+
end
|
|
1889
|
+
|
|
1718
1890
|
require 'optparse'
|
|
1719
1891
|
|
|
1720
1892
|
# For the holdings of options.
|
|
@@ -1754,8 +1926,13 @@ Instead of creating a new gist, you can update an existing one by passing its ID
|
|
|
1754
1926
|
or URL with "-u". For this to work, you must be logged in, and have created the
|
|
1755
1927
|
original gist with the same GitHub account.
|
|
1756
1928
|
|
|
1757
|
-
|
|
1929
|
+
If you want to skip empty files, use the --skip-empty flag. If all files are
|
|
1930
|
+
empty no gist will be created.
|
|
1931
|
+
|
|
1932
|
+
Usage: #{executable_name} [-o|-c|-e] [-p] [-s] [-R] [-d DESC] [-a] [-u URL]
|
|
1933
|
+
[--skip-empty] [-P] [-f NAME|-t EXT]* FILE*
|
|
1758
1934
|
#{executable_name} --login
|
|
1935
|
+
#{executable_name} [-l|-r]
|
|
1759
1936
|
|
|
1760
1937
|
EOS
|
|
1761
1938
|
|
|
@@ -1813,14 +1990,30 @@ Usage: #{executable_name} [-o|-c|-e] [-p] [-s] [-R] [-d DESC] [-a] [-u URL] [-P]
|
|
|
1813
1990
|
|
|
1814
1991
|
opts.on("--no-open")
|
|
1815
1992
|
|
|
1993
|
+
opts.on("--skip-empty", "Skip gisting empty files") do
|
|
1994
|
+
options[:skip_empty] = true
|
|
1995
|
+
end
|
|
1996
|
+
|
|
1816
1997
|
opts.on("-P", "--paste", "Paste from the clipboard to gist") do
|
|
1817
1998
|
options[:paste] = true
|
|
1818
1999
|
end
|
|
1819
2000
|
|
|
1820
|
-
opts.on("-R", "--raw", "
|
|
2001
|
+
opts.on("-R", "--raw", "Display raw URL of the new gist") do
|
|
1821
2002
|
options[:raw] = true
|
|
1822
2003
|
end
|
|
1823
2004
|
|
|
2005
|
+
opts.on("-l", "--list [USER]", "List all gists for user") do |user|
|
|
2006
|
+
options[:list] = user
|
|
2007
|
+
end
|
|
2008
|
+
|
|
2009
|
+
opts.on("-r", "--read ID [FILENAME]", "Read a gist and print out the contents") do |id|
|
|
2010
|
+
options[:read] = id
|
|
2011
|
+
end
|
|
2012
|
+
|
|
2013
|
+
opts.on("--delete [ URL | ID ]", "Delete a gist") do |id|
|
|
2014
|
+
options[:delete] = id
|
|
2015
|
+
end
|
|
2016
|
+
|
|
1824
2017
|
opts.on_tail("-h","--help", "Show this message.") do
|
|
1825
2018
|
puts opts
|
|
1826
2019
|
exit
|
|
@@ -1850,6 +2043,26 @@ begin
|
|
|
1850
2043
|
|
|
1851
2044
|
options[:public] = Gist.should_be_public?(options)
|
|
1852
2045
|
|
|
2046
|
+
if options.key? :list
|
|
2047
|
+
if options[:list]
|
|
2048
|
+
Gist.list_all_gists(options[:list])
|
|
2049
|
+
else
|
|
2050
|
+
Gist.list_all_gists
|
|
2051
|
+
end
|
|
2052
|
+
exit
|
|
2053
|
+
end
|
|
2054
|
+
|
|
2055
|
+
if options.key? :read
|
|
2056
|
+
file_name = ARGV.first
|
|
2057
|
+
Gist.read_gist(options[:read], file_name)
|
|
2058
|
+
exit
|
|
2059
|
+
end
|
|
2060
|
+
|
|
2061
|
+
if options.key? :delete
|
|
2062
|
+
Gist.delete_gist(options[:delete])
|
|
2063
|
+
exit
|
|
2064
|
+
end
|
|
2065
|
+
|
|
1853
2066
|
if options[:paste]
|
|
1854
2067
|
puts Gist.gist(Gist.paste, options)
|
|
1855
2068
|
else
|
|
@@ -1869,8 +2082,10 @@ begin
|
|
|
1869
2082
|
end
|
|
1870
2083
|
end
|
|
1871
2084
|
|
|
1872
|
-
|
|
2085
|
+
output = Gist.multi_gist(files, options)
|
|
2086
|
+
puts output if output
|
|
1873
2087
|
end
|
|
2088
|
+
|
|
1874
2089
|
rescue Gist::Error => e
|
|
1875
2090
|
puts "Error: #{e.message}"
|
|
1876
2091
|
exit 1
|
data/build/gist.1
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
.\" generated with Ronn/v0.7.3
|
|
2
2
|
.\" http://github.com/rtomayko/ronn/tree/0.7.3
|
|
3
3
|
.
|
|
4
|
-
.TH "GIST" "1" "
|
|
4
|
+
.TH "GIST" "1" "January 2018" "" "Gist manual"
|
|
5
5
|
.
|
|
6
6
|
.SH "NAME"
|
|
7
7
|
\fBgist\fR \- upload code to https://gist\.github\.com
|
|
@@ -73,7 +73,7 @@ gist \-d "Random rbx bug" a\.rb
|
|
|
73
73
|
You can update existing gists with \fB\-u\fR:
|
|
74
74
|
.
|
|
75
75
|
.IP
|
|
76
|
-
gist
|
|
76
|
+
gist \-u GIST_ID FILE_NAME gist \-u 42f2c239d2eb57299408 test\.txt
|
|
77
77
|
.
|
|
78
78
|
.IP "\(bu" 4
|
|
79
79
|
If you\'d like to copy the resulting URL to your clipboard, use \fB\-c\fR\.
|
|
@@ -94,6 +94,28 @@ And you can just ask gist to open a browser window directly with \fB\-o\fR\.
|
|
|
94
94
|
gist \-o <a\.rb
|
|
95
95
|
.
|
|
96
96
|
.IP "\(bu" 4
|
|
97
|
+
To list (public gists or all gists for authed user) gists for user
|
|
98
|
+
.
|
|
99
|
+
.IP
|
|
100
|
+
gist \-l : all gists for authed user gist \-l defunkt : list defunkt\'s public gists
|
|
101
|
+
.
|
|
102
|
+
.IP "" 0
|
|
103
|
+
.
|
|
104
|
+
.P
|
|
105
|
+
To read a gist and print it to STDOUT
|
|
106
|
+
.
|
|
107
|
+
.IP "" 4
|
|
108
|
+
.
|
|
109
|
+
.nf
|
|
110
|
+
|
|
111
|
+
gist \-r GIST_ID
|
|
112
|
+
gist \-r 374130
|
|
113
|
+
.
|
|
114
|
+
.fi
|
|
115
|
+
.
|
|
116
|
+
.IP "" 0
|
|
117
|
+
.
|
|
118
|
+
.IP "\(bu" 4
|
|
97
119
|
See \fBgist \-\-help\fR for more detail\.
|
|
98
120
|
.
|
|
99
121
|
.IP "" 0
|
|
@@ -110,39 +132,67 @@ Obtaining OAuth2 access_token from github\.
|
|
|
110
132
|
GitHub username: ConradIrwin
|
|
111
133
|
GitHub password:
|
|
112
134
|
2\-factor auth code:
|
|
113
|
-
Success! https://github\.com/settings/
|
|
135
|
+
Success! https://github\.com/settings/tokens
|
|
114
136
|
.
|
|
115
137
|
.fi
|
|
116
138
|
.
|
|
117
139
|
.IP "" 0
|
|
118
140
|
.
|
|
119
141
|
.P
|
|
120
|
-
|
|
142
|
+
This token is stored in \fB~/\.gist\fR and used for all future gisting\. If you need to you can revoke it from https://github\.com/settings/tokens, or just delete the file\.
|
|
143
|
+
.
|
|
144
|
+
.IP "\(bu" 4
|
|
145
|
+
After you\'ve done this, you can still upload gists anonymously with \fB\-a\fR\.
|
|
146
|
+
.
|
|
147
|
+
.IP
|
|
148
|
+
gist \-a a\.rb
|
|
149
|
+
.
|
|
150
|
+
.IP "" 0
|
|
151
|
+
.
|
|
152
|
+
.P
|
|
153
|
+
If you have a complicated authorization requirement you can manually create a token file by pasting a Github token with only the \fBgist\fR permission into a file called \fB~/\.gist\fR\. You can create one from https://github\.com/settings/tokens
|
|
121
154
|
.
|
|
122
155
|
.P
|
|
123
|
-
|
|
156
|
+
This file should contain only the token (~40 hex characters), and to make it easier to edit, can optionally have a final newline (\en or \er\en)\.
|
|
157
|
+
.
|
|
158
|
+
.P
|
|
159
|
+
For example, one way to create this file would be to run:
|
|
124
160
|
.
|
|
125
161
|
.IP "" 4
|
|
126
162
|
.
|
|
127
163
|
.nf
|
|
128
164
|
|
|
129
|
-
|
|
165
|
+
echo MY_SECRET_TOKEN > ~/\.gist
|
|
130
166
|
.
|
|
131
167
|
.fi
|
|
132
168
|
.
|
|
133
169
|
.IP "" 0
|
|
134
170
|
.
|
|
135
|
-
.
|
|
136
|
-
|
|
171
|
+
.SS "GitHub Enterprise"
|
|
172
|
+
If you\'d like \fBgist\fR to use your locally installed GitHub Enterprise \fIhttps://enterprise\.github\.com/\fR, you need to export the \fBGITHUB_URL\fR environment variable (usually done in your \fB~/\.bashrc\fR)\.
|
|
137
173
|
.
|
|
138
|
-
.IP "
|
|
139
|
-
After you\'ve done this, you can still upload gists anonymously with \fB\-a\fR\.
|
|
174
|
+
.IP "" 4
|
|
140
175
|
.
|
|
141
|
-
.
|
|
142
|
-
|
|
176
|
+
.nf
|
|
177
|
+
|
|
178
|
+
export GITHUB_URL=http://github\.internal\.example\.com/
|
|
179
|
+
.
|
|
180
|
+
.fi
|
|
143
181
|
.
|
|
144
182
|
.IP "" 0
|
|
145
183
|
.
|
|
184
|
+
.P
|
|
185
|
+
Once you\'ve done this and restarted your terminal (or run \fBsource ~/\.bashrc\fR), gist will automatically use github enterprise instead of the public github\.com
|
|
186
|
+
.
|
|
187
|
+
.P
|
|
188
|
+
Your token for GitHub Enterprise will be stored in \fB\.gist\.<protocol>\.<server\.name>[\.<port>]\fR (e\.g\. \fB~/\.gist\.http\.github\.internal\.example\.com\fR for the GITHUB_URL example above) instead of \fB~/\.gist\fR\.
|
|
189
|
+
.
|
|
190
|
+
.P
|
|
191
|
+
If you have multiple servers or use Enterprise and public GitHub often, you can work around this by creating scripts that set the env var and then run \fBgist\fR\. Keep in mind that to use the public GitHub you must unset the env var\. Just setting it to the public URL will not work\. Use \fBunset GITHUB_URL\fR
|
|
192
|
+
.
|
|
193
|
+
.SS "Token file format"
|
|
194
|
+
If you cannot use passwords, as most Enterprise installations do, you can generate the token via the web interface and then simply save the string in the correct file\. Avoid line breaks or you might see: \fB$ gist \-l Error: Bad credentials\fR
|
|
195
|
+
.
|
|
146
196
|
.TP
|
|
147
197
|
You can also use Gist as a library from inside your ruby code:
|
|
148
198
|
.
|
|
@@ -198,19 +248,6 @@ This will take them through the process of obtaining an OAuth2 token, and storin
|
|
|
198
248
|
.
|
|
199
249
|
.IP "" 0
|
|
200
250
|
.
|
|
201
|
-
.SH "GitHub enterprise"
|
|
202
|
-
.
|
|
203
|
-
.IP "\(bu" 4
|
|
204
|
-
If you\'d like \fBgist\fR to use your locally installed GitHub Enterprise \fIhttps://enterprise\.github\.com/\fR, you need to export the \fBGITHUB_URL\fR environment variable in your \fB~/\.bashrc\fR\.
|
|
205
|
-
.
|
|
206
|
-
.IP
|
|
207
|
-
export GITHUB_URL=http://github\.internal\.example\.com/
|
|
208
|
-
.
|
|
209
|
-
.IP "\(bu" 4
|
|
210
|
-
Once you\'ve done this and restarted your terminal (or run \fBsource ~/\.bashrc\fR), gist will automatically use github enterprise instead of the public github\.com
|
|
211
|
-
.
|
|
212
|
-
.IP "" 0
|
|
213
|
-
.
|
|
214
251
|
.SH "Configuration"
|
|
215
252
|
.
|
|
216
253
|
.IP "\(bu" 4
|
data/gist.gemspec
CHANGED
|
@@ -14,7 +14,8 @@ Gem::Specification.new do |s|
|
|
|
14
14
|
|
|
15
15
|
s.executables << 'gist'
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
s.add_development_dependency 'rake'
|
|
18
|
+
s.add_development_dependency 'ronn'
|
|
19
|
+
s.add_development_dependency 'webmock'
|
|
20
|
+
s.add_development_dependency 'rspec', '>3'
|
|
20
21
|
end
|
data/lib/gist.rb
CHANGED
|
@@ -12,18 +12,18 @@ end
|
|
|
12
12
|
module Gist
|
|
13
13
|
extend self
|
|
14
14
|
|
|
15
|
-
VERSION = '4.2
|
|
15
|
+
VERSION = '4.6.2'
|
|
16
16
|
|
|
17
17
|
# A list of clipboard commands with copy and paste support.
|
|
18
18
|
CLIPBOARD_COMMANDS = {
|
|
19
|
-
'xclip' => 'xclip -o',
|
|
20
|
-
'xsel -i' => 'xsel -o',
|
|
21
19
|
'pbcopy' => 'pbpaste',
|
|
22
|
-
'
|
|
20
|
+
'xclip' => 'xclip -o',
|
|
21
|
+
'xsel -i' => 'xsel -o',
|
|
22
|
+
'putclip' => 'getclip',
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
GITHUB_API_URL = URI("https://api.github.com/")
|
|
26
|
-
GIT_IO_URL = URI("
|
|
26
|
+
GIT_IO_URL = URI("https://git.io")
|
|
27
27
|
|
|
28
28
|
GITHUB_BASE_PATH = ""
|
|
29
29
|
GHE_BASE_PATH = "/api/v3"
|
|
@@ -40,6 +40,34 @@ module Gist
|
|
|
40
40
|
end
|
|
41
41
|
class ClipboardError < RuntimeError; include Error end
|
|
42
42
|
|
|
43
|
+
# helper module for authentication token actions
|
|
44
|
+
module AuthTokenFile
|
|
45
|
+
def self.filename
|
|
46
|
+
if ENV.key?(URL_ENV_NAME)
|
|
47
|
+
File.expand_path "~/.gist.#{ENV[URL_ENV_NAME].gsub(/:/, '.').gsub(/[^a-z0-9.]/, '')}"
|
|
48
|
+
else
|
|
49
|
+
File.expand_path "~/.gist"
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def self.read
|
|
54
|
+
File.read(filename).chomp
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def self.write(token)
|
|
58
|
+
File.open(filename, 'w', 0600) do |f|
|
|
59
|
+
f.write token
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# auth token for authentication
|
|
65
|
+
#
|
|
66
|
+
# @return [String] string value of access token or `nil`, if not found
|
|
67
|
+
def auth_token
|
|
68
|
+
@token ||= AuthTokenFile.read rescue nil
|
|
69
|
+
end
|
|
70
|
+
|
|
43
71
|
# Upload a gist to https://gist.github.com
|
|
44
72
|
#
|
|
45
73
|
# @param [String] content the code you'd like to gist
|
|
@@ -48,10 +76,14 @@ module Gist
|
|
|
48
76
|
#
|
|
49
77
|
# @see http://developer.github.com/v3/gists/
|
|
50
78
|
def gist(content, options = {})
|
|
51
|
-
filename = options[:filename] ||
|
|
79
|
+
filename = options[:filename] || default_filename
|
|
52
80
|
multi_gist({filename => content}, options)
|
|
53
81
|
end
|
|
54
82
|
|
|
83
|
+
def default_filename
|
|
84
|
+
"gistfile1.txt"
|
|
85
|
+
end
|
|
86
|
+
|
|
55
87
|
# Upload a gist to https://gist.github.com
|
|
56
88
|
#
|
|
57
89
|
# @param [Hash] files the code you'd like to gist: filename => content
|
|
@@ -64,6 +96,7 @@ module Gist
|
|
|
64
96
|
# @option options [String] :update the URL or id of a gist to update
|
|
65
97
|
# @option options [Boolean] :copy (false) Copy resulting URL to clipboard, if successful.
|
|
66
98
|
# @option options [Boolean] :open (false) Open the resulting URL in a browser.
|
|
99
|
+
# @option options [Boolean] :skip_empty (false) Skip gisting empty files.
|
|
67
100
|
# @option options [Symbol] :output (:all) The type of return value you'd like:
|
|
68
101
|
# :html_url gives a String containing the url to the gist in a browser
|
|
69
102
|
# :short_url gives a String contianing a git.io url that redirects to html_url
|
|
@@ -82,15 +115,21 @@ module Gist
|
|
|
82
115
|
json[:files] = {}
|
|
83
116
|
|
|
84
117
|
files.each_pair do |(name, content)|
|
|
85
|
-
|
|
86
|
-
|
|
118
|
+
if content.to_s.strip == ""
|
|
119
|
+
raise "Cannot gist empty files" unless options[:skip_empty]
|
|
120
|
+
else
|
|
121
|
+
name = name == "-" ? default_filename : File.basename(name)
|
|
122
|
+
json[:files][name] = {:content => content}
|
|
123
|
+
end
|
|
87
124
|
end
|
|
88
125
|
|
|
126
|
+
return if json[:files].empty? && options[:skip_empty]
|
|
127
|
+
|
|
89
128
|
existing_gist = options[:update].to_s.split("/").last
|
|
90
129
|
if options[:anonymous]
|
|
91
130
|
access_token = nil
|
|
92
131
|
else
|
|
93
|
-
access_token = (options[:access_token] ||
|
|
132
|
+
access_token = (options[:access_token] || auth_token())
|
|
94
133
|
end
|
|
95
134
|
|
|
96
135
|
url = "#{base_path}/gists"
|
|
@@ -120,15 +159,154 @@ module Gist
|
|
|
120
159
|
raise e.extend Error
|
|
121
160
|
end
|
|
122
161
|
|
|
162
|
+
# List all gists(private also) for authenticated user
|
|
163
|
+
# otherwise list public gists for given username (optional argument)
|
|
164
|
+
#
|
|
165
|
+
# @param [String] user
|
|
166
|
+
# @deprecated
|
|
167
|
+
#
|
|
168
|
+
# see https://developer.github.com/v3/gists/#list-gists
|
|
169
|
+
def list_gists(user = "")
|
|
170
|
+
url = "#{base_path}"
|
|
171
|
+
|
|
172
|
+
if user == ""
|
|
173
|
+
access_token = auth_token()
|
|
174
|
+
if access_token.to_s != ''
|
|
175
|
+
url << "/gists?access_token=" << CGI.escape(access_token)
|
|
176
|
+
|
|
177
|
+
request = Net::HTTP::Get.new(url)
|
|
178
|
+
response = http(api_url, request)
|
|
179
|
+
|
|
180
|
+
pretty_gist(response)
|
|
181
|
+
|
|
182
|
+
else
|
|
183
|
+
raise Error, "Not authenticated. Use 'gist --login' to login or 'gist -l username' to view public gists."
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
else
|
|
187
|
+
url << "/users/#{user}/gists"
|
|
188
|
+
|
|
189
|
+
request = Net::HTTP::Get.new(url)
|
|
190
|
+
response = http(api_url, request)
|
|
191
|
+
|
|
192
|
+
pretty_gist(response)
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def list_all_gists(user = "")
|
|
197
|
+
url = "#{base_path}"
|
|
198
|
+
|
|
199
|
+
if user == ""
|
|
200
|
+
access_token = auth_token()
|
|
201
|
+
if access_token.to_s != ''
|
|
202
|
+
url << "/gists?per_page=100&access_token=" << CGI.escape(access_token)
|
|
203
|
+
get_gist_pages(url)
|
|
204
|
+
else
|
|
205
|
+
raise Error, "Not authenticated. Use 'gist --login' to login or 'gist -l username' to view public gists."
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
else
|
|
209
|
+
url << "/users/#{user}/gists?per_page=100"
|
|
210
|
+
get_gist_pages(url)
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
def read_gist(id, file_name=nil)
|
|
216
|
+
url = "#{base_path}/gists/#{id}"
|
|
217
|
+
|
|
218
|
+
access_token = auth_token()
|
|
219
|
+
if access_token.to_s != ''
|
|
220
|
+
url << "?access_token=" << CGI.escape(access_token)
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
request = Net::HTTP::Get.new(url)
|
|
224
|
+
response = http(api_url, request)
|
|
225
|
+
|
|
226
|
+
if response.code == '200'
|
|
227
|
+
body = JSON.parse(response.body)
|
|
228
|
+
files = body["files"]
|
|
229
|
+
|
|
230
|
+
if file_name
|
|
231
|
+
file = files[file_name]
|
|
232
|
+
raise Error, "Gist with id of #{id} and file #{file_name} does not exist." unless file
|
|
233
|
+
else
|
|
234
|
+
file = files.values.first
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
puts file["content"]
|
|
238
|
+
else
|
|
239
|
+
raise Error, "Gist with id of #{id} does not exist."
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
def delete_gist(id)
|
|
244
|
+
id = id.split("/").last
|
|
245
|
+
url = "#{base_path}/gists/#{id}"
|
|
246
|
+
|
|
247
|
+
access_token = auth_token()
|
|
248
|
+
if access_token.to_s != ''
|
|
249
|
+
url << "?access_token=" << CGI.escape(access_token)
|
|
250
|
+
|
|
251
|
+
request = Net::HTTP::Delete.new(url)
|
|
252
|
+
response = http(api_url, request)
|
|
253
|
+
else
|
|
254
|
+
raise Error, "Not authenticated. Use 'gist --login' to login."
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
if response.code == '204'
|
|
258
|
+
puts "Deleted!"
|
|
259
|
+
else
|
|
260
|
+
raise Error, "Gist with id of #{id} does not exist."
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
def get_gist_pages(url)
|
|
265
|
+
|
|
266
|
+
request = Net::HTTP::Get.new(url)
|
|
267
|
+
response = http(api_url, request)
|
|
268
|
+
pretty_gist(response)
|
|
269
|
+
|
|
270
|
+
link_header = response.header['link']
|
|
271
|
+
|
|
272
|
+
if link_header
|
|
273
|
+
links = Hash[ link_header.gsub(/(<|>|")/, "").split(',').map { |link| link.split('; rel=') } ].invert
|
|
274
|
+
get_gist_pages(links['next']) if links['next']
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
# return prettified string result of response body for all gists
|
|
280
|
+
#
|
|
281
|
+
# @params [Net::HTTPResponse] response
|
|
282
|
+
# @return [String] prettified result of listing all gists
|
|
283
|
+
#
|
|
284
|
+
# see https://developer.github.com/v3/gists/#response
|
|
285
|
+
def pretty_gist(response)
|
|
286
|
+
body = JSON.parse(response.body)
|
|
287
|
+
if response.code == '200'
|
|
288
|
+
body.each do |gist|
|
|
289
|
+
description = "#{gist['description'] || gist['files'].keys.join(" ")} #{gist['public'] ? '' : '(secret)'}"
|
|
290
|
+
puts "#{gist['html_url']} #{description.tr("\n", " ")}\n"
|
|
291
|
+
$stdout.flush
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
else
|
|
295
|
+
raise Error, body['message']
|
|
296
|
+
end
|
|
297
|
+
end
|
|
298
|
+
|
|
123
299
|
# Convert long github urls into short git.io ones
|
|
124
300
|
#
|
|
125
301
|
# @param [String] url
|
|
126
302
|
# @return [String] shortened url, or long url if shortening fails
|
|
127
303
|
def shorten(url)
|
|
128
|
-
request = Net::HTTP::Post.new("/")
|
|
304
|
+
request = Net::HTTP::Post.new("/create")
|
|
129
305
|
request.set_form_data(:url => url)
|
|
130
306
|
response = http(GIT_IO_URL, request)
|
|
131
307
|
case response.code
|
|
308
|
+
when "200"
|
|
309
|
+
URI.join(GIT_IO_URL, response.body).to_s
|
|
132
310
|
when "201"
|
|
133
311
|
response['Location']
|
|
134
312
|
else
|
|
@@ -200,10 +378,8 @@ module Gist
|
|
|
200
378
|
end
|
|
201
379
|
|
|
202
380
|
if Net::HTTPCreated === response
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
end
|
|
206
|
-
puts "Success! #{ENV[URL_ENV_NAME] || "https://github.com/"}settings/applications"
|
|
381
|
+
AuthTokenFile.write JSON.parse(response.body)['token']
|
|
382
|
+
puts "Success! #{ENV[URL_ENV_NAME] || "https://github.com/"}settings/tokens"
|
|
207
383
|
return
|
|
208
384
|
elsif Net::HTTPUnauthorized === response
|
|
209
385
|
puts "Error: #{JSON.parse(response.body)['message']}"
|
|
@@ -383,14 +559,6 @@ Could not find copy command, tried:
|
|
|
383
559
|
ENV.key?(URL_ENV_NAME) ? URI(ENV[URL_ENV_NAME]) : GITHUB_API_URL
|
|
384
560
|
end
|
|
385
561
|
|
|
386
|
-
def auth_token_file
|
|
387
|
-
if ENV.key?(URL_ENV_NAME)
|
|
388
|
-
File.expand_path "~/.gist.#{ENV[URL_ENV_NAME].gsub(/[^a-z.]/, '')}"
|
|
389
|
-
else
|
|
390
|
-
File.expand_path "~/.gist"
|
|
391
|
-
end
|
|
392
|
-
end
|
|
393
|
-
|
|
394
562
|
def legacy_private_gister?
|
|
395
563
|
return unless which('git')
|
|
396
564
|
`git config --global gist.private` =~ /\Ayes|1|true|on\z/i
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
describe Gist::AuthTokenFile do
|
|
2
|
+
subject { Gist::AuthTokenFile }
|
|
3
|
+
|
|
4
|
+
before(:each) do
|
|
5
|
+
stub_const("Gist::URL_ENV_NAME", "STUBBED_GITHUB_URL")
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
describe "::filename" do
|
|
9
|
+
let(:filename) { double() }
|
|
10
|
+
|
|
11
|
+
context "with default GITHUB_URL" do
|
|
12
|
+
it "is ~/.gist" do
|
|
13
|
+
File.should_receive(:expand_path).with("~/.gist").and_return(filename)
|
|
14
|
+
subject.filename.should be filename
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
context "with custom GITHUB_URL" do
|
|
19
|
+
before do
|
|
20
|
+
ENV[Gist::URL_ENV_NAME] = github_url
|
|
21
|
+
end
|
|
22
|
+
let(:github_url) { "http://gh.custom.org:442/" }
|
|
23
|
+
|
|
24
|
+
it "is ~/.gist.{custom_github_url}" do
|
|
25
|
+
File.should_receive(:expand_path).with("~/.gist.http.gh.custom.org.442").and_return(filename)
|
|
26
|
+
subject.filename.should be filename
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
describe "::read" do
|
|
33
|
+
let(:token) { "auth_token" }
|
|
34
|
+
|
|
35
|
+
it "reads file contents" do
|
|
36
|
+
File.should_receive(:read).and_return(token)
|
|
37
|
+
subject.read.should eq token
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it "chomps file contents" do
|
|
41
|
+
File.should_receive(:read).and_return(token + "\n")
|
|
42
|
+
subject.read.should eq token
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
describe "::write" do
|
|
47
|
+
let(:token) { double() }
|
|
48
|
+
let(:filename) { double() }
|
|
49
|
+
let(:token_file) { double() }
|
|
50
|
+
|
|
51
|
+
before do
|
|
52
|
+
subject.stub(:filename) { filename }
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it "writes token to file" do
|
|
56
|
+
File.should_receive(:open).with(filename, 'w', 0600).and_yield(token_file)
|
|
57
|
+
token_file.should_receive(:write).with(token)
|
|
58
|
+
subject.write(token)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
data/spec/ghe_spec.rb
CHANGED
|
@@ -63,7 +63,7 @@ describe '...' do
|
|
|
63
63
|
|
|
64
64
|
it "uses them" do
|
|
65
65
|
$stdin = StringIO.new "#{MOCK_USER}_wrong\n#{MOCK_PASSWORD}_wrong\n"
|
|
66
|
-
Gist.login! username
|
|
66
|
+
Gist.login! :username => MOCK_USER, :password => MOCK_PASSWORD
|
|
67
67
|
|
|
68
68
|
assert_requested(:post, /#{MOCK_AUTHZ_GITHUB_URL}authorizations/)
|
|
69
69
|
end
|
data/spec/gist_spec.rb
CHANGED
|
@@ -2,21 +2,21 @@ describe Gist do
|
|
|
2
2
|
|
|
3
3
|
describe "should_be_public?" do
|
|
4
4
|
it "should return false if -p is specified" do
|
|
5
|
-
Gist.should_be_public?(private
|
|
5
|
+
Gist.should_be_public?(:private => true).should be_falsy
|
|
6
6
|
end
|
|
7
7
|
|
|
8
8
|
it "should return false if legacy_private_gister?" do
|
|
9
9
|
Gist.should_receive(:legacy_private_gister?).and_return(true)
|
|
10
|
-
Gist.should_be_public?.should
|
|
10
|
+
Gist.should_be_public?.should be_falsy
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
it "should return true if --no-private is specified" do
|
|
14
14
|
Gist.stub(:legacy_private_gister?).and_return(true)
|
|
15
|
-
Gist.should_be_public?(private
|
|
15
|
+
Gist.should_be_public?(:private => false).should be_truthy
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
it "should return true by default" do
|
|
19
|
-
Gist.should_be_public?.should
|
|
19
|
+
Gist.should_be_public?.should be_truthy
|
|
20
20
|
end
|
|
21
21
|
end
|
|
22
22
|
|
data/spec/shorten_spec.rb
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
describe '...' do
|
|
2
2
|
before do
|
|
3
3
|
stub_request(:post, /api\.github.com\/gists/).to_return(:body => '{"html_url": "http://github.com/"}')
|
|
4
|
-
stub_request(:post, "http://git.io/").to_return(:status => 201, :headers => { 'Location' => 'http://git.io/XXXXXX' })
|
|
5
4
|
end
|
|
6
5
|
|
|
7
|
-
it "should return a shortened version of the URL" do
|
|
8
|
-
|
|
6
|
+
it "should return a shortened version of the URL when response is 200" do
|
|
7
|
+
stub_request(:post, "https://git.io/create").to_return(:status => 200, :body => 'XXXXXX')
|
|
8
|
+
Gist.gist("Test gist", :output => :short_url, :anonymous => true).should == "https://git.io/XXXXXX"
|
|
9
9
|
end
|
|
10
|
-
end
|
|
11
10
|
|
|
11
|
+
it "should return a shortened version of the URL when response is 201" do
|
|
12
|
+
stub_request(:post, "https://git.io/create").to_return(:status => 201, :headers => { 'Location' => 'https://git.io/XXXXXX' })
|
|
13
|
+
Gist.gist("Test gist", :output => :short_url, :anonymous => true).should == "https://git.io/XXXXXX"
|
|
14
|
+
end
|
|
15
|
+
end
|
data/spec/spec_helper.rb
CHANGED
|
@@ -5,11 +5,15 @@
|
|
|
5
5
|
#
|
|
6
6
|
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
|
7
7
|
RSpec.configure do |config|
|
|
8
|
-
config.treat_symbols_as_metadata_keys_with_true_values = true
|
|
9
8
|
config.run_all_when_everything_filtered = true
|
|
10
9
|
config.filter_run :focus
|
|
10
|
+
config.mock_with :rspec do |mocks|
|
|
11
|
+
mocks.syntax = :should
|
|
12
|
+
end
|
|
13
|
+
config.expect_with :rspec do |expectations|
|
|
14
|
+
expectations.syntax = :should
|
|
15
|
+
end
|
|
11
16
|
end
|
|
12
17
|
|
|
13
18
|
require 'webmock/rspec'
|
|
14
|
-
|
|
15
|
-
|
|
19
|
+
require 'gist'
|
metadata
CHANGED
|
@@ -1,72 +1,72 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: gist
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 4.2
|
|
4
|
+
version: 4.6.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Conrad Irwin
|
|
8
|
-
- ☈king
|
|
8
|
+
- "☈king"
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date:
|
|
12
|
+
date: 2018-01-09 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: rake
|
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
|
17
17
|
requirements:
|
|
18
|
-
- -
|
|
18
|
+
- - ">="
|
|
19
19
|
- !ruby/object:Gem::Version
|
|
20
20
|
version: '0'
|
|
21
21
|
type: :development
|
|
22
22
|
prerelease: false
|
|
23
23
|
version_requirements: !ruby/object:Gem::Requirement
|
|
24
24
|
requirements:
|
|
25
|
-
- -
|
|
25
|
+
- - ">="
|
|
26
26
|
- !ruby/object:Gem::Version
|
|
27
27
|
version: '0'
|
|
28
28
|
- !ruby/object:Gem::Dependency
|
|
29
|
-
name:
|
|
29
|
+
name: ronn
|
|
30
30
|
requirement: !ruby/object:Gem::Requirement
|
|
31
31
|
requirements:
|
|
32
|
-
- -
|
|
32
|
+
- - ">="
|
|
33
33
|
- !ruby/object:Gem::Version
|
|
34
34
|
version: '0'
|
|
35
35
|
type: :development
|
|
36
36
|
prerelease: false
|
|
37
37
|
version_requirements: !ruby/object:Gem::Requirement
|
|
38
38
|
requirements:
|
|
39
|
-
- -
|
|
39
|
+
- - ">="
|
|
40
40
|
- !ruby/object:Gem::Version
|
|
41
41
|
version: '0'
|
|
42
42
|
- !ruby/object:Gem::Dependency
|
|
43
43
|
name: webmock
|
|
44
44
|
requirement: !ruby/object:Gem::Requirement
|
|
45
45
|
requirements:
|
|
46
|
-
- -
|
|
46
|
+
- - ">="
|
|
47
47
|
- !ruby/object:Gem::Version
|
|
48
48
|
version: '0'
|
|
49
49
|
type: :development
|
|
50
50
|
prerelease: false
|
|
51
51
|
version_requirements: !ruby/object:Gem::Requirement
|
|
52
52
|
requirements:
|
|
53
|
-
- -
|
|
53
|
+
- - ">="
|
|
54
54
|
- !ruby/object:Gem::Version
|
|
55
55
|
version: '0'
|
|
56
56
|
- !ruby/object:Gem::Dependency
|
|
57
|
-
name:
|
|
57
|
+
name: rspec
|
|
58
58
|
requirement: !ruby/object:Gem::Requirement
|
|
59
59
|
requirements:
|
|
60
|
-
- -
|
|
60
|
+
- - ">"
|
|
61
61
|
- !ruby/object:Gem::Version
|
|
62
|
-
version: '
|
|
62
|
+
version: '3'
|
|
63
63
|
type: :development
|
|
64
64
|
prerelease: false
|
|
65
65
|
version_requirements: !ruby/object:Gem::Requirement
|
|
66
66
|
requirements:
|
|
67
|
-
- -
|
|
67
|
+
- - ">"
|
|
68
68
|
- !ruby/object:Gem::Version
|
|
69
|
-
version: '
|
|
69
|
+
version: '3'
|
|
70
70
|
description: Provides a single function (Gist.gist) that uploads a gist.
|
|
71
71
|
email:
|
|
72
72
|
- conrad.irwin@gmail.com
|
|
@@ -76,8 +76,8 @@ executables:
|
|
|
76
76
|
extensions: []
|
|
77
77
|
extra_rdoc_files: []
|
|
78
78
|
files:
|
|
79
|
-
- .gitignore
|
|
80
|
-
- .rspec
|
|
79
|
+
- ".gitignore"
|
|
80
|
+
- ".rspec"
|
|
81
81
|
- Gemfile
|
|
82
82
|
- LICENSE.MIT
|
|
83
83
|
- README.md
|
|
@@ -87,6 +87,7 @@ files:
|
|
|
87
87
|
- build/gist.1
|
|
88
88
|
- gist.gemspec
|
|
89
89
|
- lib/gist.rb
|
|
90
|
+
- spec/auth_token_file_spec.rb
|
|
90
91
|
- spec/clipboard_spec.rb
|
|
91
92
|
- spec/ghe_spec.rb
|
|
92
93
|
- spec/gist_spec.rb
|
|
@@ -105,19 +106,18 @@ require_paths:
|
|
|
105
106
|
- lib
|
|
106
107
|
required_ruby_version: !ruby/object:Gem::Requirement
|
|
107
108
|
requirements:
|
|
108
|
-
- -
|
|
109
|
+
- - ">="
|
|
109
110
|
- !ruby/object:Gem::Version
|
|
110
111
|
version: '0'
|
|
111
112
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
112
113
|
requirements:
|
|
113
|
-
- -
|
|
114
|
+
- - ">="
|
|
114
115
|
- !ruby/object:Gem::Version
|
|
115
116
|
version: '0'
|
|
116
117
|
requirements: []
|
|
117
118
|
rubyforge_project:
|
|
118
|
-
rubygems_version: 2.
|
|
119
|
+
rubygems_version: 2.6.11
|
|
119
120
|
signing_key:
|
|
120
121
|
specification_version: 4
|
|
121
122
|
summary: Just allows you to upload gists
|
|
122
123
|
test_files: []
|
|
123
|
-
has_rdoc:
|