require 'find' require 'digest' require 'uri' module Percy class Cli module Snapshot # Static resource types that an HTML file might load and that we want to upload for rendering. STATIC_RESOURCE_EXTENSIONS = [ '.css', '.jpg', '.jpeg', '.gif', '.ico', '.png', '.bmp', '.pict', '.tif', '.tiff', '.ttf', '.eot', '.woff', '.otf', '.svg', '.svgz', '.webp', '.ps', ].freeze DEFAULT_SNAPSHOTS_REGEX = /\.(html|htm)$/ # Modified version of Diego Perini's URL regex. https://gist.github.com/dperini/729294 REMOTE_URL_REGEX_STRING = ( # protocol identifier "(?:(?:https?:)?//)" + "(?:" + # IP address exclusion # private & local networks "(?!(?:10|127)(?:\\.\\d{1,3}){3})" + "(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})" + "(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})" + # IP address dotted notation octets # excludes loopback network 0.0.0.0 # excludes reserved space >= 224.0.0.0 # excludes network & broacast addresses # (first & last IP address of each class) "(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])" + "(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}" + "(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))" + "|" + # host name "(?:(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)" + # domain name "(?:\\.(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)*" + # TLD identifier "(?:\\.(?:[a-z\\u00a1-\\uffff]{2,}))" + ")" + # port number "(?::\\d{2,5})?" + # resource path "(?:/[^\\s\"']*)?" ) HTML_REMOTE_URL_REGEX = Regexp.new(" e say_error e next end if response.status != 200 say_error "Remote resource failed, skipping: #{url}" next end sha = Digest::SHA256.hexdigest(response.body) resources << Percy::Client::Resource.new(url, sha: sha, content: response.body) end resources end def upload_snapshot(build, root_resource, related_resources) all_resources = [root_resource] + related_resources # Create the snapshot for this page. For simplicity, include all non-HTML resources in the # snapshot as related resources. May seem inefficient, but they will only be uploaded once. snapshot = Percy.create_snapshot(build['data']['id'], all_resources) # Upload the content for any missing resources. missing_resources = snapshot['data']['relationships']['missing-resources']['data'] bar = Commander::UI::ProgressBar.new( missing_resources.length, title: 'Uploading resources...', format: ':title |:progress_bar| :percent_complete% complete - :resource_url', width: 40, complete_message: nil, ) missing_resources.each do |missing_resource| missing_resource_sha = missing_resource['id'] resource = all_resources.find { |r| r.sha == missing_resource_sha } path = resource.resource_url bar.increment resource_url: resource.resource_url # Remote resources are stored in 'content', local resources are read from the filesystem. content = resource.content || File.read("#{resource.path}") Percy.upload_resource(build['data']['id'], content) end end end end end