netlify 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +40 -0
- data/LICENSE.txt +22 -0
- data/README.md +423 -0
- data/Rakefile +7 -0
- data/lib/netlify.rb +17 -0
- data/lib/netlify/access_token.rb +5 -0
- data/lib/netlify/access_tokens.rb +7 -0
- data/lib/netlify/client.rb +113 -0
- data/lib/netlify/collection_proxy.rb +48 -0
- data/lib/netlify/deploy.rb +58 -0
- data/lib/netlify/deploys.rb +43 -0
- data/lib/netlify/dns_record.rb +5 -0
- data/lib/netlify/dns_records.rb +7 -0
- data/lib/netlify/dns_zone.rb +11 -0
- data/lib/netlify/dns_zones.rb +11 -0
- data/lib/netlify/file.rb +5 -0
- data/lib/netlify/files.rb +7 -0
- data/lib/netlify/form.rb +9 -0
- data/lib/netlify/forms.rb +7 -0
- data/lib/netlify/model.rb +65 -0
- data/lib/netlify/multipass.rb +70 -0
- data/lib/netlify/site.rb +69 -0
- data/lib/netlify/sites.rb +18 -0
- data/lib/netlify/snippet.rb +5 -0
- data/lib/netlify/snippets.rb +7 -0
- data/lib/netlify/submission.rb +6 -0
- data/lib/netlify/submissions.rb +7 -0
- data/lib/netlify/user.rb +17 -0
- data/lib/netlify/users.rb +7 -0
- data/lib/netlify/version.rb +3 -0
- data/test/client_test.rb +77 -0
- data/test/files/site-dir.zip +0 -0
- data/test/files/site-dir/index.html +5 -0
- data/test/multipass_test.rb +14 -0
- data/test/sites_test.rb +66 -0
- data/test/test_helper.rb +6 -0
- metadata +156 -0
@@ -0,0 +1,70 @@
|
|
1
|
+
# Multipass implementation used for single-sign-on for resellers
|
2
|
+
require "openssl"
|
3
|
+
require "base64"
|
4
|
+
require "time"
|
5
|
+
require "json"
|
6
|
+
|
7
|
+
module Netlify
|
8
|
+
class Multipass
|
9
|
+
def initialize(multipass_secret)
|
10
|
+
### Use the Multipass secret to derive two cryptographic keys,
|
11
|
+
### one for encryption, one for signing
|
12
|
+
key_material = OpenSSL::Digest.new("sha256").digest(multipass_secret)
|
13
|
+
@encryption_key = key_material[ 0,16]
|
14
|
+
@signature_key = key_material[16,16]
|
15
|
+
end
|
16
|
+
|
17
|
+
def generate_token(customer_data_hash)
|
18
|
+
### Store the current time in ISO8601 format.
|
19
|
+
### The token will only be valid for a small timeframe around this timestamp.
|
20
|
+
customer_data_hash["created_at"] = Time.now.iso8601
|
21
|
+
|
22
|
+
### Serialize the customer data to JSON and encrypt it
|
23
|
+
ciphertext = encrypt(customer_data_hash.to_json)
|
24
|
+
|
25
|
+
### Create a signature (message authentication code) of the ciphertext
|
26
|
+
### and encode everything using URL-safe Base64 (RFC 4648)
|
27
|
+
sig = sign(ciphertext)
|
28
|
+
|
29
|
+
Base64.urlsafe_encode64(ciphertext + sign(ciphertext))
|
30
|
+
end
|
31
|
+
|
32
|
+
def decode_token(token)
|
33
|
+
decoded_token = Base64.urlsafe_decode64(token)
|
34
|
+
ciphertext, signature = [decoded_token[0..-33], decoded_token[-32..-1]]
|
35
|
+
|
36
|
+
sig = sign(ciphertext)
|
37
|
+
|
38
|
+
raise "Bad signature" unless sign(ciphertext) == signature
|
39
|
+
|
40
|
+
JSON.parse(decrypt(ciphertext))
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
def encrypt(plaintext)
|
45
|
+
cipher = OpenSSL::Cipher::Cipher.new("aes-128-cbc")
|
46
|
+
cipher.encrypt
|
47
|
+
cipher.key = @encryption_key
|
48
|
+
|
49
|
+
### Use a random IV
|
50
|
+
cipher.iv = iv = cipher.random_iv
|
51
|
+
|
52
|
+
### Use IV as first block of ciphertext
|
53
|
+
iv + cipher.update(plaintext) + cipher.final
|
54
|
+
end
|
55
|
+
|
56
|
+
def decrypt(ciphertext)
|
57
|
+
decipher = OpenSSL::Cipher::Cipher.new("aes-128-cbc")
|
58
|
+
decipher.decrypt
|
59
|
+
decipher.key = @encryption_key
|
60
|
+
|
61
|
+
decipher.iv, encrypted = [ciphertext[0..15], ciphertext[16..-1]]
|
62
|
+
|
63
|
+
decipher.update(encrypted) + decipher.final
|
64
|
+
end
|
65
|
+
|
66
|
+
def sign(data)
|
67
|
+
OpenSSL::HMAC.digest("sha256", @signature_key, data)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
data/lib/netlify/site.rb
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'digest/sha1'
|
2
|
+
require 'uri'
|
3
|
+
|
4
|
+
module Netlify
|
5
|
+
class Site < Model
|
6
|
+
fields :id, :state, :premium, :claimed, :name, :custom_domain, :url,
|
7
|
+
:admin_url, :deploy_id, :deploy_url, :screenshot_url, :created_at, :updated_at,
|
8
|
+
:password, :notification_email, :user_id, :error_message, :required
|
9
|
+
|
10
|
+
def ready?
|
11
|
+
state == "current"
|
12
|
+
end
|
13
|
+
|
14
|
+
def error?
|
15
|
+
state == "error"
|
16
|
+
end
|
17
|
+
|
18
|
+
def wait_for_ready(timeout = 900, &block)
|
19
|
+
deploy = deploys.get(deploy_id)
|
20
|
+
raise "Error fetching deploy #{deploy_id}" unless deploy
|
21
|
+
deploy.wait_for_ready(timeout, &block)
|
22
|
+
self
|
23
|
+
end
|
24
|
+
|
25
|
+
def update(attributes)
|
26
|
+
response = client.request(:put, path, :body => mutable_attributes(attributes))
|
27
|
+
process(response.parsed)
|
28
|
+
if attributes[:zip] || attributes[:dir]
|
29
|
+
deploy = deploys.create(attributes)
|
30
|
+
self.deploy_id = deploy.id
|
31
|
+
end
|
32
|
+
self
|
33
|
+
end
|
34
|
+
|
35
|
+
def destroy!
|
36
|
+
client.request(:delete, path)
|
37
|
+
true
|
38
|
+
end
|
39
|
+
|
40
|
+
def forms
|
41
|
+
Forms.new(client, path)
|
42
|
+
end
|
43
|
+
|
44
|
+
def submissions
|
45
|
+
Submissions.new(client, path)
|
46
|
+
end
|
47
|
+
|
48
|
+
def files
|
49
|
+
Files.new(client, path)
|
50
|
+
end
|
51
|
+
|
52
|
+
def snippets
|
53
|
+
Snippets.new(client, path)
|
54
|
+
end
|
55
|
+
|
56
|
+
def deploys
|
57
|
+
Deploys.new(client, path)
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
def mutable_attributes(attributes)
|
62
|
+
Hash[*[:name, :custom_domain, :password, :notification_email].map {|key|
|
63
|
+
if attributes.has_key?(key) || attributes.has_key?(key.to_s)
|
64
|
+
[key, attributes[key] || attributes[key.to_s]]
|
65
|
+
end
|
66
|
+
}.compact.flatten]
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require "Netlify/site"
|
2
|
+
require "digest/sha1"
|
3
|
+
|
4
|
+
module Netlify
|
5
|
+
class Sites < CollectionProxy
|
6
|
+
path "/sites"
|
7
|
+
|
8
|
+
def create(attributes = {})
|
9
|
+
response = client.request(:post, path, :body => Site.new(client, {}).send(:mutable_attributes, attributes))
|
10
|
+
Site.new(client, response.parsed).tap do |site|
|
11
|
+
if attributes[:zip] || attributes[:dir]
|
12
|
+
deploy = site.deploys.create(attributes)
|
13
|
+
site.deploy_id = deploy.id
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/netlify/user.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
module Netlify
|
2
|
+
class User < Model
|
3
|
+
fields :id, :uid, :email, :affiliate_id, :site_count, :created_at, :last_login
|
4
|
+
|
5
|
+
def sites
|
6
|
+
Sites.new(client, path)
|
7
|
+
end
|
8
|
+
|
9
|
+
def submissions
|
10
|
+
Submissions.new(client, path)
|
11
|
+
end
|
12
|
+
|
13
|
+
def forms
|
14
|
+
Forms.new(client, path)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/test/client_test.rb
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class ClientTest < MiniTest::Unit::TestCase
|
4
|
+
attr_reader :client
|
5
|
+
|
6
|
+
def setup
|
7
|
+
@client = Netlify::Client.new(:client_id => "client_id", :client_secret => "client_secret")
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_authorize_url
|
11
|
+
expected = URI.parse("https://api.netlify.com/oauth/authorize?response_type=code&client_id=client_id&redirect_uri=http%3A%2F%2Fexample.com%2Fcallback")
|
12
|
+
actual = URI.parse(client.authorize_url(:redirect_uri => "http://example.com/callback"))
|
13
|
+
assert_equal expected.scheme, actual.scheme
|
14
|
+
assert_equal expected.host, actual.host
|
15
|
+
assert_equal expected.port, actual.port
|
16
|
+
assert_equal expected.query.split("&").sort, actual.query.split("&").sort
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_authorize_from_code
|
20
|
+
stub_request(:post, "https://api.netlify.com/oauth/token").to_return(
|
21
|
+
:headers => {'Content-Type' => 'application/json'},
|
22
|
+
:body => {
|
23
|
+
"access_token" => "2YotnFZFEjr1zCsicMWpAA"
|
24
|
+
})
|
25
|
+
client.authorize_from_code!("authorization_code", :redirect_uri => "http://example.com/callback")
|
26
|
+
assert_equal "2YotnFZFEjr1zCsicMWpAA", client.access_token
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_authorize_from_credentials
|
30
|
+
stub_request(:post, "https://client_id:client_secret@api.netlify.com/oauth/token").to_return(
|
31
|
+
:headers => {'Content-Type' => 'application/json'},
|
32
|
+
:body => {
|
33
|
+
"access_token" => "2YotnFZFEjr1zCsicMWpAA"
|
34
|
+
})
|
35
|
+
|
36
|
+
client.authorize_from_credentials!
|
37
|
+
assert_equal "2YotnFZFEjr1zCsicMWpAA", client.access_token
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_simple_get_request
|
41
|
+
stub_request(:get, "https://api.netlify.com/api/v1/sites")
|
42
|
+
.with(:headers => {'Authorization' => "Bearer access_token"})
|
43
|
+
.to_return(
|
44
|
+
:headers => {'Content-Type' => 'application/json'},
|
45
|
+
:body => []
|
46
|
+
)
|
47
|
+
|
48
|
+
client.access_token = "access_token"
|
49
|
+
response = client.request(:get, "/sites")
|
50
|
+
assert_equal [], response.parsed
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_sites
|
54
|
+
stub_request(:get, "https://api.netlify.com/api/v1/sites")
|
55
|
+
.with(:headers => {'Authorization' => "Bearer access_token"})
|
56
|
+
.to_return(
|
57
|
+
:headers => {'Content-Type' => 'application/json'},
|
58
|
+
:body => JSON.generate([{:url => "http://www.example.com"}])
|
59
|
+
)
|
60
|
+
client.access_token = "access_token"
|
61
|
+
sites = client.sites.all
|
62
|
+
assert_equal "http://www.example.com", sites.first.url
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_get_site
|
66
|
+
stub_request(:get, "https://api.netlify.com/api/v1/sites/1234")
|
67
|
+
.with(:headers => {'Authorization' => "Bearer access_token"})
|
68
|
+
.to_return(
|
69
|
+
:headers => {'Content-Type' => 'application/json'},
|
70
|
+
:body => {:url => "http://www.example.com"}
|
71
|
+
)
|
72
|
+
|
73
|
+
client.access_token = "access_token"
|
74
|
+
site = client.sites.get("1234")
|
75
|
+
assert_equal "http://www.example.com", site.url
|
76
|
+
end
|
77
|
+
end
|
Binary file
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'digest/sha1'
|
3
|
+
|
4
|
+
class MultipassTest < MiniTest::Unit::TestCase
|
5
|
+
def setup
|
6
|
+
@mp = Netlify::Multipass.new("secret")
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_generate_and_decode_token
|
10
|
+
data = {"email" => "test@example.com", "uid" => "1234"}
|
11
|
+
token = @mp.generate_token(data)
|
12
|
+
assert_equal data, @mp.decode_token(token), "Data should be the same after generating and decoding"
|
13
|
+
end
|
14
|
+
end
|
data/test/sites_test.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'digest/sha1'
|
3
|
+
|
4
|
+
class SitesTest < MiniTest::Unit::TestCase
|
5
|
+
attr_reader :client
|
6
|
+
attr_accessor :_assertions
|
7
|
+
|
8
|
+
def setup
|
9
|
+
@client = Netlify::Client.new(:client_id => "client_id", :client_secret => "client_secret")
|
10
|
+
@client.access_token = "access_token"
|
11
|
+
self._assertions = 0
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_create_from_dir
|
15
|
+
body = nil
|
16
|
+
dir = ::File.expand_path("../files/site-dir", __FILE__)
|
17
|
+
index_sha = Digest::SHA1.hexdigest(::File.read(::File.join(dir, "index.html")))
|
18
|
+
|
19
|
+
stub_request(:post, "https://api.netlify.com/api/v1/sites")
|
20
|
+
.to_return {|request|
|
21
|
+
{
|
22
|
+
:headers => {'Content-Type' => 'application/json'},
|
23
|
+
:body => JSON.generate({:id => "1234"})
|
24
|
+
}
|
25
|
+
}
|
26
|
+
stub_request(:post, "https://api.netlify.com/api/v1/sites/1234/deploys")
|
27
|
+
.to_return {|request|
|
28
|
+
body = JSON.parse(request.body)
|
29
|
+
{
|
30
|
+
:headers => {'Content-Type' => 'application/json'},
|
31
|
+
:body => JSON.generate({:id => "2345", :state => "uploading", :required => [index_sha]})
|
32
|
+
}
|
33
|
+
}
|
34
|
+
stub_request(:put, "https://api.netlify.com/api/v1/deploys/2345/files/index.html")
|
35
|
+
stub_request(:get, "https://api.netlify.com/api/v1/deploys/2345")
|
36
|
+
.to_return(:headers => {'Content-Type' => 'application/json'}, :body => {:id => "2345", :state => "processing"})
|
37
|
+
|
38
|
+
site = client.sites.create(:dir => dir)
|
39
|
+
|
40
|
+
assert_equal index_sha, body['files']['/index.html']
|
41
|
+
|
42
|
+
assert_requested :put, "https://api.netlify.com/api/v1/deploys/2345/files/index.html",
|
43
|
+
:body => ::File.read(::File.join(dir, "index.html")), :times => 1 # ===> Success
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_create_from_zip
|
47
|
+
stub_request(:post, "https://api.netlify.com/api/v1/sites")
|
48
|
+
.to_return({
|
49
|
+
:headers => {'Content-Type' => 'application/json'},
|
50
|
+
:body => JSON.generate({:id => "1234"})
|
51
|
+
})
|
52
|
+
|
53
|
+
stub_request(:post, "https://api.netlify.com/api/v1/sites/1234/deploys")
|
54
|
+
.to_return {|request|
|
55
|
+
{
|
56
|
+
:headers => {'Content-Type' => 'application/json'},
|
57
|
+
:body => JSON.generate({:id => "2345", :state => "uploading", :required => []})
|
58
|
+
}
|
59
|
+
}
|
60
|
+
zip = ::File.expand_path("../files/site-dir.zip", __FILE__)
|
61
|
+
site = client.sites.create(:zip => zip)
|
62
|
+
|
63
|
+
assert_requested :post, "https://api.netlify.com/api/v1/sites/1234/deploys",
|
64
|
+
:body => ::File.read(zip), :times => 1
|
65
|
+
end
|
66
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,156 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: netlify
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mathias Biilmann Christensen
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-11-17 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: oauth2
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.9.2
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.9.2
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: slop
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: highline
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
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: minitest
|
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: webmock
|
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
|
+
description: API Client for Netlify
|
84
|
+
email:
|
85
|
+
- mathias@Netlify.com
|
86
|
+
executables: []
|
87
|
+
extensions: []
|
88
|
+
extra_rdoc_files: []
|
89
|
+
files:
|
90
|
+
- Gemfile
|
91
|
+
- Gemfile.lock
|
92
|
+
- LICENSE.txt
|
93
|
+
- README.md
|
94
|
+
- Rakefile
|
95
|
+
- lib/netlify.rb
|
96
|
+
- lib/netlify/access_token.rb
|
97
|
+
- lib/netlify/access_tokens.rb
|
98
|
+
- lib/netlify/client.rb
|
99
|
+
- lib/netlify/collection_proxy.rb
|
100
|
+
- lib/netlify/deploy.rb
|
101
|
+
- lib/netlify/deploys.rb
|
102
|
+
- lib/netlify/dns_record.rb
|
103
|
+
- lib/netlify/dns_records.rb
|
104
|
+
- lib/netlify/dns_zone.rb
|
105
|
+
- lib/netlify/dns_zones.rb
|
106
|
+
- lib/netlify/file.rb
|
107
|
+
- lib/netlify/files.rb
|
108
|
+
- lib/netlify/form.rb
|
109
|
+
- lib/netlify/forms.rb
|
110
|
+
- lib/netlify/model.rb
|
111
|
+
- lib/netlify/multipass.rb
|
112
|
+
- lib/netlify/site.rb
|
113
|
+
- lib/netlify/sites.rb
|
114
|
+
- lib/netlify/snippet.rb
|
115
|
+
- lib/netlify/snippets.rb
|
116
|
+
- lib/netlify/submission.rb
|
117
|
+
- lib/netlify/submissions.rb
|
118
|
+
- lib/netlify/user.rb
|
119
|
+
- lib/netlify/users.rb
|
120
|
+
- lib/netlify/version.rb
|
121
|
+
- test/client_test.rb
|
122
|
+
- test/files/site-dir.zip
|
123
|
+
- test/files/site-dir/index.html
|
124
|
+
- test/multipass_test.rb
|
125
|
+
- test/sites_test.rb
|
126
|
+
- test/test_helper.rb
|
127
|
+
homepage: https://www.netlify.com
|
128
|
+
licenses: []
|
129
|
+
metadata: {}
|
130
|
+
post_install_message:
|
131
|
+
rdoc_options: []
|
132
|
+
require_paths:
|
133
|
+
- lib
|
134
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
140
|
+
requirements:
|
141
|
+
- - ">="
|
142
|
+
- !ruby/object:Gem::Version
|
143
|
+
version: '0'
|
144
|
+
requirements: []
|
145
|
+
rubyforge_project:
|
146
|
+
rubygems_version: 2.2.2
|
147
|
+
signing_key:
|
148
|
+
specification_version: 4
|
149
|
+
summary: API Client for Netlify
|
150
|
+
test_files:
|
151
|
+
- test/client_test.rb
|
152
|
+
- test/files/site-dir.zip
|
153
|
+
- test/files/site-dir/index.html
|
154
|
+
- test/multipass_test.rb
|
155
|
+
- test/sites_test.rb
|
156
|
+
- test/test_helper.rb
|