ghkv 0.1.1
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 +7 -0
- data/.gitignore +10 -0
- data/.rspec +2 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/README.md +53 -0
- data/Rakefile +6 -0
- data/exe/ghkv +4 -0
- data/ghkv.gemspec +26 -0
- data/lib/ghkv.rb +13 -0
- data/lib/ghkv/cli.rb +48 -0
- data/lib/ghkv/ghkv.rb +110 -0
- data/lib/ghkv/http_client.rb +37 -0
- data/lib/ghkv/version.rb +3 -0
- metadata +141 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 5c97162f2622d436bc7ff714e48923da10cedf1d
|
4
|
+
data.tar.gz: 495855df9966c7596efffb298f07ee485fb19182
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e80c15dc9ed67e506c4e12eac95d8abb826b4044de52ac747892b77978d333910ecc5f5841e332aaea379c230d0531b02203df92f6aa04ca2f6debcb239d6cea
|
7
|
+
data.tar.gz: b6af58f3050486f7e541ff251afc7d1baabc8c4fb3d9daee4ffa6cf12c1d981485868ed3a1c9f4dd3b44649baa40f7e2cc54cf860184f18e82ddf6a1684db470
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
# Ghkv
|
2
|
+
|
3
|
+
`ghkv` is a gem (and corresponding CLI tool) to allow for quick and dirty
|
4
|
+
persistence when you really can't be bothered spining up a Redis instance.
|
5
|
+
|
6
|
+
## Gem
|
7
|
+
|
8
|
+
```ruby
|
9
|
+
require 'ghkv'
|
10
|
+
|
11
|
+
# token will be read from ENV['GHKV_TOKEN'] if not passed in
|
12
|
+
kv = Ghkv::Ghkv.new repo: 'consumer-data/github_build_notifier', token: '<some hex>'
|
13
|
+
kv['key'] = { "some" => "hash" }
|
14
|
+
kv['key'] # => { "some" => "hash" }
|
15
|
+
kv.keys # => ["some"]
|
16
|
+
# kv.delete 'key'
|
17
|
+
kv.save
|
18
|
+
```
|
19
|
+
|
20
|
+
If your repo is hosted on GitHub enterprise, you can pass in an `api_url`
|
21
|
+
parameter (e.g. `https://git.example.com/api/v3`) or put it in
|
22
|
+
`ENV['GHKV_API_URL']`.
|
23
|
+
|
24
|
+
## CLI
|
25
|
+
|
26
|
+
Tip: Put the following in your `~/.bash_profile` for quicker usage:
|
27
|
+
|
28
|
+
```
|
29
|
+
# ~/.bash_profile
|
30
|
+
export GHKV_TOKEN=somehexvalue
|
31
|
+
```
|
32
|
+
|
33
|
+
```
|
34
|
+
$ ghkv
|
35
|
+
Usage:
|
36
|
+
ghkv [OPTIONS] SUBCOMMAND [ARG] ...
|
37
|
+
|
38
|
+
Parameters:
|
39
|
+
SUBCOMMAND subcommand
|
40
|
+
[ARG] ... subcommand arguments
|
41
|
+
|
42
|
+
Subcommands:
|
43
|
+
get Get value from KV and print to stdout
|
44
|
+
set Pass in value to store in KV by parameter or from stdin
|
45
|
+
list List all keys in KV, one per line
|
46
|
+
delete Delete key from KV
|
47
|
+
|
48
|
+
Options:
|
49
|
+
--repo REPO Qualified (user/repo) repo name
|
50
|
+
--token TOKEN GitHub API token (default: $GHKV_TOKEN)
|
51
|
+
--api API Base URL for enterprise GitHub (default: $GHKV_API_URL)
|
52
|
+
-h, --help print help
|
53
|
+
```
|
data/Rakefile
ADDED
data/exe/ghkv
ADDED
data/ghkv.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'ghkv/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'ghkv'
|
8
|
+
spec.version = Ghkv::VERSION
|
9
|
+
spec.authors = ['Aidan Steele']
|
10
|
+
spec.email = ['aidan.steele@glassechidna.com.au']
|
11
|
+
|
12
|
+
spec.summary = %q{Use the GitHub API as a ghetto key-value store.}
|
13
|
+
|
14
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
15
|
+
spec.bindir = 'exe'
|
16
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
17
|
+
spec.require_paths = ['lib']
|
18
|
+
|
19
|
+
spec.add_development_dependency 'bundler', '~> 1.10'
|
20
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
21
|
+
spec.add_development_dependency 'rspec'
|
22
|
+
spec.add_development_dependency 'pry-stack_explorer'
|
23
|
+
spec.add_development_dependency 'pry-rescue'
|
24
|
+
|
25
|
+
spec.add_runtime_dependency 'clamp', '>= 1.0'
|
26
|
+
end
|
data/lib/ghkv.rb
ADDED
data/lib/ghkv/cli.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
module Ghkv
|
2
|
+
class CLI < Clamp::Command
|
3
|
+
option '--repo', 'REPO', 'Qualified (user/repo) repo name'
|
4
|
+
option '--token', 'TOKEN', 'GitHub API token', environment_variable: 'GHKV_TOKEN'
|
5
|
+
option '--api', 'API', 'Base URL for enterprise GitHub', environment_variable: 'GHKV_API_URL'
|
6
|
+
|
7
|
+
subcommand 'get', 'Get value from KV and print to stdout' do
|
8
|
+
parameter 'KEY', 'key'
|
9
|
+
|
10
|
+
def execute
|
11
|
+
puts ghkv[key].to_json
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
subcommand 'set', 'Pass in value to store in KV by parameter or from stdin' do
|
16
|
+
parameter 'KEY', 'key'
|
17
|
+
parameter '[VALUE]', 'value'
|
18
|
+
|
19
|
+
def execute
|
20
|
+
ghkv[key] = JSON.parse input
|
21
|
+
ghkv.save
|
22
|
+
end
|
23
|
+
|
24
|
+
def input
|
25
|
+
value || $stdin.read
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
subcommand 'list', 'List all keys in KV, one per line' do
|
30
|
+
def execute
|
31
|
+
puts ghkv.keys
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
subcommand 'delete', 'Delete key from KV' do
|
36
|
+
parameter 'KEY', 'key'
|
37
|
+
|
38
|
+
def execute
|
39
|
+
ghkv.delete key
|
40
|
+
ghkv.save
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def ghkv
|
45
|
+
@ghkv ||= Ghkv.new repo: repo, api_url: api, token: token
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/ghkv/ghkv.rb
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
module Ghkv
|
2
|
+
class Ghkv
|
3
|
+
attr_accessor :http, :cache, :dirty, :deleted, :namespace
|
4
|
+
|
5
|
+
def initialize(repo:, api_url: nil, token: ENV['GHKV_TOKEN'])
|
6
|
+
api_url ||= ENV['GHKV_API_URL'] || 'https://api.github.com/'
|
7
|
+
@http = HttpClient.new base: "#{api_url}/repos/#{repo}/", token: token
|
8
|
+
|
9
|
+
@cache = {}
|
10
|
+
@dirty = Set.new
|
11
|
+
@deleted = Set.new
|
12
|
+
@namespace = 'ghkv'
|
13
|
+
end
|
14
|
+
|
15
|
+
def [](key)
|
16
|
+
cache[key] ||= get key
|
17
|
+
end
|
18
|
+
|
19
|
+
def []=(key, value)
|
20
|
+
dirty << key
|
21
|
+
deleted.delete key
|
22
|
+
|
23
|
+
cache[key] = value
|
24
|
+
end
|
25
|
+
|
26
|
+
def delete(key)
|
27
|
+
deleted << key
|
28
|
+
cache.delete key
|
29
|
+
end
|
30
|
+
|
31
|
+
def keys
|
32
|
+
prefix_len = "refs/#{namespace}/".length
|
33
|
+
|
34
|
+
url = refs_url namespace
|
35
|
+
resp = http.request :get, url
|
36
|
+
|
37
|
+
saved_keys = resp.map { |ref| ref['ref'][prefix_len..-1] }
|
38
|
+
(saved_keys + cache.keys).uniq - deleted.to_a
|
39
|
+
end
|
40
|
+
|
41
|
+
def save
|
42
|
+
deleted.each do |key|
|
43
|
+
delete_ref key
|
44
|
+
end
|
45
|
+
|
46
|
+
dirty.each do |key|
|
47
|
+
set key, cache[key]
|
48
|
+
end
|
49
|
+
|
50
|
+
true
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def get(key)
|
56
|
+
url = refs_url("#{namespace}/#{key}")
|
57
|
+
resp = http.request(:get, url)
|
58
|
+
object = resp['object']
|
59
|
+
return nil if object.nil?
|
60
|
+
|
61
|
+
blob = http.request :get, object['url']
|
62
|
+
decoded = Base64.decode64 blob['content']
|
63
|
+
Marshal.load decoded
|
64
|
+
end
|
65
|
+
|
66
|
+
def set(key, value)
|
67
|
+
sha = post_blob value
|
68
|
+
|
69
|
+
if get(key).nil?
|
70
|
+
create_ref sha, key
|
71
|
+
else
|
72
|
+
update_ref sha, key
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def delete_ref(key)
|
77
|
+
http.request :delete, refs_url("#{namespace}/#{key}")
|
78
|
+
end
|
79
|
+
|
80
|
+
def create_ref(sha, key)
|
81
|
+
http.request(
|
82
|
+
:post,
|
83
|
+
refs_url,
|
84
|
+
ref: "refs/#{namespace}/#{key}",
|
85
|
+
sha: sha
|
86
|
+
)
|
87
|
+
end
|
88
|
+
|
89
|
+
def update_ref(sha, key)
|
90
|
+
http.request(
|
91
|
+
:patch,
|
92
|
+
refs_url("#{namespace}/#{key}"),
|
93
|
+
force: true,
|
94
|
+
sha: sha
|
95
|
+
)
|
96
|
+
end
|
97
|
+
|
98
|
+
def post_blob(value)
|
99
|
+
encoded = Base64.encode64 Marshal.dump value
|
100
|
+
payload = { encoding: 'base64', content: encoded }
|
101
|
+
blob = http.request :post, 'git/blobs', payload
|
102
|
+
blob['sha']
|
103
|
+
end
|
104
|
+
|
105
|
+
def refs_url(key=nil)
|
106
|
+
"git/refs/#{key}".chomp('/')
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Ghkv
|
2
|
+
class HttpClient
|
3
|
+
attr_accessor :token
|
4
|
+
attr_accessor :verbose
|
5
|
+
attr_accessor :base
|
6
|
+
|
7
|
+
def initialize(base:, token:)
|
8
|
+
@base = URI.parse base
|
9
|
+
@token = token
|
10
|
+
end
|
11
|
+
|
12
|
+
def http_request(method, uri)
|
13
|
+
klass = Net::HTTP.const_get method.capitalize
|
14
|
+
klass.new(uri).tap do |r|
|
15
|
+
r.basic_auth token, 'x-oauth-basic'
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def http(uri)
|
20
|
+
Net::HTTP.new(uri.host, uri.port).tap do |h|
|
21
|
+
h.set_debug_output($stdout) if verbose
|
22
|
+
h.use_ssl = uri.port == 443
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def request(method, url, body=nil)
|
27
|
+
uri = base + url
|
28
|
+
|
29
|
+
req = http_request method, uri
|
30
|
+
req.body = body.to_json if body
|
31
|
+
|
32
|
+
client = http uri
|
33
|
+
resp = client.request req
|
34
|
+
JSON.parse(resp.body) rescue nil
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/ghkv/version.rb
ADDED
metadata
ADDED
@@ -0,0 +1,141 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ghkv
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Aidan Steele
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-09-17 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.10'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.10'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: pry-stack_explorer
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: pry-rescue
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: clamp
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '1.0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '1.0'
|
97
|
+
description:
|
98
|
+
email:
|
99
|
+
- aidan.steele@glassechidna.com.au
|
100
|
+
executables:
|
101
|
+
- ghkv
|
102
|
+
extensions: []
|
103
|
+
extra_rdoc_files: []
|
104
|
+
files:
|
105
|
+
- ".gitignore"
|
106
|
+
- ".rspec"
|
107
|
+
- ".travis.yml"
|
108
|
+
- Gemfile
|
109
|
+
- README.md
|
110
|
+
- Rakefile
|
111
|
+
- exe/ghkv
|
112
|
+
- ghkv.gemspec
|
113
|
+
- lib/ghkv.rb
|
114
|
+
- lib/ghkv/cli.rb
|
115
|
+
- lib/ghkv/ghkv.rb
|
116
|
+
- lib/ghkv/http_client.rb
|
117
|
+
- lib/ghkv/version.rb
|
118
|
+
homepage:
|
119
|
+
licenses: []
|
120
|
+
metadata: {}
|
121
|
+
post_install_message:
|
122
|
+
rdoc_options: []
|
123
|
+
require_paths:
|
124
|
+
- lib
|
125
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
126
|
+
requirements:
|
127
|
+
- - ">="
|
128
|
+
- !ruby/object:Gem::Version
|
129
|
+
version: '0'
|
130
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
131
|
+
requirements:
|
132
|
+
- - ">="
|
133
|
+
- !ruby/object:Gem::Version
|
134
|
+
version: '0'
|
135
|
+
requirements: []
|
136
|
+
rubyforge_project:
|
137
|
+
rubygems_version: 2.4.5
|
138
|
+
signing_key:
|
139
|
+
specification_version: 4
|
140
|
+
summary: Use the GitHub API as a ghetto key-value store.
|
141
|
+
test_files: []
|