# encoding: utf-8require'travis/cli'require'travis/tools/system'require'securerandom'require'openssl'require'digest'require'shellwords'moduleTravismoduleCLIclassEncryptFile<RepoCommandattr_accessor:stagedescription'encrypts a file and adds decryption steps to .travis.yml'on'-K','--key KEY','encryption key to be used (randomly generated otherwise)'on'--iv IV','encryption IV to be used (randomly generated otherwise)'on'-d','--decrypt','decrypt the file instead of encrypting it, requires key and iv'on'-f','--force','override output file if it exists'on'-p','--print-key','print (possibly generated) key and iv'on'-w','--decrypt-to PATH','where to write the decrypted file to on the Travis CI VM'on'-a','--add [STAGE]','automatically add command to .travis.yml (default stage is before_install)'do|c,stage|c.stage=stage||'before_install'enddefrun(input_path,output_path=nil)self.decrypt_to||=decrypt_to_for(input_path)output_path||=File.basename(output_path_for(input_path))self.output=$stdout.tty??StringIO.new:$stderrifoutput_path=='-'result=transcode(input_path)ifoutput_path=='-'$stdout.putsresultelsesay"storing result as #{color(output_path,:info)}"write_file(output_path,result,force)returnifdecrypt?error"requires --decrypt-to option when reading from stdin"unlessdecrypt_to?set_env_vars(input_path)command=decrypt_command(output_path)stage?store_command(command):print_command(command)notes(input_path,output_path)endenddefsetupsuperself.key||=SecureRandom.hex(32)unlessdecrypt?self.iv||=SecureRandom.hex(16)unlessdecrypt?error"key must be 64 characters long and a valid hex number"unlesskey=~/^[a-f0-9]{64}$/error"iv must be 32 characters long and a valid hex number"unlessiv=~/^[a-f0-9]{32}$/enddefprint_command(command)empty_linesaycommand,template(__FILE__)enddefstore_command(command)travis_config[stage]=Array(travis_config[stage])travis_config[stage].delete(command)travis_config[stage].unshift(command)save_travis_configenddefdecrypt_command(path)"openssl aes-256-cbc -K $#{env_name(:key)} -iv $#{env_name(:iv)} -in #{escape_path(path)} -out #{escape_path(decrypt_to)} -d"enddefset_env_vars(input_path)say"storing secure env variables for decryption"repository.env_vars.upsertenv_name(input_path,:key),key,:public=>falserepository.env_vars.upsertenv_name(input_path,:iv),iv,:public=>falseenddefenv_name(input_path,name)@env_prefix||="encrypted_#{Digest.hexencode(Digest::SHA1.digest(input_path)[0..5])}""#{@env_prefix}_#{name}"enddefnotes(input_path,output_path)say"\nkey: #{color(key,:info)}\niv: #{color(iv,:info)}"ifprint_key?empty_linesay"Make sure to add #{color(output_path,:info)} to the git repository."say"Make sure #{color("not",:underline)} to add #{color(input_path,:info)} to the git repository."ifinput_path!='-'say"Commit all changes to your #{color('.travis.yml',:info)}."enddeftranscode(input_path)description="stdin#{' (waiting for input)'if$stdin.tty?}"ifinput_path=='-'say"#{decrypt?"de":"en"}crypting #{color(description||input_path,:info)} for #{color(slug,:info)}"data=input_path=='-'?$stdin.read:File.binread(input_path)aes=OpenSSL::Cipher.new('AES-256-CBC')decrypt?aes.decrypt:aes.encryptaes.key=[key].pack('H*')aes.iv=[iv].pack('H*')aes.update(data)+aes.finalenddefdecrypt_to_for(input_path)returnifinput_path=='-'ifinput_path.start_with?Dir.homeinput_path.sub(Dir.home,'~')elseinput_pathendenddefescape_path(path)Shellwords.escape(path).sub(/^\\~\//,'~\/')enddefoutput_path_for(input_path)caseinput_pathwhen'-'thenreturn'-'when/^(.+)\.enc$/thenreturn$1ifdecrypt?when/^(.+)\.dec$/thenreturn$1unlessdecrypt?endifinteractive?andinput_path=~/(\.enc|\.dec)$/exit1unlessdanger_zone?"File extension of input file is #{color($1,:info)}, are you sure that is correct?"end"#{input_path}.#{decrypt?'dec':'enc'}"endendendend__END__
Please add the following to your build script (<[[ color('before_install', :info) ]]> stage in your <[[ color('.travis.yml', :info) ]]>, for instance):
%s
Pro Tip: You can add it automatically by running with <[[ color('--add', :info) ]]>.