shopify_theme 0.0.22 → 0.0.23
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -1
- data/.travis.yml +1 -0
- data/CHANGELOG.md +9 -0
- data/README.md +14 -3
- data/bin/theme +1 -0
- data/lib/shopify_theme.rb +5 -1
- data/lib/shopify_theme/api_checker.rb +41 -0
- data/lib/shopify_theme/cli.rb +30 -7
- data/lib/shopify_theme/version.rb +1 -1
- data/spec/cassettes/api_check_api_down.yml +55 -0
- data/spec/cassettes/api_check_success.yml +55 -0
- data/spec/cassettes/api_check_unauthorized.yml +55 -0
- data/spec/unit/api_checker_spec.rb +44 -0
- metadata +9 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6971fe42d85d617b5d0a95de3c9e34279dac2199
|
4
|
+
data.tar.gz: d3ca43ec6f1b2ed28266e67a6036d748709a0f85
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 994ba6ea158f91729a131200ebc778c72467234febfa8716e72f829156c75fd028e6d9c085ba80bc6fa0bd7e9a5d6c11a7778db69d1bd42c06e37134abfb87e2
|
7
|
+
data.tar.gz: 94ee9d1a1266d99e6b9a0f762e8bfe0e8cfa50bf09f2651a58d070e5de1c42e73020bfdadcfd3d45e1e51b8cbcb0ee54e2e83a9863dc8f1ad0f5a844c6e72592
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
# v0.0.23 (Unreleased)
|
2
|
+
|
3
|
+
* Adds support for OAuth access token based authentication -- gavinballard
|
4
|
+
|
5
|
+
# v0.0.22
|
6
|
+
|
7
|
+
* Parsing of Timber release feed to always retrieve latest release
|
8
|
+
* Minor documentation improvements
|
9
|
+
|
1
10
|
# v0.0.21
|
2
11
|
|
3
12
|
* Adds Locale support
|
data/README.md
CHANGED
@@ -10,7 +10,7 @@ The Shopify theme gem is a command line tool that allows you to manage and edit
|
|
10
10
|
## Requirements
|
11
11
|
This gem works with OS X or Windows with Ruby 1.9.
|
12
12
|
|
13
|
-
First time installing Ruby on Windows? Try [Rubyinstaller](http://
|
13
|
+
First time installing Ruby on Windows? Try [Rubyinstaller](http://rubyinstaller.org/).
|
14
14
|
|
15
15
|
|
16
16
|
## Configuration
|
@@ -24,9 +24,9 @@ Click on “Create a Private App” to generate the credentials for a new app. Y
|
|
24
24
|
|
25
25
|
Navigate to the directory where you theme files live, or where you'd like them to be, and execute the following command:
|
26
26
|
|
27
|
-
`theme configure api_key password
|
27
|
+
`theme configure api_key password store_domain`
|
28
28
|
|
29
|
-
**Important:** enter the `
|
29
|
+
**Important:** enter the `store_domain` with no `http://`.
|
30
30
|
|
31
31
|
In your directory you should see a file named config.yml with your credentials. If you want, you may also edit the config.yml file directly and paste your API Key and Password in the appropriate area.
|
32
32
|
|
@@ -74,6 +74,17 @@ Specifies which files and directories should be responding to changes. If nothin
|
|
74
74
|
|
75
75
|
Specifies which files should be explicitly ignored and not affected by any commands entered.
|
76
76
|
|
77
|
+
### Alternative OAuth configuration (for advanced users)
|
78
|
+
As of v0.0.23, you can use a Shopify application's OAuth access token to authenticate with a store and manipulate theme files.
|
79
|
+
|
80
|
+
Once your application has obtained an access token through Shopify's regular OAuth authentication flow, run the following command to configure the gem:
|
81
|
+
|
82
|
+
`theme configure_oauth access_token store_domain`
|
83
|
+
|
84
|
+
The gem can then be used in exactly the same way as if you were authenticating via a Private App.
|
85
|
+
|
86
|
+
Note that the access token your Application has obtained ***must*** be valid for the `write_themes` scope in order to use the theme gem in this way.
|
87
|
+
|
77
88
|
|
78
89
|
## Commands
|
79
90
|
|
data/bin/theme
CHANGED
data/lib/shopify_theme.rb
CHANGED
@@ -132,9 +132,13 @@ module ShopifyTheme
|
|
132
132
|
shopify.get(path).code == 200
|
133
133
|
end
|
134
134
|
|
135
|
+
def self.get_index
|
136
|
+
shopify.get(path)
|
137
|
+
end
|
138
|
+
|
135
139
|
private
|
136
140
|
def self.shopify
|
137
|
-
|
141
|
+
headers 'X-Shopify-Access-Token' => config[:password] || config[:access_token]
|
138
142
|
base_uri "https://#{config[:store]}"
|
139
143
|
ShopifyTheme
|
140
144
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module ShopifyTheme
|
2
|
+
class APIChecker
|
3
|
+
class APIResult
|
4
|
+
OK = 200
|
5
|
+
UNAUTHORIZED = 401
|
6
|
+
SERVER_ERROR_CODES = (500..599)
|
7
|
+
|
8
|
+
attr_reader :response
|
9
|
+
def initialize(http_response)
|
10
|
+
@response = http_response
|
11
|
+
end
|
12
|
+
|
13
|
+
def accessed_api?
|
14
|
+
response.code == OK
|
15
|
+
end
|
16
|
+
|
17
|
+
def cannot_access_api?
|
18
|
+
!accessed_api?
|
19
|
+
end
|
20
|
+
|
21
|
+
def invalid_config?
|
22
|
+
response.code == UNAUTHORIZED
|
23
|
+
end
|
24
|
+
|
25
|
+
def api_down?
|
26
|
+
SERVER_ERROR_CODES.include?(response.code)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def initialize(client)
|
31
|
+
@client = client
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_connectivity
|
35
|
+
return APIResult.new(client.get_index)
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
attr_reader :client
|
40
|
+
end
|
41
|
+
end
|
data/lib/shopify_theme/cli.rb
CHANGED
@@ -36,18 +36,36 @@ module ShopifyTheme
|
|
36
36
|
end
|
37
37
|
|
38
38
|
desc "check", "check configuration"
|
39
|
-
def check
|
40
|
-
|
41
|
-
|
39
|
+
def check(exit_on_failure=false)
|
40
|
+
result = APIChecker.new(ShopifyTheme).test_connectivity
|
41
|
+
|
42
|
+
if result.api_down?
|
43
|
+
say("Cannot connect to Shopify. API appears to be down", :red)
|
44
|
+
say("Visit http://status.shopify.com for more details", :yello)
|
45
|
+
elsif result.invalid_config?
|
46
|
+
say("Cannot connect to Shopify. Configuration is invalid.", :red)
|
47
|
+
say("Verify that your API key, password and domain are correct.", :yellow)
|
48
|
+
say("Visit https://github.com/shopify/shopify_theme#configuration for more details", :yellow)
|
49
|
+
say("If your shop domain is correct, the following URL should take you to the Private Apps page for the shop:", :yellow)
|
50
|
+
say(" https://#{config[:store]}/admin/apps/private", :yellow)
|
42
51
|
else
|
43
|
-
say("
|
52
|
+
say("Shopify API is accessible and configuration is valid", :green) unless exit_on_failure
|
44
53
|
end
|
54
|
+
|
55
|
+
exit(1) if result.cannot_access_api? && exit_on_failure
|
45
56
|
end
|
46
57
|
|
47
58
|
desc "configure API_KEY PASSWORD STORE THEME_ID", "generate a config file for the store to connect to"
|
48
59
|
def configure(api_key=nil, password=nil, store=nil, theme_id=nil)
|
49
60
|
config = {:api_key => api_key, :password => password, :store => store, :theme_id => theme_id}
|
50
61
|
create_file('config.yml', config.to_yaml)
|
62
|
+
check(true)
|
63
|
+
end
|
64
|
+
|
65
|
+
desc "configure_oauth ACCESS_TOKEN STORE THEME_ID", "generate a config file for the store to connect to using an OAuth access token"
|
66
|
+
def configure_oauth(access_token=nil, store=nil, theme_id=nil)
|
67
|
+
config = {:access_token => access_token, :store => store, :theme_id => theme_id}
|
68
|
+
create_file('config.yml', config.to_yaml)
|
51
69
|
end
|
52
70
|
|
53
71
|
desc "bootstrap API_KEY PASSWORD STORE THEME_NAME", "bootstrap with Timber to shop and configure local directory."
|
@@ -55,6 +73,7 @@ module ShopifyTheme
|
|
55
73
|
method_option :version, :type => :string, :default => "latest"
|
56
74
|
def bootstrap(api_key=nil, password=nil, store=nil, theme_name=nil)
|
57
75
|
ShopifyTheme.config = {:api_key => api_key, :password => password, :store => store}
|
76
|
+
check(true)
|
58
77
|
|
59
78
|
theme_name ||= 'Timber'
|
60
79
|
say("Registering #{theme_name} theme on #{store}", :green)
|
@@ -78,6 +97,7 @@ module ShopifyTheme
|
|
78
97
|
method_option :quiet, :type => :boolean, :default => false
|
79
98
|
method_option :exclude
|
80
99
|
def download(*keys)
|
100
|
+
check(true)
|
81
101
|
assets = keys.empty? ? ShopifyTheme.asset_list : keys
|
82
102
|
|
83
103
|
if options['exclude']
|
@@ -101,6 +121,7 @@ module ShopifyTheme
|
|
101
121
|
desc "upload FILE", "upload all theme assets to shop"
|
102
122
|
method_option :quiet, :type => :boolean, :default => false
|
103
123
|
def upload(*keys)
|
124
|
+
check(true)
|
104
125
|
assets = keys.empty? ? local_assets_list : keys
|
105
126
|
assets.each do |asset|
|
106
127
|
send_asset(asset, options['quiet'])
|
@@ -111,6 +132,7 @@ module ShopifyTheme
|
|
111
132
|
desc "replace FILE", "completely replace shop theme assets with local theme assets"
|
112
133
|
method_option :quiet, :type => :boolean, :default => false
|
113
134
|
def replace(*keys)
|
135
|
+
check(true)
|
114
136
|
say("Are you sure you want to completely replace your shop theme assets? This is not undoable.", :yellow)
|
115
137
|
if ask("Continue? (Y/N): ") == "Y"
|
116
138
|
# only delete files on remote that are not present locally
|
@@ -130,6 +152,7 @@ module ShopifyTheme
|
|
130
152
|
desc "remove FILE", "remove theme asset"
|
131
153
|
method_option :quiet, :type => :boolean, :default => false
|
132
154
|
def remove(*keys)
|
155
|
+
check(true)
|
133
156
|
keys.each do |key|
|
134
157
|
delete_asset(key, options['quiet'])
|
135
158
|
end
|
@@ -140,14 +163,14 @@ module ShopifyTheme
|
|
140
163
|
method_option :quiet, :type => :boolean, :default => false
|
141
164
|
method_option :keep_files, :type => :boolean, :default => false
|
142
165
|
def watch
|
166
|
+
check(true)
|
143
167
|
puts "Watching current folder: #{Dir.pwd}"
|
144
168
|
watcher do |filename, event|
|
145
169
|
filename = filename.gsub("#{Dir.pwd}/", '')
|
146
170
|
|
147
|
-
|
148
|
-
action = if [:changed, :new].include?(event)
|
171
|
+
action = if [:changed, :new].include?(event) && local_assets_list.include?(filename)
|
149
172
|
:send_asset
|
150
|
-
elsif event == :delete
|
173
|
+
elsif event == :delete && !options['keep_files']
|
151
174
|
:delete_asset
|
152
175
|
else
|
153
176
|
raise NotImplementedError, "Unknown event -- #{event} -- #{filename}"
|
@@ -0,0 +1,55 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: get
|
5
|
+
uri: https://something.myshopify.com/admin/assets.json
|
6
|
+
body:
|
7
|
+
encoding: US-ASCII
|
8
|
+
string: ''
|
9
|
+
headers:
|
10
|
+
X-Shopify-Access-Token:
|
11
|
+
- alakazam
|
12
|
+
response:
|
13
|
+
status:
|
14
|
+
code: 503
|
15
|
+
message: Unavailable
|
16
|
+
headers:
|
17
|
+
Server:
|
18
|
+
- nginx
|
19
|
+
Date:
|
20
|
+
- Tue, 01 Sep 2015 18:47:36 GMT
|
21
|
+
Content-Type:
|
22
|
+
- application/json; charset=utf-8
|
23
|
+
Transfer-Encoding:
|
24
|
+
- chunked
|
25
|
+
Connection:
|
26
|
+
- keep-alive
|
27
|
+
X-Sorting-Hat-Podid:
|
28
|
+
- '0'
|
29
|
+
X-Sorting-Hat-Shopid:
|
30
|
+
- '2538'
|
31
|
+
X-Sorting-Hat-Podid-Cached:
|
32
|
+
- '1'
|
33
|
+
X-Sorting-Hat-Shopid-Cached:
|
34
|
+
- '1'
|
35
|
+
Status:
|
36
|
+
- 401 Unauthorized
|
37
|
+
X-Frame-Options:
|
38
|
+
- DENY
|
39
|
+
X-Shopid:
|
40
|
+
- '2538'
|
41
|
+
X-Shardid:
|
42
|
+
- '0'
|
43
|
+
Www-Authenticate:
|
44
|
+
- Basic Realm="Shopify API Authentication"
|
45
|
+
X-Xss-Protection:
|
46
|
+
- 1; mode=block; report=/xss-report/6cb4f1eb-40ea-4f96-b36c-e7edc4cc6005?source%5Baction%5D=index&source%5Bcontroller%5D=admin%2Fassets&source%5Bsection%5D=admin
|
47
|
+
X-Request-Id:
|
48
|
+
- 6cb4f1eb-40ea-4f96-b36c-e7edc4cc6005
|
49
|
+
body:
|
50
|
+
encoding: UTF-8
|
51
|
+
string: '{"errors":"[API] Invalid API key or access token (unrecognized login
|
52
|
+
or wrong password)"}'
|
53
|
+
http_version:
|
54
|
+
recorded_at: Tue, 01 Sep 2015 18:47:36 GMT
|
55
|
+
recorded_with: VCR 2.9.3
|
@@ -0,0 +1,55 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: get
|
5
|
+
uri: https://something.myshopify.com/admin/assets.json
|
6
|
+
body:
|
7
|
+
encoding: US-ASCII
|
8
|
+
string: ''
|
9
|
+
headers:
|
10
|
+
X-Shopify-Access-Token:
|
11
|
+
- alakazam
|
12
|
+
response:
|
13
|
+
status:
|
14
|
+
code: 200
|
15
|
+
message: OK
|
16
|
+
headers:
|
17
|
+
Server:
|
18
|
+
- nginx
|
19
|
+
Date:
|
20
|
+
- Tue, 01 Sep 2015 18:47:35 GMT
|
21
|
+
Content-Type:
|
22
|
+
- application/json; charset=utf-8
|
23
|
+
Transfer-Encoding:
|
24
|
+
- chunked
|
25
|
+
Connection:
|
26
|
+
- keep-alive
|
27
|
+
X-Sorting-Hat-Podid:
|
28
|
+
- '0'
|
29
|
+
X-Sorting-Hat-Shopid:
|
30
|
+
- '2538'
|
31
|
+
X-Sorting-Hat-Podid-Cached:
|
32
|
+
- '0'
|
33
|
+
X-Sorting-Hat-Shopid-Cached:
|
34
|
+
- '0'
|
35
|
+
Status:
|
36
|
+
- 401 Unauthorized
|
37
|
+
X-Frame-Options:
|
38
|
+
- DENY
|
39
|
+
X-Shopid:
|
40
|
+
- '2538'
|
41
|
+
X-Shardid:
|
42
|
+
- '0'
|
43
|
+
Www-Authenticate:
|
44
|
+
- Basic Realm="Shopify API Authentication"
|
45
|
+
X-Xss-Protection:
|
46
|
+
- 1; mode=block; report=/xss-report/f2203a5b-1bf3-4207-942b-81472f5d3073?source%5Baction%5D=index&source%5Bcontroller%5D=admin%2Fassets&source%5Bsection%5D=admin
|
47
|
+
X-Request-Id:
|
48
|
+
- f2203a5b-1bf3-4207-942b-81472f5d3073
|
49
|
+
body:
|
50
|
+
encoding: UTF-8
|
51
|
+
string: '{"errors":"[API] Invalid API key or access token (unrecognized login
|
52
|
+
or wrong password)"}'
|
53
|
+
http_version:
|
54
|
+
recorded_at: Tue, 01 Sep 2015 18:47:36 GMT
|
55
|
+
recorded_with: VCR 2.9.3
|
@@ -0,0 +1,55 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: get
|
5
|
+
uri: https://something.myshopify.com/admin/assets.json
|
6
|
+
body:
|
7
|
+
encoding: US-ASCII
|
8
|
+
string: ''
|
9
|
+
headers:
|
10
|
+
X-Shopify-Access-Token:
|
11
|
+
- alakazam
|
12
|
+
response:
|
13
|
+
status:
|
14
|
+
code: 401
|
15
|
+
message: Unauthorized
|
16
|
+
headers:
|
17
|
+
Server:
|
18
|
+
- nginx
|
19
|
+
Date:
|
20
|
+
- Tue, 01 Sep 2015 18:47:36 GMT
|
21
|
+
Content-Type:
|
22
|
+
- application/json; charset=utf-8
|
23
|
+
Transfer-Encoding:
|
24
|
+
- chunked
|
25
|
+
Connection:
|
26
|
+
- keep-alive
|
27
|
+
X-Sorting-Hat-Podid:
|
28
|
+
- '0'
|
29
|
+
X-Sorting-Hat-Shopid:
|
30
|
+
- '2538'
|
31
|
+
X-Sorting-Hat-Podid-Cached:
|
32
|
+
- '1'
|
33
|
+
X-Sorting-Hat-Shopid-Cached:
|
34
|
+
- '1'
|
35
|
+
Status:
|
36
|
+
- 401 Unauthorized
|
37
|
+
X-Frame-Options:
|
38
|
+
- DENY
|
39
|
+
X-Shopid:
|
40
|
+
- '2538'
|
41
|
+
X-Shardid:
|
42
|
+
- '0'
|
43
|
+
Www-Authenticate:
|
44
|
+
- Basic Realm="Shopify API Authentication"
|
45
|
+
X-Xss-Protection:
|
46
|
+
- 1; mode=block; report=/xss-report/94fab138-9672-45ff-81b7-38f35ba81ed2?source%5Baction%5D=index&source%5Bcontroller%5D=admin%2Fassets&source%5Bsection%5D=admin
|
47
|
+
X-Request-Id:
|
48
|
+
- 94fab138-9672-45ff-81b7-38f35ba81ed2
|
49
|
+
body:
|
50
|
+
encoding: UTF-8
|
51
|
+
string: '{"errors":"[API] Invalid API key or access token (unrecognized login
|
52
|
+
or wrong password)"}'
|
53
|
+
http_version:
|
54
|
+
recorded_at: Tue, 01 Sep 2015 18:47:36 GMT
|
55
|
+
recorded_with: VCR 2.9.3
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'shopify_theme'
|
3
|
+
require 'shopify_theme/api_checker'
|
4
|
+
|
5
|
+
module ShopifyTheme
|
6
|
+
describe "APIChecker" do
|
7
|
+
attr_reader :checker
|
8
|
+
|
9
|
+
before do
|
10
|
+
config = {
|
11
|
+
api_key: 'abracadabra',
|
12
|
+
password: 'alakazam',
|
13
|
+
store: 'something.myshopify.com'
|
14
|
+
}
|
15
|
+
ShopifyTheme.config = config
|
16
|
+
@checker = APIChecker.new(ShopifyTheme)
|
17
|
+
end
|
18
|
+
|
19
|
+
after do
|
20
|
+
ShopifyTheme.config = nil
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should return an APIResponse that says if the API is accessible when it gets a 200 response" do
|
24
|
+
VCR.use_cassette("api_check_success") do
|
25
|
+
response = checker.test_connectivity
|
26
|
+
assert response.accessed_api?
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should return an APIResponse that says the API is down if it gets a 500-series response" do
|
31
|
+
VCR.use_cassette("api_check_api_down") do
|
32
|
+
response = checker.test_connectivity
|
33
|
+
assert response.api_down?
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should return an APIResponse that says the client is misconfigured if it gets a 401 response" do
|
38
|
+
VCR.use_cassette("api_check_unauthorized") do
|
39
|
+
response = checker.test_connectivity
|
40
|
+
assert response.invalid_config?
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: shopify_theme
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.23
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John Duff
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-09-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -172,13 +172,18 @@ files:
|
|
172
172
|
- doc/API-key-and-password.jpg
|
173
173
|
- doc/how_to_find_theme_id.png
|
174
174
|
- lib/shopify_theme.rb
|
175
|
+
- lib/shopify_theme/api_checker.rb
|
175
176
|
- lib/shopify_theme/cli.rb
|
176
177
|
- lib/shopify_theme/releases.rb
|
177
178
|
- lib/shopify_theme/version.rb
|
178
179
|
- shipit.rubygems.yml
|
179
180
|
- shopify_theme.gemspec
|
181
|
+
- spec/cassettes/api_check_api_down.yml
|
182
|
+
- spec/cassettes/api_check_success.yml
|
183
|
+
- spec/cassettes/api_check_unauthorized.yml
|
180
184
|
- spec/cassettes/timber_releases.yml
|
181
185
|
- spec/spec_helper.rb
|
186
|
+
- spec/unit/api_checker_spec.rb
|
182
187
|
- spec/unit/cli_spec.rb
|
183
188
|
- spec/unit/releases_spec.rb
|
184
189
|
homepage: https://github.com/Shopify/shopify_theme
|
@@ -201,12 +206,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
201
206
|
version: '0'
|
202
207
|
requirements: []
|
203
208
|
rubyforge_project: shopify_theme
|
204
|
-
rubygems_version: 2.2.
|
209
|
+
rubygems_version: 2.2.3
|
205
210
|
signing_key:
|
206
211
|
specification_version: 4
|
207
212
|
summary: Command line tool for developing themes
|
208
|
-
test_files:
|
209
|
-
- spec/cassettes/timber_releases.yml
|
210
|
-
- spec/spec_helper.rb
|
211
|
-
- spec/unit/cli_spec.rb
|
212
|
-
- spec/unit/releases_spec.rb
|
213
|
+
test_files: []
|