gist 4.5.0 → 4.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +34 -15
- data/Rakefile +3 -1
- data/bin/gist +9 -0
- data/build/gist +331 -8
- data/build/gist.1 +44 -16
- data/lib/gist.rb +37 -8
- data/spec/auth_token_file_spec.rb +2 -2
- data/spec/shorten_spec.rb +8 -4
- data/vendor/netrc.rb +286 -0
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 306aea3698bda142b6130a3160a10c7cfc816142
|
4
|
+
data.tar.gz: b5cc50975bfa626326ac4c865f2d1849d3b95d50
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4f65b5b05ce628788db1fc1324191fa51c56f39827474efb44c0cbcef690001a89be5626b490a0111989ccea4a882fcb2e1d360cc75742d58fb2846e285a2129
|
7
|
+
data.tar.gz: 339e1387cb0f267a8a49e37963fc01fc353d6e77c7e1546ad029234c21672580ffac2ce0366e9443d22a020b9f92e5f4ac7c9cb30decc18ac3bb5ff5436fe3d6
|
data/README.md
CHANGED
@@ -70,6 +70,11 @@ upload content to https://gist.github.com/.
|
|
70
70
|
gist -l : all gists for authed user
|
71
71
|
gist -l defunkt : list defunkt's public gists
|
72
72
|
|
73
|
+
To read a gist and print it to STDOUT
|
74
|
+
|
75
|
+
gist -r GIST_ID
|
76
|
+
gist -r 374130
|
77
|
+
|
73
78
|
See `gist --help` for more detail.
|
74
79
|
|
75
80
|
## Login
|
@@ -83,18 +88,42 @@ an OAuth2 token (with the "gist" permission).
|
|
83
88
|
GitHub username: ConradIrwin
|
84
89
|
GitHub password:
|
85
90
|
2-factor auth code:
|
86
|
-
Success! https://github.com/settings/
|
91
|
+
Success! https://github.com/settings/tokens
|
87
92
|
|
88
93
|
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
|
-
you can save your Github Enterprise token in `~/.gist.github.example.com` where
|
92
|
-
"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.
|
93
96
|
|
94
97
|
After you've done this, you can still upload gists anonymously with `-a`.
|
95
98
|
|
96
99
|
gist -a a.rb
|
97
100
|
|
101
|
+
### GitHub Enterprise
|
102
|
+
|
103
|
+
If you'd like `gist` to use your locally installed [GitHub Enterprise](https://enterprise.github.com/),
|
104
|
+
you need to export the `GITHUB_URL` environment variable (usually done in your `~/.bashrc`).
|
105
|
+
|
106
|
+
export GITHUB_URL=http://github.internal.example.com/
|
107
|
+
|
108
|
+
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
|
110
|
+
|
111
|
+
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`.
|
113
|
+
|
114
|
+
If you have multiple servers or use Enterprise and public GitHub often, you can work around this by creating scripts
|
115
|
+
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
|
116
|
+
setting it to the public URL will not work. Use `unset GITHUB_URL`
|
117
|
+
|
118
|
+
### Token file format
|
119
|
+
|
120
|
+
If you cannot use passwords, as most Enterprise installations do, you can generate the token via the web interface
|
121
|
+
and then simply save the string in the correct file. Avoid line breaks or you might see:
|
122
|
+
```
|
123
|
+
$ gist -l
|
124
|
+
Error: Bad credentials
|
125
|
+
```
|
126
|
+
|
98
127
|
# Library
|
99
128
|
|
100
129
|
You can also use Gist as a library from inside your ruby code:
|
@@ -126,16 +155,6 @@ NOTE: The access_token must have the "gist" scope.
|
|
126
155
|
This will take them through the process of obtaining an OAuth2 token, and storing it
|
127
156
|
in `~/.gist`, where it can later be read by `Gist.gist`
|
128
157
|
|
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
158
|
## Configuration
|
140
159
|
|
141
160
|
If you'd like `-o` or `-c` to be the default when you use the gist executable, add an
|
data/Rakefile
CHANGED
@@ -23,7 +23,9 @@ 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")
|
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"))
|
27
29
|
|
28
30
|
f.puts File.read("bin/gist").gsub(/^require.*gist.*\n/, '');
|
29
31
|
end
|
data/bin/gist
CHANGED
@@ -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
|
@@ -167,6 +171,11 @@ begin
|
|
167
171
|
exit
|
168
172
|
end
|
169
173
|
|
174
|
+
if options.key? :delete
|
175
|
+
Gist.delete_gist(options[:delete])
|
176
|
+
exit
|
177
|
+
end
|
178
|
+
|
170
179
|
if options[:paste]
|
171
180
|
puts Gist.gist(Gist.paste, options)
|
172
181
|
else
|
data/build/gist
CHANGED
@@ -1314,22 +1314,313 @@ rescue LoadError
|
|
1314
1314
|
require File.join File.dirname(File.dirname(__FILE__)), 'vendor', 'json.rb'
|
1315
1315
|
end
|
1316
1316
|
|
1317
|
+
begin
|
1318
|
+
|
1319
|
+
# The netrc library (https://github.com/heroku/netrc) is reproduced below, along
|
1320
|
+
# with its copyright and license:
|
1321
|
+
# - https://raw.githubusercontent.com/heroku/netrc/262ef111/LICENSE.md
|
1322
|
+
# - https://raw.githubusercontent.com/heroku/netrc/262ef111/lib/netrc.rb
|
1323
|
+
|
1324
|
+
# The MIT License (MIT)
|
1325
|
+
#
|
1326
|
+
# Copyright (c) 2011-2014 [CONTRIBUTORS.md](https://github.com/geemus/netrc/blob/master/CONTRIBUTORS.md)
|
1327
|
+
#
|
1328
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy of
|
1329
|
+
# this software and associated documentation files (the "Software"), to deal in
|
1330
|
+
# the Software without restriction, including without limitation the rights to
|
1331
|
+
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
1332
|
+
# the Software, and to permit persons to whom the Software is furnished to do so,
|
1333
|
+
# subject to the following conditions:
|
1334
|
+
#
|
1335
|
+
# The above copyright notice and this permission notice shall be included in all
|
1336
|
+
# copies or substantial portions of the Software.
|
1337
|
+
#
|
1338
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
1339
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
1340
|
+
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
1341
|
+
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
1342
|
+
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
1343
|
+
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
1344
|
+
|
1345
|
+
require 'rbconfig'
|
1346
|
+
require 'io/console'
|
1347
|
+
|
1348
|
+
class Netrc
|
1349
|
+
VERSION = "0.11.0"
|
1350
|
+
|
1351
|
+
# see http://stackoverflow.com/questions/4871309/what-is-the-correct-way-to-detect-if-ruby-is-running-on-windows
|
1352
|
+
WINDOWS = RbConfig::CONFIG["host_os"] =~ /mswin|mingw|cygwin/
|
1353
|
+
CYGWIN = RbConfig::CONFIG["host_os"] =~ /cygwin/
|
1354
|
+
|
1355
|
+
def self.default_path
|
1356
|
+
File.join(ENV['NETRC'] || home_path, netrc_filename)
|
1357
|
+
end
|
1358
|
+
|
1359
|
+
def self.home_path
|
1360
|
+
home = Dir.respond_to?(:home) ? Dir.home : ENV['HOME']
|
1361
|
+
|
1362
|
+
if WINDOWS && !CYGWIN
|
1363
|
+
home ||= File.join(ENV['HOMEDRIVE'], ENV['HOMEPATH']) if ENV['HOMEDRIVE'] && ENV['HOMEPATH']
|
1364
|
+
home ||= ENV['USERPROFILE']
|
1365
|
+
# XXX: old stuff; most likely unnecessary
|
1366
|
+
home = home.tr("\\", "/") unless home.nil?
|
1367
|
+
end
|
1368
|
+
|
1369
|
+
(home && File.readable?(home)) ? home : Dir.pwd
|
1370
|
+
rescue ArgumentError
|
1371
|
+
return Dir.pwd
|
1372
|
+
end
|
1373
|
+
|
1374
|
+
def self.netrc_filename
|
1375
|
+
WINDOWS && !CYGWIN ? "_netrc" : ".netrc"
|
1376
|
+
end
|
1377
|
+
|
1378
|
+
def self.config
|
1379
|
+
@config ||= {}
|
1380
|
+
end
|
1381
|
+
|
1382
|
+
def self.configure
|
1383
|
+
yield(self.config) if block_given?
|
1384
|
+
self.config
|
1385
|
+
end
|
1386
|
+
|
1387
|
+
def self.check_permissions(path)
|
1388
|
+
perm = File.stat(path).mode & 0777
|
1389
|
+
if perm != 0600 && !(WINDOWS) && !(Netrc.config[:allow_permissive_netrc_file])
|
1390
|
+
raise Error, "Permission bits for '#{path}' should be 0600, but are "+perm.to_s(8)
|
1391
|
+
end
|
1392
|
+
end
|
1393
|
+
|
1394
|
+
# Reads path and parses it as a .netrc file. If path doesn't
|
1395
|
+
# exist, returns an empty object. Decrypt paths ending in .gpg.
|
1396
|
+
def self.read(path=default_path)
|
1397
|
+
check_permissions(path)
|
1398
|
+
data = if path =~ /\.gpg$/
|
1399
|
+
decrypted = if ENV['GPG_AGENT_INFO']
|
1400
|
+
`gpg --batch --quiet --decrypt #{path}`
|
1401
|
+
else
|
1402
|
+
print "Enter passphrase for #{path}: "
|
1403
|
+
STDIN.noecho do
|
1404
|
+
`gpg --batch --passphrase-fd 0 --quiet --decrypt #{path}`
|
1405
|
+
end
|
1406
|
+
end
|
1407
|
+
if $?.success?
|
1408
|
+
decrypted
|
1409
|
+
else
|
1410
|
+
raise Error.new("Decrypting #{path} failed.") unless $?.success?
|
1411
|
+
end
|
1412
|
+
else
|
1413
|
+
File.read(path)
|
1414
|
+
end
|
1415
|
+
new(path, parse(lex(data.lines.to_a)))
|
1416
|
+
rescue Errno::ENOENT
|
1417
|
+
new(path, parse(lex([])))
|
1418
|
+
end
|
1419
|
+
|
1420
|
+
class TokenArray < Array
|
1421
|
+
def take
|
1422
|
+
if length < 1
|
1423
|
+
raise Error, "unexpected EOF"
|
1424
|
+
end
|
1425
|
+
shift
|
1426
|
+
end
|
1427
|
+
|
1428
|
+
def readto
|
1429
|
+
l = []
|
1430
|
+
while length > 0 && ! yield(self[0])
|
1431
|
+
l << shift
|
1432
|
+
end
|
1433
|
+
return l.join
|
1434
|
+
end
|
1435
|
+
end
|
1436
|
+
|
1437
|
+
def self.lex(lines)
|
1438
|
+
tokens = TokenArray.new
|
1439
|
+
for line in lines
|
1440
|
+
content, comment = line.split(/(\s*#.*)/m)
|
1441
|
+
content.each_char do |char|
|
1442
|
+
case char
|
1443
|
+
when /\s/
|
1444
|
+
if tokens.last && tokens.last[-1..-1] =~ /\s/
|
1445
|
+
tokens.last << char
|
1446
|
+
else
|
1447
|
+
tokens << char
|
1448
|
+
end
|
1449
|
+
else
|
1450
|
+
if tokens.last && tokens.last[-1..-1] =~ /\S/
|
1451
|
+
tokens.last << char
|
1452
|
+
else
|
1453
|
+
tokens << char
|
1454
|
+
end
|
1455
|
+
end
|
1456
|
+
end
|
1457
|
+
if comment
|
1458
|
+
tokens << comment
|
1459
|
+
end
|
1460
|
+
end
|
1461
|
+
tokens
|
1462
|
+
end
|
1463
|
+
|
1464
|
+
def self.skip?(s)
|
1465
|
+
s =~ /^\s/
|
1466
|
+
end
|
1467
|
+
|
1468
|
+
|
1469
|
+
|
1470
|
+
# Returns two values, a header and a list of items.
|
1471
|
+
# Each item is a tuple, containing some or all of:
|
1472
|
+
# - machine keyword (including trailing whitespace+comments)
|
1473
|
+
# - machine name
|
1474
|
+
# - login keyword (including surrounding whitespace+comments)
|
1475
|
+
# - login
|
1476
|
+
# - password keyword (including surrounding whitespace+comments)
|
1477
|
+
# - password
|
1478
|
+
# - trailing chars
|
1479
|
+
# This lets us change individual fields, then write out the file
|
1480
|
+
# with all its original formatting.
|
1481
|
+
def self.parse(ts)
|
1482
|
+
cur, item = [], []
|
1483
|
+
|
1484
|
+
unless ts.is_a?(TokenArray)
|
1485
|
+
ts = TokenArray.new(ts)
|
1486
|
+
end
|
1487
|
+
|
1488
|
+
pre = ts.readto{|t| t == "machine" || t == "default"}
|
1489
|
+
|
1490
|
+
while ts.length > 0
|
1491
|
+
if ts[0] == 'default'
|
1492
|
+
cur << ts.take.to_sym
|
1493
|
+
cur << ''
|
1494
|
+
else
|
1495
|
+
cur << ts.take + ts.readto{|t| ! skip?(t)}
|
1496
|
+
cur << ts.take
|
1497
|
+
end
|
1498
|
+
|
1499
|
+
if ts.include?('login')
|
1500
|
+
cur << ts.readto{|t| t == "login"} + ts.take + ts.readto{|t| ! skip?(t)}
|
1501
|
+
cur << ts.take
|
1502
|
+
end
|
1503
|
+
|
1504
|
+
if ts.include?('password')
|
1505
|
+
cur << ts.readto{|t| t == "password"} + ts.take + ts.readto{|t| ! skip?(t)}
|
1506
|
+
cur << ts.take
|
1507
|
+
end
|
1508
|
+
|
1509
|
+
cur << ts.readto{|t| t == "machine" || t == "default"}
|
1510
|
+
|
1511
|
+
item << cur
|
1512
|
+
cur = []
|
1513
|
+
end
|
1514
|
+
|
1515
|
+
[pre, item]
|
1516
|
+
end
|
1517
|
+
|
1518
|
+
def initialize(path, data)
|
1519
|
+
@new_item_prefix = ''
|
1520
|
+
@path = path
|
1521
|
+
@pre, @data = data
|
1522
|
+
|
1523
|
+
if @data && @data.last && :default == @data.last[0]
|
1524
|
+
@default = @data.pop
|
1525
|
+
else
|
1526
|
+
@default = nil
|
1527
|
+
end
|
1528
|
+
end
|
1529
|
+
|
1530
|
+
attr_accessor :new_item_prefix
|
1531
|
+
|
1532
|
+
def [](k)
|
1533
|
+
if item = @data.detect {|datum| datum[1] == k}
|
1534
|
+
Entry.new(item[3], item[5])
|
1535
|
+
elsif @default
|
1536
|
+
Entry.new(@default[3], @default[5])
|
1537
|
+
end
|
1538
|
+
end
|
1539
|
+
|
1540
|
+
def []=(k, info)
|
1541
|
+
if item = @data.detect {|datum| datum[1] == k}
|
1542
|
+
item[3], item[5] = info
|
1543
|
+
else
|
1544
|
+
@data << new_item(k, info[0], info[1])
|
1545
|
+
end
|
1546
|
+
end
|
1547
|
+
|
1548
|
+
def length
|
1549
|
+
@data.length
|
1550
|
+
end
|
1551
|
+
|
1552
|
+
def delete(key)
|
1553
|
+
datum = nil
|
1554
|
+
for value in @data
|
1555
|
+
if value[1] == key
|
1556
|
+
datum = value
|
1557
|
+
break
|
1558
|
+
end
|
1559
|
+
end
|
1560
|
+
@data.delete(datum)
|
1561
|
+
end
|
1562
|
+
|
1563
|
+
def each(&block)
|
1564
|
+
@data.each(&block)
|
1565
|
+
end
|
1566
|
+
|
1567
|
+
def new_item(m, l, p)
|
1568
|
+
[new_item_prefix+"machine ", m, "\n login ", l, "\n password ", p, "\n"]
|
1569
|
+
end
|
1570
|
+
|
1571
|
+
def save
|
1572
|
+
if @path =~ /\.gpg$/
|
1573
|
+
e = IO.popen("gpg -a --batch --default-recipient-self -e", "r+") do |gpg|
|
1574
|
+
gpg.puts(unparse)
|
1575
|
+
gpg.close_write
|
1576
|
+
gpg.read
|
1577
|
+
end
|
1578
|
+
raise Error.new("Encrypting #{@path} failed.") unless $?.success?
|
1579
|
+
File.open(@path, 'w', 0600) {|file| file.print(e)}
|
1580
|
+
else
|
1581
|
+
File.open(@path, 'w', 0600) {|file| file.print(unparse)}
|
1582
|
+
end
|
1583
|
+
end
|
1584
|
+
|
1585
|
+
def unparse
|
1586
|
+
@pre + @data.map do |datum|
|
1587
|
+
datum = datum.join
|
1588
|
+
unless datum[-1..-1] == "\n"
|
1589
|
+
datum << "\n"
|
1590
|
+
else
|
1591
|
+
datum
|
1592
|
+
end
|
1593
|
+
end.join
|
1594
|
+
end
|
1595
|
+
|
1596
|
+
Entry = Struct.new(:login, :password) do
|
1597
|
+
alias to_ary to_a
|
1598
|
+
end
|
1599
|
+
|
1600
|
+
end
|
1601
|
+
|
1602
|
+
class Netrc::Error < ::StandardError
|
1603
|
+
end
|
1604
|
+
rescue LoadError
|
1605
|
+
require File.join File.dirname(File.dirname(__FILE__)), 'vendor', 'netrc.rb'
|
1606
|
+
end
|
1607
|
+
|
1317
1608
|
# It just gists.
|
1318
1609
|
module Gist
|
1319
1610
|
extend self
|
1320
1611
|
|
1321
|
-
VERSION = '4.
|
1612
|
+
VERSION = '4.6.0'
|
1322
1613
|
|
1323
1614
|
# A list of clipboard commands with copy and paste support.
|
1324
1615
|
CLIPBOARD_COMMANDS = {
|
1616
|
+
'pbcopy' => 'pbpaste',
|
1325
1617
|
'xclip' => 'xclip -o',
|
1326
1618
|
'xsel -i' => 'xsel -o',
|
1327
|
-
'
|
1328
|
-
'putclip' => 'getclip'
|
1619
|
+
'putclip' => 'getclip',
|
1329
1620
|
}
|
1330
1621
|
|
1331
1622
|
GITHUB_API_URL = URI("https://api.github.com/")
|
1332
|
-
GIT_IO_URL = URI("
|
1623
|
+
GIT_IO_URL = URI("https://git.io")
|
1333
1624
|
|
1334
1625
|
GITHUB_BASE_PATH = ""
|
1335
1626
|
GHE_BASE_PATH = "/api/v3"
|
@@ -1350,7 +1641,7 @@ module Gist
|
|
1350
1641
|
module AuthTokenFile
|
1351
1642
|
def self.filename
|
1352
1643
|
if ENV.key?(URL_ENV_NAME)
|
1353
|
-
File.expand_path "~/.gist.#{ENV[URL_ENV_NAME].gsub(/[^a-
|
1644
|
+
File.expand_path "~/.gist.#{ENV[URL_ENV_NAME].gsub(/:/, '.').gsub(/[^a-z0-9.]/, '')}"
|
1354
1645
|
else
|
1355
1646
|
File.expand_path "~/.gist"
|
1356
1647
|
end
|
@@ -1371,7 +1662,7 @@ module Gist
|
|
1371
1662
|
#
|
1372
1663
|
# @return [String] string value of access token or `nil`, if not found
|
1373
1664
|
def auth_token
|
1374
|
-
@token ||= AuthTokenFile.read rescue nil
|
1665
|
+
@token ||= AuthTokenFile.read rescue nil || Netrc.read[self.api_url.host][1]
|
1375
1666
|
end
|
1376
1667
|
|
1377
1668
|
# Upload a gist to https://gist.github.com
|
@@ -1529,6 +1820,27 @@ module Gist
|
|
1529
1820
|
end
|
1530
1821
|
end
|
1531
1822
|
|
1823
|
+
def delete_gist(id)
|
1824
|
+
id = id.split("/").last
|
1825
|
+
url = "#{base_path}/gists/#{id}"
|
1826
|
+
|
1827
|
+
access_token = auth_token()
|
1828
|
+
if access_token.to_s != ''
|
1829
|
+
url << "?access_token=" << CGI.escape(access_token)
|
1830
|
+
|
1831
|
+
request = Net::HTTP::Delete.new(url)
|
1832
|
+
response = http(api_url, request)
|
1833
|
+
else
|
1834
|
+
raise Error, "Not authenticated. Use 'gist --login' to login."
|
1835
|
+
end
|
1836
|
+
|
1837
|
+
if response.code == '204'
|
1838
|
+
puts "Deleted!"
|
1839
|
+
else
|
1840
|
+
raise Error, "Gist with id of #{id} does not exist."
|
1841
|
+
end
|
1842
|
+
end
|
1843
|
+
|
1532
1844
|
def get_gist_pages(url)
|
1533
1845
|
|
1534
1846
|
request = Net::HTTP::Get.new(url)
|
@@ -1569,10 +1881,12 @@ module Gist
|
|
1569
1881
|
# @param [String] url
|
1570
1882
|
# @return [String] shortened url, or long url if shortening fails
|
1571
1883
|
def shorten(url)
|
1572
|
-
request = Net::HTTP::Post.new("/")
|
1884
|
+
request = Net::HTTP::Post.new("/create")
|
1573
1885
|
request.set_form_data(:url => url)
|
1574
1886
|
response = http(GIT_IO_URL, request)
|
1575
1887
|
case response.code
|
1888
|
+
when "200"
|
1889
|
+
URI.join(GIT_IO_URL, response.body).to_s
|
1576
1890
|
when "201"
|
1577
1891
|
response['Location']
|
1578
1892
|
else
|
@@ -1645,7 +1959,7 @@ module Gist
|
|
1645
1959
|
|
1646
1960
|
if Net::HTTPCreated === response
|
1647
1961
|
AuthTokenFile.write JSON.parse(response.body)['token']
|
1648
|
-
puts "Success! #{ENV[URL_ENV_NAME] || "https://github.com/"}settings/
|
1962
|
+
puts "Success! #{ENV[URL_ENV_NAME] || "https://github.com/"}settings/tokens"
|
1649
1963
|
return
|
1650
1964
|
elsif Net::HTTPUnauthorized === response
|
1651
1965
|
puts "Error: #{JSON.parse(response.body)['message']}"
|
@@ -1962,6 +2276,10 @@ Usage: #{executable_name} [-o|-c|-e] [-p] [-s] [-R] [-d DESC] [-a] [-u URL] [-P]
|
|
1962
2276
|
options[:read] = id
|
1963
2277
|
end
|
1964
2278
|
|
2279
|
+
opts.on("--delete [ URL | ID ]", "Delete a gist") do |id|
|
2280
|
+
options[:delete] = id
|
2281
|
+
end
|
2282
|
+
|
1965
2283
|
opts.on_tail("-h","--help", "Show this message.") do
|
1966
2284
|
puts opts
|
1967
2285
|
exit
|
@@ -2006,6 +2324,11 @@ begin
|
|
2006
2324
|
exit
|
2007
2325
|
end
|
2008
2326
|
|
2327
|
+
if options.key? :delete
|
2328
|
+
Gist.delete_gist(options[:delete])
|
2329
|
+
exit
|
2330
|
+
end
|
2331
|
+
|
2009
2332
|
if options[:paste]
|
2010
2333
|
puts Gist.gist(Gist.paste, options)
|
2011
2334
|
else
|
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" "May 2017" "" "Gist manual"
|
5
5
|
.
|
6
6
|
.SH "NAME"
|
7
7
|
\fBgist\fR \- upload code to https://gist\.github\.com
|
@@ -99,6 +99,22 @@ To list (public gists or all gists for authed user) gists for user
|
|
99
99
|
.IP
|
100
100
|
gist \-l : all gists for authed user gist \-l defunkt : list defunkt\'s public gists
|
101
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
|
+
.
|
102
118
|
.IP "\(bu" 4
|
103
119
|
See \fBgist \-\-help\fR for more detail\.
|
104
120
|
.
|
@@ -116,14 +132,14 @@ Obtaining OAuth2 access_token from github\.
|
|
116
132
|
GitHub username: ConradIrwin
|
117
133
|
GitHub password:
|
118
134
|
2\-factor auth code:
|
119
|
-
Success! https://github\.com/settings/
|
135
|
+
Success! https://github\.com/settings/tokens
|
120
136
|
.
|
121
137
|
.fi
|
122
138
|
.
|
123
139
|
.IP "" 0
|
124
140
|
.
|
125
141
|
.P
|
126
|
-
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/
|
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\.
|
127
143
|
.
|
128
144
|
.IP "\(bu" 4
|
129
145
|
After you\'ve done this, you can still upload gists anonymously with \fB\-a\fR\.
|
@@ -133,6 +149,31 @@ gist \-a a\.rb
|
|
133
149
|
.
|
134
150
|
.IP "" 0
|
135
151
|
.
|
152
|
+
.SS "GitHub Enterprise"
|
153
|
+
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)\.
|
154
|
+
.
|
155
|
+
.IP "" 4
|
156
|
+
.
|
157
|
+
.nf
|
158
|
+
|
159
|
+
export GITHUB_URL=http://github\.internal\.example\.com/
|
160
|
+
.
|
161
|
+
.fi
|
162
|
+
.
|
163
|
+
.IP "" 0
|
164
|
+
.
|
165
|
+
.P
|
166
|
+
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
|
167
|
+
.
|
168
|
+
.P
|
169
|
+
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\.
|
170
|
+
.
|
171
|
+
.P
|
172
|
+
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
|
173
|
+
.
|
174
|
+
.SS "Token file format"
|
175
|
+
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
|
176
|
+
.
|
136
177
|
.TP
|
137
178
|
You can also use Gist as a library from inside your ruby code:
|
138
179
|
.
|
@@ -188,19 +229,6 @@ This will take them through the process of obtaining an OAuth2 token, and storin
|
|
188
229
|
.
|
189
230
|
.IP "" 0
|
190
231
|
.
|
191
|
-
.SH "GitHub enterprise"
|
192
|
-
.
|
193
|
-
.IP "\(bu" 4
|
194
|
-
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\.
|
195
|
-
.
|
196
|
-
.IP
|
197
|
-
export GITHUB_URL=http://github\.internal\.example\.com/
|
198
|
-
.
|
199
|
-
.IP "\(bu" 4
|
200
|
-
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
|
201
|
-
.
|
202
|
-
.IP "" 0
|
203
|
-
.
|
204
232
|
.SH "Configuration"
|
205
233
|
.
|
206
234
|
.IP "\(bu" 4
|
data/lib/gist.rb
CHANGED
@@ -8,22 +8,28 @@ 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
|
+
|
11
17
|
# It just gists.
|
12
18
|
module Gist
|
13
19
|
extend self
|
14
20
|
|
15
|
-
VERSION = '4.
|
21
|
+
VERSION = '4.6.0'
|
16
22
|
|
17
23
|
# A list of clipboard commands with copy and paste support.
|
18
24
|
CLIPBOARD_COMMANDS = {
|
25
|
+
'pbcopy' => 'pbpaste',
|
19
26
|
'xclip' => 'xclip -o',
|
20
27
|
'xsel -i' => 'xsel -o',
|
21
|
-
'
|
22
|
-
'putclip' => 'getclip'
|
28
|
+
'putclip' => 'getclip',
|
23
29
|
}
|
24
30
|
|
25
31
|
GITHUB_API_URL = URI("https://api.github.com/")
|
26
|
-
GIT_IO_URL = URI("
|
32
|
+
GIT_IO_URL = URI("https://git.io")
|
27
33
|
|
28
34
|
GITHUB_BASE_PATH = ""
|
29
35
|
GHE_BASE_PATH = "/api/v3"
|
@@ -44,7 +50,7 @@ module Gist
|
|
44
50
|
module AuthTokenFile
|
45
51
|
def self.filename
|
46
52
|
if ENV.key?(URL_ENV_NAME)
|
47
|
-
File.expand_path "~/.gist.#{ENV[URL_ENV_NAME].gsub(/[^a-
|
53
|
+
File.expand_path "~/.gist.#{ENV[URL_ENV_NAME].gsub(/:/, '.').gsub(/[^a-z0-9.]/, '')}"
|
48
54
|
else
|
49
55
|
File.expand_path "~/.gist"
|
50
56
|
end
|
@@ -65,7 +71,7 @@ module Gist
|
|
65
71
|
#
|
66
72
|
# @return [String] string value of access token or `nil`, if not found
|
67
73
|
def auth_token
|
68
|
-
@token ||= AuthTokenFile.read rescue nil
|
74
|
+
@token ||= AuthTokenFile.read rescue nil || Netrc.read[self.api_url.host][1]
|
69
75
|
end
|
70
76
|
|
71
77
|
# Upload a gist to https://gist.github.com
|
@@ -223,6 +229,27 @@ module Gist
|
|
223
229
|
end
|
224
230
|
end
|
225
231
|
|
232
|
+
def delete_gist(id)
|
233
|
+
id = id.split("/").last
|
234
|
+
url = "#{base_path}/gists/#{id}"
|
235
|
+
|
236
|
+
access_token = auth_token()
|
237
|
+
if access_token.to_s != ''
|
238
|
+
url << "?access_token=" << CGI.escape(access_token)
|
239
|
+
|
240
|
+
request = Net::HTTP::Delete.new(url)
|
241
|
+
response = http(api_url, request)
|
242
|
+
else
|
243
|
+
raise Error, "Not authenticated. Use 'gist --login' to login."
|
244
|
+
end
|
245
|
+
|
246
|
+
if response.code == '204'
|
247
|
+
puts "Deleted!"
|
248
|
+
else
|
249
|
+
raise Error, "Gist with id of #{id} does not exist."
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
226
253
|
def get_gist_pages(url)
|
227
254
|
|
228
255
|
request = Net::HTTP::Get.new(url)
|
@@ -263,10 +290,12 @@ module Gist
|
|
263
290
|
# @param [String] url
|
264
291
|
# @return [String] shortened url, or long url if shortening fails
|
265
292
|
def shorten(url)
|
266
|
-
request = Net::HTTP::Post.new("/")
|
293
|
+
request = Net::HTTP::Post.new("/create")
|
267
294
|
request.set_form_data(:url => url)
|
268
295
|
response = http(GIT_IO_URL, request)
|
269
296
|
case response.code
|
297
|
+
when "200"
|
298
|
+
URI.join(GIT_IO_URL, response.body).to_s
|
270
299
|
when "201"
|
271
300
|
response['Location']
|
272
301
|
else
|
@@ -339,7 +368,7 @@ module Gist
|
|
339
368
|
|
340
369
|
if Net::HTTPCreated === response
|
341
370
|
AuthTokenFile.write JSON.parse(response.body)['token']
|
342
|
-
puts "Success! #{ENV[URL_ENV_NAME] || "https://github.com/"}settings/
|
371
|
+
puts "Success! #{ENV[URL_ENV_NAME] || "https://github.com/"}settings/tokens"
|
343
372
|
return
|
344
373
|
elsif Net::HTTPUnauthorized === response
|
345
374
|
puts "Error: #{JSON.parse(response.body)['message']}"
|
@@ -19,10 +19,10 @@ describe Gist::AuthTokenFile do
|
|
19
19
|
before do
|
20
20
|
ENV[Gist::URL_ENV_NAME] = github_url
|
21
21
|
end
|
22
|
-
let(:github_url) { "gh.custom.org" }
|
22
|
+
let(:github_url) { "http://gh.custom.org:442/" }
|
23
23
|
|
24
24
|
it "is ~/.gist.{custom_github_url}" do
|
25
|
-
File.should_receive(:expand_path).with("~/.gist
|
25
|
+
File.should_receive(:expand_path).with("~/.gist.http.gh.custom.org.442").and_return(filename)
|
26
26
|
subject.filename.should be filename
|
27
27
|
end
|
28
28
|
end
|
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/vendor/netrc.rb
ADDED
@@ -0,0 +1,286 @@
|
|
1
|
+
|
2
|
+
# The netrc library (https://github.com/heroku/netrc) is reproduced below, along
|
3
|
+
# with its copyright and license:
|
4
|
+
# - https://raw.githubusercontent.com/heroku/netrc/262ef111/LICENSE.md
|
5
|
+
# - https://raw.githubusercontent.com/heroku/netrc/262ef111/lib/netrc.rb
|
6
|
+
|
7
|
+
# The MIT License (MIT)
|
8
|
+
#
|
9
|
+
# Copyright (c) 2011-2014 [CONTRIBUTORS.md](https://github.com/geemus/netrc/blob/master/CONTRIBUTORS.md)
|
10
|
+
#
|
11
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy of
|
12
|
+
# this software and associated documentation files (the "Software"), to deal in
|
13
|
+
# the Software without restriction, including without limitation the rights to
|
14
|
+
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
15
|
+
# the Software, and to permit persons to whom the Software is furnished to do so,
|
16
|
+
# subject to the following conditions:
|
17
|
+
#
|
18
|
+
# The above copyright notice and this permission notice shall be included in all
|
19
|
+
# copies or substantial portions of the Software.
|
20
|
+
#
|
21
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
22
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
23
|
+
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
24
|
+
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
25
|
+
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
26
|
+
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
27
|
+
|
28
|
+
require 'rbconfig'
|
29
|
+
require 'io/console'
|
30
|
+
|
31
|
+
class Netrc
|
32
|
+
VERSION = "0.11.0"
|
33
|
+
|
34
|
+
# see http://stackoverflow.com/questions/4871309/what-is-the-correct-way-to-detect-if-ruby-is-running-on-windows
|
35
|
+
WINDOWS = RbConfig::CONFIG["host_os"] =~ /mswin|mingw|cygwin/
|
36
|
+
CYGWIN = RbConfig::CONFIG["host_os"] =~ /cygwin/
|
37
|
+
|
38
|
+
def self.default_path
|
39
|
+
File.join(ENV['NETRC'] || home_path, netrc_filename)
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.home_path
|
43
|
+
home = Dir.respond_to?(:home) ? Dir.home : ENV['HOME']
|
44
|
+
|
45
|
+
if WINDOWS && !CYGWIN
|
46
|
+
home ||= File.join(ENV['HOMEDRIVE'], ENV['HOMEPATH']) if ENV['HOMEDRIVE'] && ENV['HOMEPATH']
|
47
|
+
home ||= ENV['USERPROFILE']
|
48
|
+
# XXX: old stuff; most likely unnecessary
|
49
|
+
home = home.tr("\\", "/") unless home.nil?
|
50
|
+
end
|
51
|
+
|
52
|
+
(home && File.readable?(home)) ? home : Dir.pwd
|
53
|
+
rescue ArgumentError
|
54
|
+
return Dir.pwd
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.netrc_filename
|
58
|
+
WINDOWS && !CYGWIN ? "_netrc" : ".netrc"
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.config
|
62
|
+
@config ||= {}
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.configure
|
66
|
+
yield(self.config) if block_given?
|
67
|
+
self.config
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.check_permissions(path)
|
71
|
+
perm = File.stat(path).mode & 0777
|
72
|
+
if perm != 0600 && !(WINDOWS) && !(Netrc.config[:allow_permissive_netrc_file])
|
73
|
+
raise Error, "Permission bits for '#{path}' should be 0600, but are "+perm.to_s(8)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Reads path and parses it as a .netrc file. If path doesn't
|
78
|
+
# exist, returns an empty object. Decrypt paths ending in .gpg.
|
79
|
+
def self.read(path=default_path)
|
80
|
+
check_permissions(path)
|
81
|
+
data = if path =~ /\.gpg$/
|
82
|
+
decrypted = if ENV['GPG_AGENT_INFO']
|
83
|
+
`gpg --batch --quiet --decrypt #{path}`
|
84
|
+
else
|
85
|
+
print "Enter passphrase for #{path}: "
|
86
|
+
STDIN.noecho do
|
87
|
+
`gpg --batch --passphrase-fd 0 --quiet --decrypt #{path}`
|
88
|
+
end
|
89
|
+
end
|
90
|
+
if $?.success?
|
91
|
+
decrypted
|
92
|
+
else
|
93
|
+
raise Error.new("Decrypting #{path} failed.") unless $?.success?
|
94
|
+
end
|
95
|
+
else
|
96
|
+
File.read(path)
|
97
|
+
end
|
98
|
+
new(path, parse(lex(data.lines.to_a)))
|
99
|
+
rescue Errno::ENOENT
|
100
|
+
new(path, parse(lex([])))
|
101
|
+
end
|
102
|
+
|
103
|
+
class TokenArray < Array
|
104
|
+
def take
|
105
|
+
if length < 1
|
106
|
+
raise Error, "unexpected EOF"
|
107
|
+
end
|
108
|
+
shift
|
109
|
+
end
|
110
|
+
|
111
|
+
def readto
|
112
|
+
l = []
|
113
|
+
while length > 0 && ! yield(self[0])
|
114
|
+
l << shift
|
115
|
+
end
|
116
|
+
return l.join
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def self.lex(lines)
|
121
|
+
tokens = TokenArray.new
|
122
|
+
for line in lines
|
123
|
+
content, comment = line.split(/(\s*#.*)/m)
|
124
|
+
content.each_char do |char|
|
125
|
+
case char
|
126
|
+
when /\s/
|
127
|
+
if tokens.last && tokens.last[-1..-1] =~ /\s/
|
128
|
+
tokens.last << char
|
129
|
+
else
|
130
|
+
tokens << char
|
131
|
+
end
|
132
|
+
else
|
133
|
+
if tokens.last && tokens.last[-1..-1] =~ /\S/
|
134
|
+
tokens.last << char
|
135
|
+
else
|
136
|
+
tokens << char
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
if comment
|
141
|
+
tokens << comment
|
142
|
+
end
|
143
|
+
end
|
144
|
+
tokens
|
145
|
+
end
|
146
|
+
|
147
|
+
def self.skip?(s)
|
148
|
+
s =~ /^\s/
|
149
|
+
end
|
150
|
+
|
151
|
+
|
152
|
+
|
153
|
+
# Returns two values, a header and a list of items.
|
154
|
+
# Each item is a tuple, containing some or all of:
|
155
|
+
# - machine keyword (including trailing whitespace+comments)
|
156
|
+
# - machine name
|
157
|
+
# - login keyword (including surrounding whitespace+comments)
|
158
|
+
# - login
|
159
|
+
# - password keyword (including surrounding whitespace+comments)
|
160
|
+
# - password
|
161
|
+
# - trailing chars
|
162
|
+
# This lets us change individual fields, then write out the file
|
163
|
+
# with all its original formatting.
|
164
|
+
def self.parse(ts)
|
165
|
+
cur, item = [], []
|
166
|
+
|
167
|
+
unless ts.is_a?(TokenArray)
|
168
|
+
ts = TokenArray.new(ts)
|
169
|
+
end
|
170
|
+
|
171
|
+
pre = ts.readto{|t| t == "machine" || t == "default"}
|
172
|
+
|
173
|
+
while ts.length > 0
|
174
|
+
if ts[0] == 'default'
|
175
|
+
cur << ts.take.to_sym
|
176
|
+
cur << ''
|
177
|
+
else
|
178
|
+
cur << ts.take + ts.readto{|t| ! skip?(t)}
|
179
|
+
cur << ts.take
|
180
|
+
end
|
181
|
+
|
182
|
+
if ts.include?('login')
|
183
|
+
cur << ts.readto{|t| t == "login"} + ts.take + ts.readto{|t| ! skip?(t)}
|
184
|
+
cur << ts.take
|
185
|
+
end
|
186
|
+
|
187
|
+
if ts.include?('password')
|
188
|
+
cur << ts.readto{|t| t == "password"} + ts.take + ts.readto{|t| ! skip?(t)}
|
189
|
+
cur << ts.take
|
190
|
+
end
|
191
|
+
|
192
|
+
cur << ts.readto{|t| t == "machine" || t == "default"}
|
193
|
+
|
194
|
+
item << cur
|
195
|
+
cur = []
|
196
|
+
end
|
197
|
+
|
198
|
+
[pre, item]
|
199
|
+
end
|
200
|
+
|
201
|
+
def initialize(path, data)
|
202
|
+
@new_item_prefix = ''
|
203
|
+
@path = path
|
204
|
+
@pre, @data = data
|
205
|
+
|
206
|
+
if @data && @data.last && :default == @data.last[0]
|
207
|
+
@default = @data.pop
|
208
|
+
else
|
209
|
+
@default = nil
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
attr_accessor :new_item_prefix
|
214
|
+
|
215
|
+
def [](k)
|
216
|
+
if item = @data.detect {|datum| datum[1] == k}
|
217
|
+
Entry.new(item[3], item[5])
|
218
|
+
elsif @default
|
219
|
+
Entry.new(@default[3], @default[5])
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
def []=(k, info)
|
224
|
+
if item = @data.detect {|datum| datum[1] == k}
|
225
|
+
item[3], item[5] = info
|
226
|
+
else
|
227
|
+
@data << new_item(k, info[0], info[1])
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
def length
|
232
|
+
@data.length
|
233
|
+
end
|
234
|
+
|
235
|
+
def delete(key)
|
236
|
+
datum = nil
|
237
|
+
for value in @data
|
238
|
+
if value[1] == key
|
239
|
+
datum = value
|
240
|
+
break
|
241
|
+
end
|
242
|
+
end
|
243
|
+
@data.delete(datum)
|
244
|
+
end
|
245
|
+
|
246
|
+
def each(&block)
|
247
|
+
@data.each(&block)
|
248
|
+
end
|
249
|
+
|
250
|
+
def new_item(m, l, p)
|
251
|
+
[new_item_prefix+"machine ", m, "\n login ", l, "\n password ", p, "\n"]
|
252
|
+
end
|
253
|
+
|
254
|
+
def save
|
255
|
+
if @path =~ /\.gpg$/
|
256
|
+
e = IO.popen("gpg -a --batch --default-recipient-self -e", "r+") do |gpg|
|
257
|
+
gpg.puts(unparse)
|
258
|
+
gpg.close_write
|
259
|
+
gpg.read
|
260
|
+
end
|
261
|
+
raise Error.new("Encrypting #{@path} failed.") unless $?.success?
|
262
|
+
File.open(@path, 'w', 0600) {|file| file.print(e)}
|
263
|
+
else
|
264
|
+
File.open(@path, 'w', 0600) {|file| file.print(unparse)}
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
def unparse
|
269
|
+
@pre + @data.map do |datum|
|
270
|
+
datum = datum.join
|
271
|
+
unless datum[-1..-1] == "\n"
|
272
|
+
datum << "\n"
|
273
|
+
else
|
274
|
+
datum
|
275
|
+
end
|
276
|
+
end.join
|
277
|
+
end
|
278
|
+
|
279
|
+
Entry = Struct.new(:login, :password) do
|
280
|
+
alias to_ary to_a
|
281
|
+
end
|
282
|
+
|
283
|
+
end
|
284
|
+
|
285
|
+
class Netrc::Error < ::StandardError
|
286
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gist
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.
|
4
|
+
version: 4.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Conrad Irwin
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2017-05-16 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
@@ -96,6 +96,7 @@ files:
|
|
96
96
|
- spec/shorten_spec.rb
|
97
97
|
- spec/spec_helper.rb
|
98
98
|
- vendor/json.rb
|
99
|
+
- vendor/netrc.rb
|
99
100
|
homepage: https://github.com/defunkt/gist
|
100
101
|
licenses:
|
101
102
|
- MIT
|
@@ -116,9 +117,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
116
117
|
version: '0'
|
117
118
|
requirements: []
|
118
119
|
rubyforge_project:
|
119
|
-
rubygems_version: 2.
|
120
|
+
rubygems_version: 2.6.11
|
120
121
|
signing_key:
|
121
122
|
specification_version: 4
|
122
123
|
summary: Just allows you to upload gists
|
123
124
|
test_files: []
|
124
|
-
has_rdoc:
|