├── .gitignore ├── .travis.yml ├── Gemfile ├── LICENSE.md ├── README.md ├── Rakefile ├── bin └── travis-custom-deploy ├── lib ├── travis-custom-deploy.rb └── travis-custom-deploy │ ├── deployment.rb │ ├── options.rb │ ├── transfer.rb │ ├── transfer │ ├── base.rb │ └── sftp.rb │ └── version.rb ├── test ├── helper.rb ├── test_deployment.rb └── test_options.rb └── travis-custom-deploy.gemspec /.gitignore: -------------------------------------------------------------------------------- 1 | Gemfile.lock 2 | *.swp 3 | rdoc/ 4 | .bundle/ 5 | coverage 6 | tags 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | cache: bundler 3 | before_install: 4 | - gem install bundler 5 | rvm: 6 | - 2.1.0 7 | - 2.0.0 8 | script: bundle exec rake 9 | deploy: 10 | provider: rubygems 11 | api_key: 12 | secure: fogw9FFH6muJHIu9/soA87R+YQJo+Zns4xS1zFR2XhkkAS92d/OspqSd3mqTtTzHrZH1yyLQOmhnR7hS1gsukPhXbVyUi07wp6DaZ3gH7zMJwkNnZgKqa0z1GscKAgzSu02f2hAIHs7Vh92RaQ90qp3u6oPATxKdj5tGm8oRZUo= 13 | gem: travis-custom-deploy 14 | on: 15 | tags: true 16 | repo: jens-na/travis-custom-deploy 17 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | gemspec 3 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License 2 | =============== 3 | 4 | Copyright (c) 2014, Jens Nazarenus 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | travis-custom-deploy [![Gem Version](https://badge.fury.io/rb/travis-custom-deploy.png)](http://badge.fury.io/rb/travis-custom-deploy) 2 | ==================== 3 | [![Build Status](https://travis-ci.org/jens-na/travis-custom-deploy.png?branch=master)](https://travis-ci.org/jens-na/travis-custom-deploy) 4 | [![Coverage Status](https://coveralls.io/repos/jens-na/travis-custom-deploy/badge.png?branch=master)](https://coveralls.io/r/jens-na/travis-custom-deploy?branch=master) 5 | [![Code Climate](https://codeclimate.com/github/jens-na/travis-custom-deploy.png)](https://codeclimate.com/github/jens-na/travis-custom-deploy) 6 | [![Dependency Status](https://gemnasium.com/jens-na/travis-custom-deploy.png)](https://gemnasium.com/jens-na/travis-custom-deploy) 7 | 8 | Deploy static files with [Travis-CI](https://travis-ci.org/) to your own server. 9 | 10 | Configuration 11 | ------------- 12 | Add the gem to your Gemfile or gemspec. 13 | 14 | ```ruby 15 | gem "travis-custom-deploy", "~> 0.0.6" 16 | ``` 17 | To use travis-custom-deploy with Travis-CI you need to define environment variables. I recommend 18 | to use [secure environment variables](http://about.travis-ci.org/docs/user/build-configuration/#Secure-environment-variables) with Travis-CI. The best way to do that is to use travis gem: 19 | ```ruby 20 | gem install travis 21 | cd my_project 22 | travis encrypt DEPLOY_HOST="your-hostname.com" --add 23 | travis encrypt DEPLOY_USERNAME="username" --add 24 | travis encrypt DEPLOY_PASSWORD="password" --add 25 | travis encrypt DEPLOY_REMOTEDIR="/path/to/deploy/" --add 26 | ``` 27 | 28 | To trigger the deployment you have to add the following line to your `~/.travis.yml` file: 29 | ```yml 30 | after_success: bundle exec travis-custom-deploy sftp _site/ 31 | ``` 32 | 33 | Example: 34 | ```yml 35 | language: ruby 36 | rvm: 2.0.0 37 | env: 38 | global: 39 | - secure: 40 | - secure: 41 | - secure: 42 | - secure: 43 | script: 44 | - bundle exec jekyll build --trace 45 | - bundle exec ./script/htmlcheck 46 | 47 | after_success: bundle exec travis-custom-deploy sftp _site/ 48 | ``` 49 | 50 | Protocol support 51 | ---------------- 52 | The protocol to use to transfer the files to the remote server. 53 | 54 | #### SFTP 55 | 56 | Necessary environment variables: 57 | - `DEPLOY_HOST` 58 | - `DEPLOY_USERNAME` 59 | - `DEPLOY_PASSWORD` 60 | - `DEPLOY_REMOTEDIR` 61 | 62 | Example: 63 | ``` 64 | after_success: bundle exec travis-custom-deploy sftp files/ 65 | ``` 66 | 67 | Services 68 | -------- 69 | There are predefined services available. For example if you want to deploy a [Jekyll](https://github.com/jekyll/jekyll) page you can write: 70 | 71 | ```yml 72 | after_success: travis-custom-deploy sftp service:jekyll 73 | ``` 74 | 75 | Services currently availble: 76 | ``` 77 | Service File 78 | -------- ------- 79 | jekyll _site/ 80 | ``` 81 | 82 | License and Copyright 83 | ===================== 84 | Licensed under the MIT license. 85 | 86 | Jens Nazarenus, 2014 87 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'rake' 3 | require 'rdoc' 4 | 5 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), *%w[lib])) 6 | 7 | # Returns the name of the project 8 | def name 9 | @name ||= Dir['*.gemspec'].first.split('.').first 10 | end 11 | 12 | # Returns the version number of the project 13 | def version 14 | line = File.read("lib/#{name}/version.rb")[/^\s*VERSION\s*=\s*.*/] 15 | line.match(/.*VERSION\s*=\s*['"](.*)['"]/)[1] 16 | end 17 | 18 | if ENV["TRAVIS"] == "true" 19 | require 'coveralls/rake/task' 20 | Coveralls::RakeTask.new 21 | 22 | task :default => [:test, 'coveralls:push'] 23 | else 24 | task :default => [:test] 25 | end 26 | 27 | # << test 28 | require 'rake/testtask' 29 | Rake::TestTask.new(:test) do |test| 30 | test.libs << 'lib' << 'test' 31 | test.pattern = 'test/**/test_*.rb' 32 | test.verbose = true 33 | end 34 | 35 | # << rdoc 36 | require 'rdoc/task' 37 | Rake::RDocTask.new do |rdoc| 38 | rdoc.rdoc_dir = 'rdoc' 39 | rdoc.title = "#{name} #{version}" 40 | rdoc.rdoc_files.include('README*') 41 | rdoc.rdoc_files.include('lib/**/*.rb') 42 | end 43 | -------------------------------------------------------------------------------- /bin/travis-custom-deploy: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'travis-custom-deploy' 4 | 5 | # The usage for the binary of travis-custom-deploy 6 | def usage 7 | puts 'Usage: travis-custom-deploy TRANSFER_TYPE FILES...' 8 | exit 1 9 | end 10 | 11 | # Returns a list of read environment variables in a hash based 12 | # on options.rb. The hash may be used as parameter for deployment.rb. 13 | def get_env_vars(transfer_type) 14 | envs = TravisCustomDeploy::Options.get_options(transfer_type) 15 | options = {} 16 | for e in envs 17 | value = ENV['DEPLOY_' + e.upcase] 18 | options[e] = value 19 | end 20 | options 21 | end 22 | 23 | if ARGV.count < 2 24 | usage 25 | end 26 | 27 | # do not deploy if a pull request is tested 28 | unless TravisCustomDeploy::Options.deploy_allowed? 29 | puts 'Deployment not allowed for pull requests.' 30 | exit 0 31 | end 32 | 33 | transfer_type = ARGV[0] 34 | files = ARGV[1..-1] 35 | 36 | options = get_env_vars(transfer_type) 37 | 38 | d = TravisCustomDeploy::Deployment.new(transfer_type, options, files) 39 | d.deploy 40 | -------------------------------------------------------------------------------- /lib/travis-custom-deploy.rb: -------------------------------------------------------------------------------- 1 | require 'travis-custom-deploy/deployment' 2 | require 'travis-custom-deploy/transfer' 3 | require 'travis-custom-deploy/version' 4 | require 'travis-custom-deploy/options' 5 | require 'travis-custom-deploy/transfer/base' 6 | -------------------------------------------------------------------------------- /lib/travis-custom-deploy/deployment.rb: -------------------------------------------------------------------------------- 1 | module TravisCustomDeploy 2 | 3 | class Deployment 4 | 5 | attr_reader :options 6 | attr_reader :files 7 | 8 | # Initializes a new deployment 9 | # 10 | # remteopts - the options to connect to the remote server 11 | # files - the files to transfer 12 | def initialize(transfer_type, options, files) 13 | raise ArgumentError, 'transfer type must not be nil' if transfer_type.nil? 14 | @files = files 15 | check_services(@files[0]) 16 | @options = options 17 | @transfer = get_transfer(transfer_type) 18 | end 19 | 20 | # Starts the deployment 21 | def deploy 22 | @transfer.transfer 23 | end 24 | 25 | # Creates an instance for the transfer type and return it 26 | # 27 | # type - the transfer type like sftp, ftp, etc. 28 | def get_transfer(type) 29 | type = type[0].upcase + type[1..-1] 30 | try_require(type) 31 | Transfer.const_get(type).new(@options, @files) 32 | end 33 | 34 | # Try requiring a transfer type class 35 | # 36 | # name - the name of the transfer type like: sftp, stp 37 | def try_require(name) 38 | require("travis-custom-deploy/transfer/#{name.downcase}") 39 | true 40 | end 41 | protected :try_require 42 | 43 | # Check if the first file matches service: 44 | # and try to determine the files based on the service. 45 | # 46 | # first_file the first file given 47 | def check_services(first_file) 48 | if first_file.start_with?('service:') 49 | service = first_file.sub(/service:/, '') 50 | SERVICES.each do |k,v| 51 | if k == service 52 | @files = v 53 | end 54 | end 55 | end 56 | end 57 | 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /lib/travis-custom-deploy/options.rb: -------------------------------------------------------------------------------- 1 | module TravisCustomDeploy 2 | 3 | # The available services. Each service has an 4 | # unique id and an array of files or directories to deploy. 5 | SERVICES = { 6 | 7 | # Jekyll support 8 | # Usage: service:jekyll 9 | 'jekyll' => [ '_site/' ] 10 | } 11 | 12 | # The possible options of the different transfer types 13 | OPTIONS = { 14 | 15 | 'sftp' => [ 16 | 'host', # host name of the sftp server 17 | 'username', # username to connect 18 | 'password', # password to connect 19 | 'remotedir' # remote dir, for example: /public/ 20 | ], 21 | 22 | 'ftp' => [ 23 | 'host', # host name of the ftp server 24 | 'username', # username to connect 25 | 'password', # password to connect 26 | 'remotedir' # remote dir, for example: /public/ 27 | ], 28 | 29 | 'copy' => [ 30 | 'remotedir' # the destination where to copy the files 31 | ] 32 | 33 | } 34 | 35 | class Options 36 | 37 | # Returns the options for the corresponding transfer type 38 | # 39 | # transfer_type - the transfer type 40 | # 41 | # Returns an array of options, which must be available 42 | def self.get_options(transfer_type) 43 | OPTIONS.each do |k,v| 44 | if k == transfer_type 45 | return v 46 | end 47 | end 48 | nil 49 | end 50 | 51 | # Returns true if travis-custom-deploy is not invoked as part of a pull 52 | # request. The deployment would fail anyway if secure environment 53 | # variables are used. 54 | # 55 | # Returns true if the deployment is allowed. 56 | def self.deploy_allowed? 57 | allowed = ENV['TRAVIS_PULL_REQUEST'] 58 | unless allowed.nil? 59 | return false if allowed != "false" 60 | end 61 | true 62 | end 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /lib/travis-custom-deploy/transfer.rb: -------------------------------------------------------------------------------- 1 | module TravisCustomDeploy 2 | 3 | module Transfer 4 | 5 | autoload :Sftp, 'travis-custom-deploy/transfer/sftp' 6 | 7 | end 8 | 9 | end 10 | -------------------------------------------------------------------------------- /lib/travis-custom-deploy/transfer/base.rb: -------------------------------------------------------------------------------- 1 | module TravisCustomDeploy 2 | 3 | module Transfer 4 | 5 | class Base 6 | 7 | attr_reader :options 8 | attr_reader :files 9 | 10 | def initialize(options, files) 11 | @options = options 12 | @files = files 13 | check_options(options) 14 | end 15 | 16 | # The method which needs to be implemented by subclasses 17 | # of transfer types. 18 | def transfer 19 | raise NotImplementedError 20 | end 21 | 22 | # The method which needs to be implemented by subclasses 23 | # to check if the remote options are sufficient for the 24 | # defined transfer type 25 | def check_options 26 | raise NotImplementedError 27 | end 28 | 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /lib/travis-custom-deploy/transfer/sftp.rb: -------------------------------------------------------------------------------- 1 | require 'net/sftp' 2 | 3 | module TravisCustomDeploy 4 | 5 | module Transfer 6 | 7 | # The class which transfers files via the SSH File Transfer Protocol 8 | # (SFTP). 9 | class Sftp < Base 10 | 11 | def initialize(options, files) 12 | super(options, files) 13 | prepare_remotedir 14 | end 15 | 16 | def transfer 17 | Net::SFTP.start(@options['host'], @options['username'], :password => @options['password']) do |sftp| 18 | for e in @files 19 | if File.directory?(e) 20 | send_dir(sftp, e, @remotedir) 21 | else 22 | sftp.upload!(e, @remotedir + "/" + File.basename(e)) 23 | end 24 | end 25 | end 26 | end 27 | 28 | # Check if the passed arguments are sufficient 29 | def check_options(options) 30 | raise ArgumentError, 'host name must not be nil' if options['host'].nil? 31 | raise ArgumentError, 'userna)e must not be nil' if options['username'].nil? 32 | raise ArgumentError, 'password must not be nil' if options['password'].nil? 33 | end 34 | 35 | # Sends a directory to the remote server with the given parameters 36 | # 37 | # sftp - the SFTP connection 38 | # dir - the local dir to transfer 39 | # the remote folder where to place the files 40 | def send_dir(sftp, dir, remote) 41 | Dir.foreach(dir) do |file_name| 42 | next if file_name =~ /^(\.|\.\.)$/ 43 | 44 | localfile = File.join(dir, file_name) 45 | if File.directory?(localfile) 46 | begin 47 | sftp.mkdir!(remote + "/" + file_name) 48 | rescue Net::SFTP::StatusException 49 | # the directory probably already exists 50 | end 51 | send_dir(sftp, File.join(dir, file_name), File.join(remote, file_name)) 52 | else 53 | sftp.upload!(File.join(dir, file_name), File.join(remote, file_name)) 54 | end 55 | end 56 | end 57 | 58 | # Prepares the remote directory (remote trailing slash) 59 | def prepare_remotedir 60 | @remotedir = @options['remotedir'] 61 | @remotedir = "" if @remotedir.nil? 62 | @remotedir = @remotedir[0..-2] if @remotedir[-1] == "/" 63 | end 64 | 65 | end 66 | end 67 | end 68 | -------------------------------------------------------------------------------- /lib/travis-custom-deploy/version.rb: -------------------------------------------------------------------------------- 1 | module TravisCustomDeploy 2 | 3 | VERSION = "0.0.6" 4 | 5 | end 6 | -------------------------------------------------------------------------------- /test/helper.rb: -------------------------------------------------------------------------------- 1 | require 'coveralls' 2 | Coveralls.wear_merged! 3 | 4 | require 'test/unit' 5 | require 'shoulda-context' 6 | require 'rr' 7 | 8 | require 'travis-custom-deploy' 9 | include TravisCustomDeploy 10 | -------------------------------------------------------------------------------- /test/test_deployment.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | class TestDeployment < Test::Unit::TestCase 4 | 5 | context "A deployment" do 6 | setup do 7 | @opts = { 8 | 'host' => 'example.org', 9 | 'username' => 'username', 10 | 'password' => 'password', 11 | 'remotedir' => '/public/' 12 | } 13 | end 14 | 15 | context "with files" do 16 | setup do 17 | @deployment = Deployment.new('sftp', @opts, ['foo', 'bar', 'foo2']) 18 | end 19 | 20 | should "return file names" do 21 | assert_equal ['foo', 'bar', 'foo2'], @deployment.files 22 | end 23 | end 24 | 25 | context "with service identifier as first file name" do 26 | setup do 27 | @deployment = Deployment.new('sftp', @opts, ['service:jekyll']) 28 | end 29 | 30 | should "return file names of service" do 31 | assert_equal ['_site/'], @deployment.files 32 | end 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /test/test_options.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | class TestOptions < Test::Unit::TestCase 4 | 5 | context "Options" do 6 | 7 | context "environment variables" do 8 | setup do 9 | @env = Options.get_options('sftp') 10 | end 11 | 12 | should "return sftp options" do 13 | assert_equal ['host', 'username', 'password', 'remotedir'], @env 14 | end 15 | end 16 | 17 | context "no pull request variable" do 18 | setup do 19 | ENV['TRAVIS_PULL_REQUEST'] = nil 20 | end 21 | 22 | should "allow deployment" do 23 | assert_equal true, Options::deploy_allowed? 24 | end 25 | end 26 | 27 | context "pull request variable false" do 28 | setup do 29 | ENV['TRAVIS_PULL_REQUEST'] = "false" 30 | end 31 | 32 | should "allow deployment" do 33 | assert_equal true, Options::deploy_allowed? 34 | end 35 | end 36 | 37 | context "pull request variable with number" do 38 | setup do 39 | ENV['TRAVIS_PULL_REQUEST'] = "1000" 40 | end 41 | 42 | should "don't allow deployment" do 43 | assert_equal false, Options::deploy_allowed? 44 | end 45 | end 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /travis-custom-deploy.gemspec: -------------------------------------------------------------------------------- 1 | $:.push File.expand_path("../lib", __FILE__) 2 | require "travis-custom-deploy/version" 3 | 4 | Gem::Specification.new do |s| 5 | s.name = "travis-custom-deploy" 6 | s.version = TravisCustomDeploy::VERSION 7 | s.platform = Gem::Platform::RUBY 8 | s.licenses = ['MIT'] 9 | s.authors = ["Jens Nazarenus"] 10 | s.email = ["me@jens-na.de"] 11 | s.homepage = "https://github.com/jens-na/travis-custom-deploy" 12 | s.summary = %q{Travis custom deployment gem} 13 | s.description = %q{Deploy static files to your own servers with Travis-CI} 14 | 15 | s.add_runtime_dependency('net-sftp', "~> 2.1.2") 16 | 17 | s.add_development_dependency("rspec", "~>2.14.1") 18 | s.add_development_dependency('rake', "~> 10.1.1") 19 | s.add_development_dependency('redgreen', "~> 1.2.2") 20 | s.add_development_dependency('shoulda-context', "~> 1.1.6") 21 | s.add_development_dependency('rr', "~> 1.1.2") 22 | s.add_development_dependency('coveralls', "~> 0.7.0") 23 | s.add_development_dependency('pry') 24 | s.add_development_dependency('pry-nav') 25 | 26 | s.files = `git ls-files`.split("\n") 27 | s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") 28 | s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } 29 | s.require_paths = ["lib"] 30 | end 31 | --------------------------------------------------------------------------------