nanoc-sftp 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +4 -0
- data/.gitignore +4 -0
- data/ChangeLog +9 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +20 -0
- data/README.markdown +128 -0
- data/Rakefile +40 -0
- data/lib/nanoc/sftp/deployer.rb +125 -0
- data/lib/nanoc/sftp/file_sets.rb +44 -0
- data/lib/nanoc/sftp/file_util.rb +104 -0
- data/lib/nanoc/sftp/ui/highline.rb +54 -0
- data/lib/nanoc/sftp/ui/yad.rb +129 -0
- data/lib/nanoc/sftp/ui.rb +47 -0
- data/lib/nanoc/sftp/version.rb +8 -0
- data/lib/nanoc/sftp.rb +8 -0
- data/nanoc-sftp.gemspec +27 -0
- metadata +130 -0
data/.document
ADDED
data/.gitignore
ADDED
data/ChangeLog
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2012 Brent Sanders
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.markdown
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
nanoc-sftp
|
2
|
+
===========
|
3
|
+
|
4
|
+
A deploy script for [nanoc](http://nanoc.stoneship.org/),
|
5
|
+
that uses only SFTP to talk to the server.
|
6
|
+
|
7
|
+
**NOTE:** this is unfinished, and currently requires you to have
|
8
|
+
[yad](http://code.google.com/p/yad/) installed for some things
|
9
|
+
that will hopefully be made optional in the future.
|
10
|
+
|
11
|
+
|
12
|
+
Installation
|
13
|
+
------------
|
14
|
+
|
15
|
+
Add this line to your application's Gemfile:
|
16
|
+
|
17
|
+
```bash
|
18
|
+
gem 'nanoc-sftp'
|
19
|
+
```
|
20
|
+
|
21
|
+
And then run:
|
22
|
+
|
23
|
+
```bash
|
24
|
+
bundle
|
25
|
+
```
|
26
|
+
|
27
|
+
Or install it yourself with:
|
28
|
+
|
29
|
+
```bash
|
30
|
+
gem install nanoc-sftp
|
31
|
+
```
|
32
|
+
|
33
|
+
Usage
|
34
|
+
-----
|
35
|
+
|
36
|
+
This is still broken, due to what may be a bug in nanoc.
|
37
|
+
Currently, the only way to guarantee that this gem is loaded
|
38
|
+
soon enough is to patch one of the loaders for the `nanoc` tool
|
39
|
+
itself.
|
40
|
+
|
41
|
+
For example, if you add the above `gem 'nanoc-sftp'` line to your
|
42
|
+
`Gemfile`, run the gem bundler to install local binstubs:
|
43
|
+
|
44
|
+
```bash
|
45
|
+
gem install --binstubs
|
46
|
+
```
|
47
|
+
|
48
|
+
Then, patch the file `bin/nanoc` to include the line
|
49
|
+
'require 'nanoc/sftp'` **before** the main program is loaded. For
|
50
|
+
example, it should end up looking something like this:
|
51
|
+
|
52
|
+
```ruby
|
53
|
+
require 'pathname'
|
54
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
55
|
+
Pathname.new(__FILE__).realpath)
|
56
|
+
|
57
|
+
require 'rubygems'
|
58
|
+
require 'bundler/setup'
|
59
|
+
|
60
|
+
require 'nanoc'
|
61
|
+
require 'nanoc/sftp'
|
62
|
+
|
63
|
+
load Gem.bin_path('nanoc', 'nanoc')
|
64
|
+
```
|
65
|
+
|
66
|
+
Then, you will need to run nanoc through both this binstub loader AND
|
67
|
+
the bunder environment:
|
68
|
+
|
69
|
+
```bash
|
70
|
+
bundle exec ./bin/nanoc deploy --target staging
|
71
|
+
```
|
72
|
+
|
73
|
+
For the moment, I suggest setting up a simple alias in your shell
|
74
|
+
to wrap this up. This will create such an alias for you. (you may need
|
75
|
+
to modify it slightly if your aliases are not stored in `~/.bash_aliases`
|
76
|
+
|
77
|
+
```bash
|
78
|
+
echo 'alias nanoc_deploy_staging="bundle exec ./bin/nanoc deploy --target staging"' >> "${HOME}/.bash_aliases"
|
79
|
+
```
|
80
|
+
|
81
|
+
Hopefully, a better solution will happen soon-ish.
|
82
|
+
|
83
|
+
Configuration
|
84
|
+
-------------
|
85
|
+
|
86
|
+
The deployment settings are listed in your `config.yaml` file.
|
87
|
+
If you wanted to deploy to `myusername@server.example.com`, using
|
88
|
+
the non-standard SFTP port `1337`, into the remote directory
|
89
|
+
`/foo/bar`, it would look like this:
|
90
|
+
|
91
|
+
```yaml
|
92
|
+
deploy:
|
93
|
+
staging:
|
94
|
+
kind: sftp
|
95
|
+
user: "myusername"
|
96
|
+
host: "server.example.com"
|
97
|
+
port: 1337
|
98
|
+
path: "/foo/bar"
|
99
|
+
```
|
100
|
+
|
101
|
+
You can leave out the `port:` line to use the standard
|
102
|
+
SFTP port.
|
103
|
+
|
104
|
+
You will be prompted for the password each time the deploy
|
105
|
+
script is found, if the authentication isn't handled automagically
|
106
|
+
by a pre-shared key, etc.
|
107
|
+
|
108
|
+
Requirements
|
109
|
+
------------
|
110
|
+
|
111
|
+
Because of a personal need, this gem currently relies
|
112
|
+
on the utility `[yad](http://code.google.com/p/yad/)` which
|
113
|
+
implements a simple GUI.
|
114
|
+
|
115
|
+
This currently is used to provide a login prompt and a
|
116
|
+
verification step before overwriting files. I intend to make
|
117
|
+
this optional in the future, but right now it's required
|
118
|
+
that the `yad` binary be somewhere in your `$PATH`.
|
119
|
+
|
120
|
+
Copyright
|
121
|
+
---------
|
122
|
+
|
123
|
+
Copyright (c) 2012 Brent Sanders
|
124
|
+
|
125
|
+
See LICENSE.txt for details.
|
126
|
+
|
127
|
+
|
128
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
|
5
|
+
begin
|
6
|
+
require 'bundler'
|
7
|
+
rescue LoadError => e
|
8
|
+
warn e.message
|
9
|
+
warn "Run `gem install bundler` to install Bundler."
|
10
|
+
exit -1
|
11
|
+
end
|
12
|
+
|
13
|
+
begin
|
14
|
+
Bundler.setup(:development)
|
15
|
+
rescue Bundler::BundlerError => e
|
16
|
+
warn e.message
|
17
|
+
warn "Run `bundle install` to install missing gems."
|
18
|
+
exit e.status_code
|
19
|
+
end
|
20
|
+
|
21
|
+
require 'rake'
|
22
|
+
|
23
|
+
require 'rubygems/tasks'
|
24
|
+
class Gem::Tasks::Install
|
25
|
+
def install(path)
|
26
|
+
run 'gem', 'install', '-q', '--no-rdoc', '--no-ri', path
|
27
|
+
end
|
28
|
+
end
|
29
|
+
task :validate
|
30
|
+
|
31
|
+
Gem::Tasks.new(:scm => false)
|
32
|
+
|
33
|
+
#require 'rdoc/task'
|
34
|
+
#RDoc::Task.new do |rdoc|
|
35
|
+
# rdoc.title = "nanoc-sftp"
|
36
|
+
#end
|
37
|
+
#task :doc => :rdoc
|
38
|
+
|
39
|
+
|
40
|
+
task :default => :build
|
@@ -0,0 +1,125 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'open3'
|
4
|
+
require 'nanoc/sftp/file_sets'
|
5
|
+
require 'nanoc/sftp/file_util'
|
6
|
+
require 'nanoc/sftp/ui'
|
7
|
+
require 'nanoc/sftp/ui/highline'
|
8
|
+
require 'nanoc/sftp/ui/yad'
|
9
|
+
|
10
|
+
module Nanoc::Sftp
|
11
|
+
class Deployer < ::Nanoc::Extra::Deployer
|
12
|
+
include Nanoc::Sftp::FileSets
|
13
|
+
include Nanoc::Sftp::FileUtil
|
14
|
+
include Nanoc::Sftp::UI
|
15
|
+
include Nanoc::Sftp::UI::Highline
|
16
|
+
include Nanoc::Sftp::UI::Yad
|
17
|
+
|
18
|
+
REQUIRED_FIELDS = [:host, :user, :pass, :path]
|
19
|
+
OPTIONAL_FIELDS = [:port, :spinner]
|
20
|
+
FIELDS = REQUIRED_FIELDS + OPTIONAL_FIELDS
|
21
|
+
|
22
|
+
attr_accessor *FIELDS
|
23
|
+
|
24
|
+
SSL_TRANSPORT_OPTIONS = {
|
25
|
+
:compression => true
|
26
|
+
}
|
27
|
+
|
28
|
+
attr_reader :sftp, :srcdir, :topdir, :site
|
29
|
+
|
30
|
+
def blank?(x)
|
31
|
+
x = x.to_s
|
32
|
+
x.nil? || x.empty?
|
33
|
+
end
|
34
|
+
|
35
|
+
def initialize(*args)
|
36
|
+
super *args
|
37
|
+
|
38
|
+
FIELDS.each do |field|
|
39
|
+
instance_variable_set "@#{field}", config[field].to_s
|
40
|
+
end
|
41
|
+
|
42
|
+
@target = config[:target] || 'staging'
|
43
|
+
@target.upcase!
|
44
|
+
|
45
|
+
@spinner = :fancy if @spinner.nil? || @spinner == ''
|
46
|
+
@spinner = @spinner.to_sym if @spinner.is_a? String
|
47
|
+
|
48
|
+
@srcdir = File.expand_path(self.source_path)
|
49
|
+
@topdir = File.expand_path File.join(@srcdir, '/..')
|
50
|
+
|
51
|
+
Dir.chdir(@topdir) do
|
52
|
+
@site = Nanoc::Site.new('.')
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def run
|
57
|
+
require 'net/sftp'
|
58
|
+
|
59
|
+
stty_backup = `stty -g`
|
60
|
+
|
61
|
+
Signal.trap("INT") do
|
62
|
+
system "stty #{stty_backup}"
|
63
|
+
exit(1)
|
64
|
+
end
|
65
|
+
|
66
|
+
deploy!
|
67
|
+
|
68
|
+
ensure
|
69
|
+
system "stty #{stty_backup}"
|
70
|
+
end
|
71
|
+
|
72
|
+
def deploy!
|
73
|
+
with_sftp do
|
74
|
+
list_existing_files!
|
75
|
+
if verify_deploy_plan!
|
76
|
+
msg "Deploy plan verified - sending files to #{host} ..."
|
77
|
+
deploy_files!
|
78
|
+
msg "Finished!"
|
79
|
+
else
|
80
|
+
msg "*** CANCEL ***"
|
81
|
+
msg "Quitting; server remains unchanged!"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def list_existing_files!
|
87
|
+
list_recursive path do |entry|
|
88
|
+
existing_files.add entry
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def deploy_files!
|
93
|
+
msg "Sending *NEW* files..."
|
94
|
+
new_files.each do |file|
|
95
|
+
upload! file
|
96
|
+
end
|
97
|
+
|
98
|
+
msg "Sending *UPDATED* files..."
|
99
|
+
updated_files.each do |file|
|
100
|
+
upload! file
|
101
|
+
end
|
102
|
+
|
103
|
+
msg "Removing *STALE* files..."
|
104
|
+
stale_files.each do |file|
|
105
|
+
expunge! file
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def missing_required_login_fields?
|
110
|
+
REQUIRED_FIELDS.each do |field|
|
111
|
+
val = send(field).to_s
|
112
|
+
blank?(val) and return true
|
113
|
+
end
|
114
|
+
false
|
115
|
+
end
|
116
|
+
|
117
|
+
def login_options
|
118
|
+
SSL_TRANSPORT_OPTIONS.tap do |opt|
|
119
|
+
opt[:password] = @pass
|
120
|
+
opt[:port] = @port unless blank? @port
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Nanoc::Sftp
|
2
|
+
module FileSets
|
3
|
+
def compiled_files_array
|
4
|
+
site.items.map do |item|
|
5
|
+
item.reps.map do |rep|
|
6
|
+
rep.raw_path.sub /^#{self.source_path}\//, ''
|
7
|
+
end
|
8
|
+
end.flatten.compact.select do |f|
|
9
|
+
File.file? "#{self.source_path}/#{f}"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def compiled_files
|
14
|
+
@compiled_files ||= Set.new compiled_files_array
|
15
|
+
end
|
16
|
+
|
17
|
+
def existing_files
|
18
|
+
@existing_files ||= Set.new
|
19
|
+
end
|
20
|
+
|
21
|
+
def updated_files
|
22
|
+
@updated_files ||= compiled_files.intersection(existing_files)
|
23
|
+
end
|
24
|
+
|
25
|
+
def new_files
|
26
|
+
@new_files ||= compiled_files.difference(updated_files)
|
27
|
+
end
|
28
|
+
|
29
|
+
def stale_files
|
30
|
+
@stale_files ||= existing_files.difference(updated_files)
|
31
|
+
end
|
32
|
+
|
33
|
+
def full_changeset
|
34
|
+
[].tap do |list|
|
35
|
+
list.concat(updated_files.map{ |fn| [fn, :update] })
|
36
|
+
list.concat( new_files.map{ |fn| [fn, :new ] })
|
37
|
+
list.concat( stale_files.map{ |fn| [fn, :stale ] })
|
38
|
+
end.sort do |a,b|
|
39
|
+
a.first <=> b.first
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
@@ -0,0 +1,104 @@
|
|
1
|
+
module Nanoc::Sftp
|
2
|
+
module FileUtil
|
3
|
+
def with_sftp(&block)
|
4
|
+
ask_for_login_fields if missing_required_login_fields?
|
5
|
+
msg "openening a connection to #{user}@#{host}"
|
6
|
+
msg "option: #{login_options.inspect}"
|
7
|
+
retval = 1
|
8
|
+
Net::SFTP.start(host, @user, login_options) do |sftp|
|
9
|
+
@sftp = sftp
|
10
|
+
retval = block.call
|
11
|
+
@sftp = nil
|
12
|
+
end
|
13
|
+
retval
|
14
|
+
end
|
15
|
+
|
16
|
+
def simulate_upload!(file_path)
|
17
|
+
msg = "<dry_run - simulating upload> "
|
18
|
+
print msg
|
19
|
+
|
20
|
+
file_size = File.size(file_path)
|
21
|
+
file_mb = file_size.to_f / (2**20).to_f
|
22
|
+
min_delay = 0.15
|
23
|
+
per_mb = 0.8
|
24
|
+
delay = min_delay + (per_mb * file_mb)
|
25
|
+
msglen = 1 + msg.length + delay.to_i
|
26
|
+
msg_bs = "\b" * msglen
|
27
|
+
msg_sp = " " * msglen
|
28
|
+
msg_erase = "#{msg_bs}#{msg_sp}#{msg_bs}"
|
29
|
+
|
30
|
+
while delay > 1.0
|
31
|
+
delay -= 1.0
|
32
|
+
print "." ; spin 1.0
|
33
|
+
end
|
34
|
+
print "." ; spin delay
|
35
|
+
|
36
|
+
print msg_erase
|
37
|
+
end
|
38
|
+
|
39
|
+
def create_remote_directory!(path, dir)
|
40
|
+
@created_directories ||= {}
|
41
|
+
return if @created_directories[dir]
|
42
|
+
|
43
|
+
unless sftp.dir[path, dir].first == dir
|
44
|
+
fullpath = "#{path}/#{dir}"
|
45
|
+
puts "mkdir(#{fullpath.inspect})"
|
46
|
+
sftp.mkdir(fullpath)
|
47
|
+
end
|
48
|
+
@created_directories[dir] = true
|
49
|
+
end
|
50
|
+
|
51
|
+
def upload_file(local_path, remote_path)
|
52
|
+
puts "UPLOADING: #{local_path}"
|
53
|
+
puts " --> TO: #{remote_path}"
|
54
|
+
if self.dry_run?
|
55
|
+
simulate_upload!(local_path)
|
56
|
+
else
|
57
|
+
sftp.upload! local_path, remote_path
|
58
|
+
end
|
59
|
+
puts "ok"
|
60
|
+
end
|
61
|
+
|
62
|
+
def upload!(file)
|
63
|
+
dir = File.dirname(file)
|
64
|
+
dirlist = []
|
65
|
+
while dir != '.'
|
66
|
+
dirlist.push(dir)
|
67
|
+
dir = File.dirname(dir)
|
68
|
+
end
|
69
|
+
while dirlist.length > 0
|
70
|
+
create_remote_directory! path, dirlist.pop
|
71
|
+
end
|
72
|
+
upload_file "#{srcdir}/#{file}", "#{path}/#{file}"
|
73
|
+
end
|
74
|
+
|
75
|
+
def expunge!(file)
|
76
|
+
dst = "#{path}/#{file}"
|
77
|
+
puts "EXPUNGING: #{dst.inspect}"
|
78
|
+
sftp.remove!(dst)
|
79
|
+
end
|
80
|
+
|
81
|
+
def skip_entry?(entry)
|
82
|
+
entry.directory? ||
|
83
|
+
entry.name.start_with?('.')
|
84
|
+
end
|
85
|
+
|
86
|
+
def filtered_yield(enum)
|
87
|
+
enum.each do |entry|
|
88
|
+
yield entry.name unless skip_entry? entry
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def list(dir, &block)
|
93
|
+
filtered_yield sftp.dir.foreach(dir).to_enum, &block
|
94
|
+
end
|
95
|
+
|
96
|
+
def glob(*args, &block)
|
97
|
+
filtered_yield sftp.dir.glob(*args).to_enum, &block
|
98
|
+
end
|
99
|
+
|
100
|
+
def list_recursive(dir, &block)
|
101
|
+
glob dir, "**/*", &block
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Nanoc::Sftp::UI
|
2
|
+
module Highline
|
3
|
+
def load_highline_if_possible
|
4
|
+
require 'highline/import'
|
5
|
+
@have_hl = true
|
6
|
+
rescue LoadError
|
7
|
+
@have_hl = false
|
8
|
+
end
|
9
|
+
|
10
|
+
def have_highline?
|
11
|
+
load_highline_if_possible unless defined? @have_hl
|
12
|
+
puts "Have highline: #{@have_hl}"
|
13
|
+
@have_hl
|
14
|
+
end
|
15
|
+
|
16
|
+
def ask_for_login_fields_with_highline
|
17
|
+
@host = ask("What is the <%= color('hostname', BOLD) %> " +
|
18
|
+
"of the SFTP server?\n"
|
19
|
+
) { |q|
|
20
|
+
q.default = @host
|
21
|
+
q.validate = /^(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])$/
|
22
|
+
|
23
|
+
q.whitespace = :remove
|
24
|
+
}
|
25
|
+
|
26
|
+
@post = ask("What <%= color('Port') %> is the SFTP server using? ",
|
27
|
+
"(leave it blank to use the standard port, 22/tcp)"
|
28
|
+
) { |q|
|
29
|
+
q.default = @port.to_s
|
30
|
+
q.whitespace = :remove
|
31
|
+
q.validate = /^\d{2,5}$/
|
32
|
+
}
|
33
|
+
|
34
|
+
@user = ask("What <%= color('username') %> to login as? "
|
35
|
+
) { |q|
|
36
|
+
q.default = @user
|
37
|
+
q.whitespace = :strip
|
38
|
+
q.validate = /^\w+$/
|
39
|
+
}
|
40
|
+
|
41
|
+
@pass = ask("Please enter your <%= color('password', BOLD) %> now,\n" +
|
42
|
+
"to login with SFTP \"<%= color('#{@user}', BOLD) %>" +
|
43
|
+
"@<%= color('#{@host}', BOLD) %>:<%= color('#{@port}', BOLD) %>\":"
|
44
|
+
) { |q|
|
45
|
+
q.echo = false
|
46
|
+
q.whitespace = nil
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
def verify_with_highline
|
51
|
+
raise "FIXME"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
require 'pp'
|
2
|
+
|
3
|
+
module Nanoc::Sftp::UI
|
4
|
+
module Yad
|
5
|
+
def have_yad?
|
6
|
+
if @yad.nil? ; then
|
7
|
+
ENV['PATH'].split(/:/).each do |dir|
|
8
|
+
file = "#{dir}/yad"
|
9
|
+
if File.executable?(file)
|
10
|
+
@yad = file
|
11
|
+
return @yad
|
12
|
+
end
|
13
|
+
end
|
14
|
+
false
|
15
|
+
else
|
16
|
+
@yad
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def verify_deploy_plan_yad!
|
21
|
+
text = "<span size='x-large'>Deployment Plan</span>\n<span>Sould the following changes be made on the server?</span>"
|
22
|
+
args = [
|
23
|
+
"--list",
|
24
|
+
"--column=CHK:CHK",
|
25
|
+
"--column=@back@",
|
26
|
+
"--column=IMG:IMG",
|
27
|
+
"--column=Status:TEXT",
|
28
|
+
"--column=TIP:TIP",
|
29
|
+
"--column=File:TEXT",
|
30
|
+
"--hide-column=5",
|
31
|
+
"--search-column=6",
|
32
|
+
"--checklist",
|
33
|
+
"--print-all",
|
34
|
+
"--title=Deployment Plan",
|
35
|
+
"--image=upload-media",
|
36
|
+
"--window-icon=upload-media",
|
37
|
+
# "--no-headers",
|
38
|
+
"--width=600",
|
39
|
+
"--height=400",
|
40
|
+
"--image-on-top",
|
41
|
+
"--text=#{text}",
|
42
|
+
"--buttons-layout=spread",
|
43
|
+
"--button=gtk-execute:0",
|
44
|
+
"--button=gtk-cancel:1",
|
45
|
+
"--borders=3"
|
46
|
+
]
|
47
|
+
cmd = args.map { |x| "\"#{x}\"" }.join(' ')
|
48
|
+
cmd = "#{@yad} #{cmd}"
|
49
|
+
|
50
|
+
Open3.popen3(cmd) do |stdin,stdout,stderr,wait_thr|
|
51
|
+
stdin.write(full_changeset.map do |x|
|
52
|
+
fn, status = *x
|
53
|
+
case status
|
54
|
+
when :new then ['TRUE', '#CCFFD1', 'gtk-new', 'NEW', "creates: #{path}/#{fn}"]
|
55
|
+
when :update then ['TRUE', '#FFD0D2', 'gtk-save-as', 'UPDATE', "updates: #{path}/#{fn} with #{srcdir}/#{fn}"]
|
56
|
+
when :stale then ['FALSE', '#FFF899', 'gtk-delete', 'STALE', "removes: #{path}/#{fn}"]
|
57
|
+
else raise "unknown status: #{status}"
|
58
|
+
end.concat(["#{fn}"])
|
59
|
+
end.flatten.join("\n"))
|
60
|
+
stdin.flush
|
61
|
+
stdin.close
|
62
|
+
@plan = stdout.readlines.map do |line|
|
63
|
+
Hash[ [:ok, :bgcolor, :status, :tooltip, :file].zip line.split(/\|/) ]
|
64
|
+
end
|
65
|
+
@success = wait_thr.value.success?
|
66
|
+
end
|
67
|
+
|
68
|
+
if @success
|
69
|
+
@plan.each do |x|
|
70
|
+
if x[:ok] == 'FALSE'
|
71
|
+
case x[:status]
|
72
|
+
when 'NEW' then new_files
|
73
|
+
when 'UPDATE' then updated_files
|
74
|
+
when 'STALE' then stale_files
|
75
|
+
else raise "bad status value: #{x[:status]}"
|
76
|
+
end.delete x[:file]
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
def ask_for_login_fields_with_yad
|
84
|
+
dialog_text = [
|
85
|
+
'<span size="large">',
|
86
|
+
'Deploying to the ',
|
87
|
+
'<span font-weight="bold" foreground="#c24545">',
|
88
|
+
@target,
|
89
|
+
'</span> server</span>'
|
90
|
+
].join('')
|
91
|
+
|
92
|
+
args = [
|
93
|
+
"--form",
|
94
|
+
"--buttons-layout=edge",
|
95
|
+
"--button=gtk-apply:0",
|
96
|
+
"--button=gtk-cancel:1",
|
97
|
+
"--on-load",
|
98
|
+
"--borders=5",
|
99
|
+
"--text=#{dialog_text}",
|
100
|
+
"--title=Deploying website to the #{@target} server",
|
101
|
+
"--image=upload-media",
|
102
|
+
"--window-icon=upload-media",
|
103
|
+
"--align=right",
|
104
|
+
"--columns=2",
|
105
|
+
"--separator=|",
|
106
|
+
"--field=Hostname",
|
107
|
+
"--field=Port:NUM",
|
108
|
+
"--field=Username",
|
109
|
+
"--field=Password:H",
|
110
|
+
"thoughtnoise.net",
|
111
|
+
"2501!22..65535!1!0",
|
112
|
+
"telleena",
|
113
|
+
"" # empty pass
|
114
|
+
]
|
115
|
+
cmd = args.map { |x| "'#{x}'" }.join(' ')
|
116
|
+
cmd = "#{@yad} #{cmd}"
|
117
|
+
|
118
|
+
output = IO.popen(cmd, 'r') do |prog|
|
119
|
+
prog.read.split(/\|/)
|
120
|
+
end
|
121
|
+
ret = $?.to_i
|
122
|
+
|
123
|
+
@host = output[0]
|
124
|
+
@port = output[1].to_i
|
125
|
+
@user = output[2]
|
126
|
+
@pass = output[3]
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Nanoc::Sftp
|
4
|
+
module UI
|
5
|
+
SPIN = {
|
6
|
+
:basic => "\\|/-",
|
7
|
+
# :fancy => "▟▄▙▌▛▀▜▐"
|
8
|
+
:fancy => "\u259F\u2584\u2599\u258C\u259B\u2580\u259C\u2590"
|
9
|
+
}
|
10
|
+
|
11
|
+
def spin(n, style = spinner)
|
12
|
+
@spinval ||= 0
|
13
|
+
print '* '
|
14
|
+
while n > 0.1
|
15
|
+
@spinval += 1
|
16
|
+
@spinval = 0 if @spinval >= SPIN[style].length
|
17
|
+
c = SPIN[style][@spinval,1]
|
18
|
+
print "\b\b#{c} "
|
19
|
+
sleep 0.1
|
20
|
+
n -= 0.1
|
21
|
+
end
|
22
|
+
print "\b\b \b\b"
|
23
|
+
sleep n
|
24
|
+
end
|
25
|
+
|
26
|
+
def msg(*args)
|
27
|
+
msg = args.flatten.join(' ')
|
28
|
+
puts "[nanoc-sftp] #{msg}"
|
29
|
+
end
|
30
|
+
|
31
|
+
def ask_for_login_fields
|
32
|
+
if have_yad?
|
33
|
+
ask_for_login_fields_with_yad
|
34
|
+
else
|
35
|
+
raise "no yad?"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def verify_deploy_plan!
|
40
|
+
if have_yad?
|
41
|
+
return verify_deploy_plan_yad!
|
42
|
+
else
|
43
|
+
raise "no yad?"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/lib/nanoc/sftp.rb
ADDED
data/nanoc-sftp.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require File.expand_path('../lib/nanoc/sftp/version', __FILE__)
|
4
|
+
|
5
|
+
Gem::Specification.new do |gem|
|
6
|
+
gem.name = "nanoc-sftp"
|
7
|
+
gem.version = Nanoc::Sftp::VERSION
|
8
|
+
gem.summary = %q{Nanoc deploy plugin For places where a full SSH login is disabled, but simple chroot-jail SFTP is allowed. The stock rsync method is recommended over this, if possible.}
|
9
|
+
gem.description = %q{An SFTP-only deploy script for nanoc}
|
10
|
+
gem.license = "MIT"
|
11
|
+
gem.authors = ["Brent Sanders"]
|
12
|
+
gem.email = "git@thoughtnoise.net"
|
13
|
+
gem.homepage = "http://github.dom/pdkl95/nanoc-sftp"
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ['lib']
|
19
|
+
|
20
|
+
gem.add_dependency "net-sftp", "~> 2.0"
|
21
|
+
#gem.add_dependency "highline", "~> 1.6"
|
22
|
+
|
23
|
+
gem.add_development_dependency 'bundler', '~> 1.0'
|
24
|
+
gem.add_development_dependency 'rake', '~> 0.8'
|
25
|
+
#gem.add_development_dependency 'rdoc', '~> 3.0'
|
26
|
+
gem.add_development_dependency 'rubygems-tasks', '~> 0.2'
|
27
|
+
end
|
metadata
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: nanoc-sftp
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Brent Sanders
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-09-21 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: net-sftp
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '2.0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '2.0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: bundler
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '1.0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '1.0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rake
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0.8'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.8'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: rubygems-tasks
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0.2'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0.2'
|
78
|
+
description: An SFTP-only deploy script for nanoc
|
79
|
+
email: git@thoughtnoise.net
|
80
|
+
executables: []
|
81
|
+
extensions: []
|
82
|
+
extra_rdoc_files: []
|
83
|
+
files:
|
84
|
+
- .document
|
85
|
+
- .gitignore
|
86
|
+
- ChangeLog
|
87
|
+
- Gemfile
|
88
|
+
- LICENSE.txt
|
89
|
+
- README.markdown
|
90
|
+
- Rakefile
|
91
|
+
- lib/nanoc/sftp.rb
|
92
|
+
- lib/nanoc/sftp/deployer.rb
|
93
|
+
- lib/nanoc/sftp/file_sets.rb
|
94
|
+
- lib/nanoc/sftp/file_util.rb
|
95
|
+
- lib/nanoc/sftp/ui.rb
|
96
|
+
- lib/nanoc/sftp/ui/highline.rb
|
97
|
+
- lib/nanoc/sftp/ui/yad.rb
|
98
|
+
- lib/nanoc/sftp/version.rb
|
99
|
+
- nanoc-sftp.gemspec
|
100
|
+
homepage: http://github.dom/pdkl95/nanoc-sftp
|
101
|
+
licenses:
|
102
|
+
- MIT
|
103
|
+
post_install_message:
|
104
|
+
rdoc_options: []
|
105
|
+
require_paths:
|
106
|
+
- lib
|
107
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
108
|
+
none: false
|
109
|
+
requirements:
|
110
|
+
- - ! '>='
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
version: '0'
|
113
|
+
segments:
|
114
|
+
- 0
|
115
|
+
hash: 1725721838413355219
|
116
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
117
|
+
none: false
|
118
|
+
requirements:
|
119
|
+
- - ! '>='
|
120
|
+
- !ruby/object:Gem::Version
|
121
|
+
version: '0'
|
122
|
+
requirements: []
|
123
|
+
rubyforge_project:
|
124
|
+
rubygems_version: 1.8.24
|
125
|
+
signing_key:
|
126
|
+
specification_version: 3
|
127
|
+
summary: Nanoc deploy plugin For places where a full SSH login is disabled, but simple
|
128
|
+
chroot-jail SFTP is allowed. The stock rsync method is recommended over this, if
|
129
|
+
possible.
|
130
|
+
test_files: []
|