nanoc-sftp 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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: []
|