#!/usr/bin/env ruby require 'base64' require 'digest' require 'openssl' require 'shellwords' prefix = "ruby_#{RUBY_VERSION}-rails_#{ENV.fetch 'RAILS'}-" BUNDLE = prefix + 'bundle' APP = prefix + 'app' def download_bundle s3 :save, file: "#{BUNDLE}.sha2", as: "remote-#{BUNDLE}.sha2" s3 :save, file: "#{BUNDLE}.tgz", as: "remote-#{BUNDLE}.tgz", untar: true end def download_app # Force test app re-build if dependencies were updated unless digest_changed? 'Gemfile.lock', "remote-#{BUNDLE}.sha2", "#{BUNDLE}.sha2" s3 :save, file: "#{APP}.sha2", as: "remote-#{APP}.sha2" # Force test app re-build if spec/support changed unless digest_changed? 'spec/support', "remote-#{APP}.sha2", "#{APP}.sha2" s3 :save, file: "#{APP}.tgz", as: "remote-#{APP}.tgz", untar: true end end end def upload unless ID && SECRET puts "S3 credentials missing" return end [ ['Gemfile.lock', BUNDLE, 'bundle'], ['spec/support', APP, 'spec/rails'] ].each do |to_check, name, to_save| puts "=> Checking #{to_check} for changes" if ret = digest_changed?(to_check, "remote-#{name}.sha2", "#{name}.sha2") puts " => Changes found: #{ret[:old]} -> #{ret[:new]}" puts " => Creating an archive" `tar -cjf #{name}.tgz #{to_save}` puts " => Uploading a new archive" s3 :upload, file: "#{name}.tgz" s3 :upload, file: "#{name}.sha2" else puts " => There were no changes, doing nothing" end end end def digest_changed?(to_check, old_digest_file, new_digest_file) if File.exists? new_digest_file digest = File.read new_digest_file else # Supports a single file, as well as a folder files = Dir[to_check, "#{to_check}/**/*"].reject{ |f| File.directory? f } content = files.sort!.map{ |f| File.read f }.join digest = Digest::SHA2.hexdigest content File.write new_digest_file, digest end old_digest = File.read old_digest_file if File.exists? old_digest_file {old: old_digest, new: digest} if digest != old_digest end def sign(secret, to_sign) Base64.strict_encode64 OpenSSL::HMAC.digest OpenSSL::Digest::SHA1.new, secret, to_sign end ID = ENV['AWS_S3_ID'] SECRET = ENV['AWS_S3_SECRET'] URL = 'https://s3.amazonaws.com/ActiveAdmin' # s3 :list # s3 :upload, file: 'foo' # s3 :find, file: 'foo' # s3 :save, file: 'foo', as: 'bar' # s3 :save, file: 'foo', untar: true def s3(action, options = {}) verb = {list: :get, upload: :put, find: :get, save: :get}.fetch action file = options.fetch(:file) unless action == :list extra_arg = case action when :upload then "-T #{file}" when :save then options[:as] ? "-o #{options[:as]}" : '-O' end do_after = "&& tar -xf #{options[:as] || file}" if options[:untar] if ID && SECRET now = Time.now.strftime "%a, %d %b %Y %H:%M:%S %z" signature = sign SECRET, "#{verb.upcase}\n\n\n#{now}\n/ActiveAdmin/#{file}" headers = ["Authorization: AWS #{ID}:#{signature}", "Date: #{now}"].map do |h| "-H #{Shellwords.escape h}" end.join ' ' end output = `curl -f #{headers} #{extra_arg} #{URL}/#{file} #{do_after}` [$?.success?, output] end if %w[download_bundle download_app upload].include? ARGV[0] send ARGV[0] else raise "unexpected argument(s): #{ARGV}" end