├── Gemfile ├── bin ├── setup └── console ├── test ├── helper.rb └── test_rake_ftp_file.rb ├── .gitignore ├── Rakefile ├── lib └── rake │ └── contrib │ ├── compositepublisher.rb │ ├── sshpublisher.rb │ └── ftptools.rb ├── rake-contrib.gemspec ├── LICENSE.txt └── README.md /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gemspec 4 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | set -vx 5 | 6 | bundle install 7 | -------------------------------------------------------------------------------- /test/helper.rb: -------------------------------------------------------------------------------- 1 | $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__) 2 | 3 | require 'minitest/autorun' 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /.yardoc 3 | /Gemfile.lock 4 | /_yardoc/ 5 | /coverage/ 6 | /doc/ 7 | /pkg/ 8 | /spec/reports/ 9 | /tmp/ 10 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require "bundler/setup" 4 | require "rake/contrib/compositepublisher" 5 | require "rake/contrib/ftptools" 6 | require "rake/contrib/sshpublisher" 7 | 8 | require "irb" 9 | IRB.start 10 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | require "rake/testtask" 3 | 4 | Rake::TestTask.new(:test) do |t| 5 | t.libs << "test" 6 | t.libs << "lib" 7 | t.test_files = FileList['test/**/test_*.rb'] 8 | end 9 | 10 | task :default => :test 11 | -------------------------------------------------------------------------------- /lib/rake/contrib/compositepublisher.rb: -------------------------------------------------------------------------------- 1 | module Rake 2 | 3 | # Manage several publishers as a single entity. 4 | class CompositePublisher 5 | def initialize 6 | @publishers = [] 7 | end 8 | 9 | # Add a publisher to the composite. 10 | def add(pub) 11 | @publishers << pub 12 | end 13 | 14 | # Upload all the individual publishers. 15 | def upload 16 | @publishers.each { |p| p.upload } 17 | end 18 | end 19 | 20 | end 21 | 22 | -------------------------------------------------------------------------------- /rake-contrib.gemspec: -------------------------------------------------------------------------------- 1 | Gem::Specification.new do |spec| 2 | spec.name = "rake-contrib" 3 | spec.version = "1.0.0" 4 | spec.authors = ["SHIBATA Hiroshi"] 5 | spec.email = ["hsbt@ruby-lang.org"] 6 | 7 | spec.summary = %q{Additional libraries for Rake} 8 | spec.description = %q{Additional libraries for Rake} 9 | spec.homepage = "https://github.com/ruby/rake-contrib" 10 | spec.license = "MIT" 11 | 12 | spec.files = Dir["lib/**/*.rb", "README.md", "LICENSE.txt"] 13 | spec.require_paths = ["lib"] 14 | 15 | spec.add_dependency "rake" 16 | 17 | spec.add_development_dependency "bundler" 18 | spec.add_development_dependency "minitest" 19 | end 20 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 SHIBATA Hiroshi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /lib/rake/contrib/sshpublisher.rb: -------------------------------------------------------------------------------- 1 | require 'rake/dsl_definition' 2 | require 'rake/contrib/compositepublisher' 3 | 4 | module Rake 5 | # Publish an entire directory to an existing remote directory using 6 | # SSH. 7 | class SshDirPublisher 8 | include Rake::DSL 9 | 10 | # Creates an SSH publisher which will scp all files in +local_dir+ to 11 | # +remote_dir+ on +host+ 12 | 13 | def initialize(host, remote_dir, local_dir) 14 | @host = host 15 | @remote_dir = remote_dir 16 | @local_dir = local_dir 17 | end 18 | 19 | # Uploads the files 20 | 21 | def upload 22 | sh "scp", "-rq", "#{@local_dir}/*", "#{@host}:#{@remote_dir}" 23 | end 24 | end 25 | 26 | # Publish an entire directory to a fresh remote directory using SSH. 27 | class SshFreshDirPublisher < SshDirPublisher 28 | 29 | # Uploads the files after removing the existing remote directory. 30 | 31 | def upload 32 | sh "ssh", @host, "rm", "-rf", @remote_dir rescue nil 33 | sh "ssh", @host, "mkdir", @remote_dir 34 | super 35 | end 36 | end 37 | 38 | # Publish a list of files to an existing remote directory. 39 | class SshFilePublisher 40 | include Rake::DSL 41 | 42 | # Creates an SSH publisher which will scp all +files+ in +local_dir+ to 43 | # +remote_dir+ on +host+. 44 | 45 | def initialize(host, remote_dir, local_dir, *files) 46 | @host = host 47 | @remote_dir = remote_dir 48 | @local_dir = local_dir 49 | @files = files 50 | end 51 | 52 | # Uploads the files 53 | 54 | def upload 55 | @files.each do |fn| 56 | sh "scp", "-q", "#{@local_dir}/#{fn}", "#{@host}:#{@remote_dir}" 57 | end 58 | end 59 | end 60 | end 61 | -------------------------------------------------------------------------------- /test/test_rake_ftp_file.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../helper', __FILE__) 2 | require 'date' 3 | require 'time' 4 | require 'rake/contrib/ftptools' 5 | 6 | class FakeDate 7 | def self.today 8 | Date.new(2003, 10, 3) 9 | end 10 | 11 | def self.now 12 | Time.local(2003, 10, 3, 12, 00, 00) 13 | end 14 | end 15 | 16 | class TestRakeFtpFile < Minitest::Test 17 | 18 | def setup 19 | super 20 | 21 | Rake::FtpFile.class_eval { 22 | @date_class = FakeDate 23 | @time_class = FakeDate 24 | } 25 | end 26 | 27 | def test_general 28 | file = Rake::FtpFile.new( 29 | "here", 30 | "-rw-r--r-- 1 a279376 develop 121770 Mar 6 14:50 wiki.pl") 31 | assert_equal "wiki.pl", file.name 32 | assert_equal "here/wiki.pl", file.path 33 | assert_equal "a279376", file.owner 34 | assert_equal "develop", file.group 35 | assert_equal 0644, file.mode 36 | assert_equal 121_770, file.size 37 | assert_equal Time.mktime(2003, 3, 6, 14, 50, 0, 0), file.time 38 | assert ! file.directory? 39 | assert ! file.symlink? 40 | end 41 | 42 | def test_far_date 43 | file = Rake::FtpFile.new( 44 | ".", 45 | "drwxr-xr-x 3 a279376 develop 4096 Nov 26 2001 vss") 46 | assert_equal Time.mktime(2001, 11, 26, 0, 0, 0, 0), file.time 47 | end 48 | 49 | def test_close_date 50 | file = Rake::FtpFile.new( 51 | ".", 52 | "drwxr-xr-x 3 a279376 develop 4096 Nov 26 15:35 vss") 53 | assert_equal Time.mktime(2002, 11, 26, 15, 35, 0, 0), file.time 54 | end 55 | 56 | def test_directory 57 | file = Rake::FtpFile.new( 58 | ".", 59 | "drwxrwxr-x 9 a279376 develop 4096 Mar 13 14:32 working") 60 | assert file.directory? 61 | assert !file.symlink? 62 | end 63 | 64 | def test_symlink 65 | file = Rake::FtpFile.new( 66 | ".", 67 | "lrwxrwxrwx 1 a279376 develop 64 Mar 26 2002 " + 68 | "xtrac -> /home/a279376/working/ics/development/java/" + 69 | "com/fmr/fwp/ics/xtrac") 70 | assert_equal 'xtrac', file.name 71 | assert file.symlink? 72 | assert !file.directory? 73 | end 74 | end 75 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rake::Contrib 2 | 3 | Additional libraries for Rake. 4 | 5 | This gem was extracted from Rake when v11.0 was released. 6 | 7 | ## Installation 8 | 9 | Add this line to your application's Gemfile: 10 | 11 | ```ruby 12 | gem 'rake-contrib' 13 | ``` 14 | 15 | And then execute: 16 | 17 | $ bundle 18 | 19 | Or install it yourself as: 20 | 21 | $ gem install rake-contrib 22 | 23 | ## Usage 24 | 25 | See also [rake-contrib API documentation at RubyDoc.info][rake-contrib-api] 26 | for full details. 27 | 28 | [rake-contrib-api]: http://www.rubydoc.info/gems/rake-contrib/ 29 | 30 | ### SshPublisher 31 | 32 | This gem comes with a simple SSH publishing class, to copy files to a remote 33 | host. 34 | 35 | In your Rakefile: 36 | 37 | ```ruby 38 | require 'rake/contrib/sshpublisher' 39 | ``` 40 | 41 | This gives your Rake tasks access to these utility classes around the Unix 42 | `scp` command: 43 | 44 | - `SshDirPublisher` - A publisher to `scp` all files in local directory to 45 | given remote directory on SSH host. 46 | - `SshFilePublisher` - A publisher to `scp` given files in local directory 47 | to given remote directory on SSH host. 48 | - `SshFreshDirPublisher` - Like `SshDirPublisher`, but removes and replaces 49 | given directory. 50 | - `CompositePublisher` - Manage several publishers as a single entity. 51 | 52 | ## Development 53 | 54 | After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. 55 | 56 | To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org). 57 | 58 | ## Contributing 59 | 60 | Bug reports and pull requests are welcome on GitHub at https://github.com/ruby/rake-contrib. 61 | 62 | ## License 63 | 64 | The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT). 65 | -------------------------------------------------------------------------------- /lib/rake/contrib/ftptools.rb: -------------------------------------------------------------------------------- 1 | # = Tools for FTP uploading. 2 | # 3 | # This file is still under development and is not released for general 4 | # use. 5 | 6 | require 'date' 7 | require 'net/ftp' 8 | require 'rake/file_list' 9 | 10 | module Rake # :nodoc: 11 | 12 | class FtpFile # :nodoc: all 13 | attr_reader :name, :size, :owner, :group, :time 14 | 15 | def self.date 16 | @date_class ||= Date 17 | end 18 | 19 | def self.time 20 | @time_class ||= Time 21 | end 22 | 23 | def initialize(path, entry) 24 | @path = path 25 | @mode, _, @owner, @group, size, d1, d2, d3, @name = entry.split(' ') 26 | @size = size.to_i 27 | @time = determine_time(d1, d2, d3) 28 | end 29 | 30 | def path 31 | File.join(@path, @name) 32 | end 33 | 34 | def directory? 35 | @mode[0] == ?d 36 | end 37 | 38 | def mode 39 | parse_mode(@mode) 40 | end 41 | 42 | def symlink? 43 | @mode[0] == ?l 44 | end 45 | 46 | private # -------------------------------------------------------- 47 | 48 | def parse_mode(m) 49 | result = 0 50 | (1..9).each do |i| 51 | result = 2 * result + ((m[i] == ?-) ? 0 : 1) 52 | end 53 | result 54 | end 55 | 56 | def determine_time(d1, d2, d3) 57 | now = self.class.time.now 58 | if /:/ !~ d3 59 | result = Time.parse("#{d1} #{d2} #{d3}") 60 | else 61 | result = Time.parse("#{d1} #{d2} #{now.year} #{d3}") 62 | result = Time.parse("#{d1} #{d2} #{now.year - 1} #{d3}") if 63 | result > now 64 | end 65 | result 66 | end 67 | end 68 | 69 | ## 70 | # Manage the uploading of files to an FTP account. 71 | class FtpUploader # :nodoc: 72 | 73 | # Log uploads to standard output when true. 74 | attr_accessor :verbose 75 | 76 | class << FtpUploader 77 | # Create an uploader and pass it to the given block as +up+. 78 | # When the block is complete, close the uploader. 79 | def connect(path, host, account, password) 80 | up = self.new(path, host, account, password) 81 | begin 82 | yield(up) 83 | ensure 84 | up.close 85 | end 86 | end 87 | end 88 | 89 | # Create an FTP uploader targeting the directory +path+ on +host+ 90 | # using the given account and password. +path+ will be the root 91 | # path of the uploader. 92 | def initialize(path, host, account, password) 93 | @created = Hash.new 94 | @path = path 95 | @ftp = Net::FTP.new(host, account, password) 96 | makedirs(@path) 97 | @ftp.chdir(@path) 98 | end 99 | 100 | # Create the directory +path+ in the uploader root path. 101 | def makedirs(path) 102 | route = [] 103 | File.split(path).each do |dir| 104 | route << dir 105 | current_dir = File.join(route) 106 | if @created[current_dir].nil? 107 | @created[current_dir] = true 108 | $stderr.puts "Creating Directory #{current_dir}" if @verbose 109 | @ftp.mkdir(current_dir) rescue nil 110 | end 111 | end 112 | end 113 | 114 | # Upload all files matching +wildcard+ to the uploader's root 115 | # path. 116 | def upload_files(wildcard) 117 | FileList.glob(wildcard).each do |fn| 118 | upload(fn) 119 | end 120 | end 121 | 122 | # Close the uploader. 123 | def close 124 | @ftp.close 125 | end 126 | 127 | private # -------------------------------------------------------- 128 | 129 | # Upload a single file to the uploader's root path. 130 | def upload(file) 131 | $stderr.puts "Uploading #{file}" if @verbose 132 | dir = File.dirname(file) 133 | makedirs(dir) 134 | @ftp.putbinaryfile(file, file) unless File.directory?(file) 135 | end 136 | end 137 | end 138 | --------------------------------------------------------------------------------