shopify_theme 0.0.22 → 0.0.23
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/.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: []
|