percy-cli 0.0.2 → 0.1.0
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/README.md +3 -0
- data/bin/percy +1 -1
- data/lib/percy/cli/snapshot.rb +67 -31
- data/lib/percy/cli/version.rb +1 -1
- data/lib/percy/cli.rb +15 -0
- data/percy-cli.gemspec +2 -1
- data/spec/percy/cli/testdata/index.html +2 -0
- metadata +20 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7b25363b82da9f2043e7e5c748bc63d7e7e84535
|
4
|
+
data.tar.gz: f511e267c41c23f9b3cb04263ae6491919a811ce
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7b230e067e0ec50a21f27bd6fe0e53152f229a6502f1df9db3e5c50168f0091558d1bf73f5447de4468fef190420c8ee04b8909f7fa3df5161b8a766b4977b6a
|
7
|
+
data.tar.gz: 117c24852676b9870e6baebd3120697db9c1d4ffd609f79819cca57547d7e479cba3a370629897b17f07bf66265eef32b12acd0ee3b74091e90a6e4234ea3d6d
|
data/README.md
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
# Percy CLI
|
2
2
|
|
3
|
+
[](https://travis-ci.org/percy/percy-cli)
|
4
|
+
[](http://badge.fury.io/rb/percy-cli)
|
5
|
+
|
3
6
|
Command-line interface for [Percy](https://percy.io).
|
4
7
|
|
5
8
|
## Installation
|
data/bin/percy
CHANGED
data/lib/percy/cli/snapshot.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
require 'find'
|
2
2
|
require 'digest'
|
3
3
|
require 'uri'
|
4
|
+
require 'thread/pool'
|
5
|
+
|
6
|
+
Thread::Pool.abort_on_exception = true
|
4
7
|
|
5
8
|
module Percy
|
6
9
|
class Cli
|
@@ -44,9 +47,9 @@ module Percy
|
|
44
47
|
# resource path
|
45
48
|
"(?:/[^\\s\"']*)?"
|
46
49
|
)
|
47
|
-
HTML_REMOTE_URL_REGEX = Regexp.new("<link.*?href=['\"](" + REMOTE_URL_REGEX_STRING + ")")
|
50
|
+
HTML_REMOTE_URL_REGEX = Regexp.new("(<link.*?href=['\"](" + REMOTE_URL_REGEX_STRING + ")[^>]+)")
|
48
51
|
|
49
|
-
# Match all url("https://...")
|
52
|
+
# Match all url("https://...") styles, with whitespace and quote variatinos.
|
50
53
|
CSS_REMOTE_URL_REGEX = Regexp.new(
|
51
54
|
"url\\s*\\([\"'\s]*(" + REMOTE_URL_REGEX_STRING + ")[\"'\s]*\\)"
|
52
55
|
)
|
@@ -55,9 +58,8 @@ module Percy
|
|
55
58
|
repo = options[:repo] || Percy.config.repo
|
56
59
|
strip_prefix = File.absolute_path(options[:strip_prefix] || root_dir)
|
57
60
|
autoload_remote_resources = options[:autoload_remote_resources] || false
|
58
|
-
|
59
|
-
|
60
|
-
build = Percy.create_build(repo)
|
61
|
+
num_threads = options[:threads] || 10
|
62
|
+
snapshot_limit = options[:snapshot_limit]
|
61
63
|
|
62
64
|
# Find all the static files in the given root directory.
|
63
65
|
root_paths = find_root_paths(root_dir, snapshots_regex: options[:snapshots_regex])
|
@@ -70,14 +72,42 @@ module Percy
|
|
70
72
|
related_resources += build_remote_resources(remote_urls)
|
71
73
|
end
|
72
74
|
|
75
|
+
all_resources = root_resources + related_resources
|
76
|
+
|
77
|
+
if root_resources.empty?
|
78
|
+
say "No root resource files found. Are there HTML files in the given directory?"
|
79
|
+
exit(-1)
|
80
|
+
end
|
81
|
+
|
82
|
+
say 'Creating build...'
|
83
|
+
build = Percy.create_build(repo, resources: related_resources)
|
84
|
+
|
85
|
+
say 'Uploading build resources...'
|
86
|
+
upload_missing_resources(build, build, all_resources, {num_threads: num_threads})
|
87
|
+
|
73
88
|
# Upload a snapshot for every root resource, and associate the related_resources.
|
89
|
+
output_lock = Mutex.new
|
90
|
+
snapshot_thread_pool = Thread.pool(num_threads)
|
91
|
+
total = snapshot_limit ? [root_resources.length, snapshot_limit].min : root_resources.length
|
74
92
|
root_resources.each_with_index do |root_resource, i|
|
75
|
-
|
76
|
-
|
93
|
+
break if snapshot_limit && i + 1 > snapshot_limit
|
94
|
+
snapshot_thread_pool.process do
|
95
|
+
output_lock.synchronize do
|
96
|
+
say "Uploading snapshot (#{i+1}/#{total}): #{root_resource.resource_url}"
|
97
|
+
end
|
98
|
+
snapshot = Percy.create_snapshot(build['data']['id'], [root_resource])
|
99
|
+
upload_missing_resources(build, snapshot, all_resources, {num_threads: num_threads})
|
100
|
+
Percy.finalize_snapshot(snapshot['data']['id'])
|
101
|
+
end
|
77
102
|
end
|
103
|
+
snapshot_thread_pool.wait
|
104
|
+
snapshot_thread_pool.shutdown
|
78
105
|
|
79
106
|
# Finalize the build.
|
107
|
+
say 'Finalizing build...'
|
80
108
|
Percy.finalize_build(build['data']['id'])
|
109
|
+
say "Done! Percy is now processing, you can view the visual diffs here:"
|
110
|
+
say build['data']['attributes']['web-url']
|
81
111
|
end
|
82
112
|
|
83
113
|
private
|
@@ -121,13 +151,16 @@ module Percy
|
|
121
151
|
case extension
|
122
152
|
when '.html'
|
123
153
|
content = File.read(path)
|
124
|
-
urls += content.scan(HTML_REMOTE_URL_REGEX).map
|
154
|
+
urls += content.scan(HTML_REMOTE_URL_REGEX).map do |match|
|
155
|
+
next if !match[0].include?('stylesheet') # Only include links with rel="stylesheet".
|
156
|
+
maybe_add_protocol(match[1])
|
157
|
+
end
|
125
158
|
when '.css'
|
126
159
|
content = File.read(path)
|
127
160
|
urls += content.scan(CSS_REMOTE_URL_REGEX).map { |match| maybe_add_protocol(match[0]) }
|
128
161
|
end
|
129
162
|
end
|
130
|
-
urls.uniq
|
163
|
+
urls.compact.uniq
|
131
164
|
end
|
132
165
|
|
133
166
|
def maybe_add_protocol(url)
|
@@ -156,8 +189,8 @@ module Percy
|
|
156
189
|
remote_urls.length,
|
157
190
|
title: 'Fetching remote resources...',
|
158
191
|
format: ':title |:progress_bar| :percent_complete% complete - :url',
|
159
|
-
width:
|
160
|
-
complete_message:
|
192
|
+
width: 20,
|
193
|
+
complete_message: "Fetched #{remote_urls.length} remote resources.",
|
161
194
|
)
|
162
195
|
|
163
196
|
remote_urls.each do |url|
|
@@ -169,7 +202,7 @@ module Percy
|
|
169
202
|
next
|
170
203
|
end
|
171
204
|
if response.status != 200
|
172
|
-
say_error "Remote resource failed, skipping: #{url}"
|
205
|
+
say_error "Remote resource failed, skipping (#{response.status}): #{url}"
|
173
206
|
next
|
174
207
|
end
|
175
208
|
|
@@ -179,34 +212,37 @@ module Percy
|
|
179
212
|
resources
|
180
213
|
end
|
181
214
|
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
# Create the snapshot for this page. For simplicity, include all non-HTML resources in the
|
186
|
-
# snapshot as related resources. May seem inefficient, but they will only be uploaded once.
|
187
|
-
snapshot = Percy.create_snapshot(build['data']['id'], all_resources)
|
188
|
-
|
215
|
+
# Uploads missing resources either for a build or snapshot.
|
216
|
+
def upload_missing_resources(build, obj, potential_resources, options = {})
|
189
217
|
# Upload the content for any missing resources.
|
190
|
-
missing_resources =
|
218
|
+
missing_resources = obj['data']['relationships']['missing-resources']['data']
|
191
219
|
bar = Commander::UI::ProgressBar.new(
|
192
220
|
missing_resources.length,
|
193
221
|
title: 'Uploading resources...',
|
194
222
|
format: ':title |:progress_bar| :percent_complete% complete - :resource_url',
|
195
|
-
width:
|
223
|
+
width: 20,
|
196
224
|
complete_message: nil,
|
197
225
|
)
|
226
|
+
output_lock = Mutex.new
|
227
|
+
uploader_thread_pool = Thread.pool(options[:num_threads] || 10)
|
198
228
|
missing_resources.each do |missing_resource|
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
229
|
+
uploader_thread_pool.process do
|
230
|
+
missing_resource_sha = missing_resource['id']
|
231
|
+
resource = potential_resources.find { |r| r.sha == missing_resource_sha }
|
232
|
+
path = resource.resource_url
|
233
|
+
output_lock.synchronize do
|
234
|
+
bar.increment resource_url: resource.resource_url
|
235
|
+
end
|
236
|
+
|
237
|
+
# Remote resources are stored in 'content', local resources are read from the filesystem.
|
238
|
+
content = resource.content || File.read("#{resource.path}")
|
239
|
+
|
240
|
+
Percy.upload_resource(build['data']['id'], content)
|
241
|
+
end
|
208
242
|
end
|
243
|
+
uploader_thread_pool.wait
|
244
|
+
uploader_thread_pool.shutdown
|
209
245
|
end
|
210
246
|
end
|
211
247
|
end
|
212
|
-
end
|
248
|
+
end
|
data/lib/percy/cli/version.rb
CHANGED
data/lib/percy/cli.rb
CHANGED
@@ -8,6 +8,9 @@ module Percy
|
|
8
8
|
include Commander::Methods
|
9
9
|
include Percy::Cli::Snapshot
|
10
10
|
|
11
|
+
DEFAULT_NUM_THREADS = 10
|
12
|
+
MAX_NUM_THREADS = 50
|
13
|
+
|
11
14
|
def say(*args)
|
12
15
|
$terminal.say(*args)
|
13
16
|
end
|
@@ -38,13 +41,25 @@ module Percy
|
|
38
41
|
'--snapshots_regex REGEX',
|
39
42
|
String,
|
40
43
|
'Regular expression for matching the files to snapshot. Defaults to: "\.(html|htm)$"'
|
44
|
+
c.option \
|
45
|
+
'--snapshot_limit NUM',
|
46
|
+
Integer,
|
47
|
+
"Max number of snapshots to upload, useful for testing. Default is unlimited."
|
41
48
|
c.option \
|
42
49
|
'--autoload_remote_resources',
|
43
50
|
'Attempts to parse HTML and CSS for remote resources, fetch them, and include in ' +
|
44
51
|
'snapshots. This can be very useful if your static website relies on remote resources.'
|
52
|
+
c.option \
|
53
|
+
'--threads NUM',
|
54
|
+
Integer,
|
55
|
+
"Number of threads in pools for snapshot and resource uploads. " +
|
56
|
+
"Defaults to #{DEFAULT_NUM_THREADS}, max #{MAX_NUM_THREADS}."
|
45
57
|
|
46
58
|
c.action do |args, options|
|
47
59
|
options.default autoload_remote_resources: false
|
60
|
+
options.default threads: DEFAULT_NUM_THREADS
|
61
|
+
options.threads = MAX_NUM_THREADS if options.threads > MAX_NUM_THREADS
|
62
|
+
|
48
63
|
raise OptionParser::MissingArgument, 'root folder path is required' if args.empty?
|
49
64
|
if args.length > 1
|
50
65
|
raise OptionParser::MissingArgument, 'only a single root folder path can be given'
|
data/percy-cli.gemspec
CHANGED
@@ -19,8 +19,9 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.require_paths = ['lib']
|
20
20
|
|
21
21
|
spec.add_dependency 'commander', '~> 4'
|
22
|
-
spec.add_dependency 'percy-client', '
|
22
|
+
spec.add_dependency 'percy-client', '>= 0.2.4'
|
23
23
|
spec.add_dependency 'faraday', '>= 0.8'
|
24
|
+
spec.add_dependency 'thread', '~> 0.2'
|
24
25
|
|
25
26
|
spec.add_development_dependency 'bundler', '~> 1.7'
|
26
27
|
spec.add_development_dependency 'rake', '~> 10.0'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: percy-cli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Perceptual Inc.
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-06-
|
11
|
+
date: 2015-06-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: commander
|
@@ -28,16 +28,16 @@ dependencies:
|
|
28
28
|
name: percy-client
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - "
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
33
|
+
version: 0.2.4
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - "
|
38
|
+
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
40
|
+
version: 0.2.4
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: faraday
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -52,6 +52,20 @@ dependencies:
|
|
52
52
|
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0.8'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: thread
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.2'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.2'
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
70
|
name: bundler
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|