octopress-deploy 1.0.0.alpha.2 → 1.0.0.alpha.3
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.
- checksums.yaml +4 -4
- data/.gitignore +10 -0
- data/History.markdown +13 -0
- data/README.md +50 -13
- data/lib/octopress-deploy.rb +97 -29
- data/lib/octopress-deploy/commands.rb +26 -0
- data/lib/octopress-deploy/git.rb +61 -44
- data/lib/octopress-deploy/rsync.rb +42 -33
- data/lib/octopress-deploy/s3.rb +177 -0
- data/lib/octopress-deploy/version.rb +1 -1
- data/octopress-deploy.gemspec +1 -0
- data/test/.gitignore +1 -0
- data/test/Gemfile +3 -4
- data/test/_deploy_rsync.yml +6 -0
- data/test/_deploy_rsync_local.yml +6 -0
- data/test/source/.hidden +1 -0
- data/test/source/dir/file +1 -0
- data/test/source/test_file +1 -0
- data/test/test.rb +154 -15
- metadata +31 -8
- data/test/_deploy.yml +0 -4
- data/test/_site/foo +0 -1
- data/test/_site/test +0 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5e4a09550ef9b0543cffbf51047b99ef82efa4c9
|
4
|
+
data.tar.gz: 8bebda9735365b81fc5bd5feb7171bd91ffec4fe
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 868c3859731c91f5cfefeb0507564d2c9bfda7d6c51209901aa8f6cffa7cd4099aa9a5c79074090deb395b3aad587f98a3cb92f03c62469e0184af393f5453ff
|
7
|
+
data.tar.gz: a887ef627c3c0f4d8e90d8594670b4bb32686a4406192b5ca19a423aede8840539912a1845c487ec6a3713592d5f84fc0e5383674be3042624737872a49294d3
|
data/.gitignore
CHANGED
data/History.markdown
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
## HEAD
|
2
|
+
|
3
|
+
### Major Enhancements
|
4
|
+
|
5
|
+
### Minor Enhancements
|
6
|
+
|
7
|
+
* Ask user if they would like to ignore the `_deploy.yml` file & automate (#7)
|
8
|
+
|
9
|
+
### Bug Fixes
|
10
|
+
|
11
|
+
* Symbolize all incoming keys to `Octopress::Deploy.init_config` (#4)
|
12
|
+
|
13
|
+
### Development Fixes
|
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
Deployment tools for Octopress and Jekyll blogs (or really any static site).
|
4
4
|
|
5
|
-
Currently this supports deploying through Git and Rsync. Requests for other
|
5
|
+
Currently this supports deploying through S3, Git and Rsync. Requests for other
|
6
6
|
deployment methods are welcome.
|
7
7
|
|
8
8
|
## Installation
|
@@ -23,46 +23,83 @@ Or install it yourself as:
|
|
23
23
|
|
24
24
|
To deploy your site run:
|
25
25
|
|
26
|
-
```
|
27
|
-
|
26
|
+
```bash
|
27
|
+
$ octopress deploy
|
28
28
|
```
|
29
29
|
|
30
30
|
This will read from your configuration file `_deploy.yml` and deploy your site. If your site has no configuration file, you will be asked if you want to generate one and what deployment method you want to use.
|
31
31
|
|
32
|
-
You can also generate a configuration by running:
|
32
|
+
You can also generate a `./_deploy.yml` configuration file by running:
|
33
33
|
|
34
|
-
```
|
35
|
-
|
34
|
+
```bash
|
35
|
+
$ octopress deploy --init git # or 'rsync' or 's3'
|
36
36
|
```
|
37
37
|
|
38
|
-
This
|
38
|
+
Once you've deployed your site, you can also pull it back down into a local directory. This is mostly useful for checking the results of a deploy. This will create the directory if it doesn't already exist.
|
39
|
+
|
40
|
+
```ruby
|
41
|
+
Octopress::Deploy.pull('some_directory')
|
42
|
+
```
|
39
43
|
|
40
44
|
### Configuration options
|
41
45
|
|
42
46
|
Configurations should be added to a `_deploy.yml` file in your project's root directory. You can pass options as a hash directy to the `push` method as well. Passed options will override options set in the config file.
|
43
47
|
|
44
|
-
|
|
48
|
+
| Config | Description | Default
|
45
49
|
|:--------------|:-------------------------------------------------|:---------------|
|
46
50
|
| `config_file` | Path to the config file. | _config.yml |
|
47
51
|
| `site_dir` | Path to comipled site files. | _site |
|
48
52
|
|
49
53
|
|
50
|
-
|
54
|
+
#### Amazon S3
|
55
|
+
|
56
|
+
Important: when using S3, you must add your _deploy.yml to your .gitignore to prevent accidentally sharing
|
57
|
+
account access information. Octopress Deploy will offer to do it for you. If you don't, you won't be able to deploy.`
|
58
|
+
|
59
|
+
| Config | Description | Default
|
60
|
+
|:--------------------|:-----------------------------------------|:-------------|
|
61
|
+
| `bucket_name` | S3 bucket name | |
|
62
|
+
| `access_key_id` | AWS access key | |
|
63
|
+
| `secret_access_key` | AWS secret key | |
|
64
|
+
| `remote_path` | Directory files should be synced to. | / |
|
65
|
+
| `delete` | Delete files to create a 1:1 file sync. | false |
|
66
|
+
| `verbose` | Display all file actions during deploy. | true |
|
67
|
+
| `region` | Region for your AWS bucket | us-east-1 |
|
68
|
+
|
69
|
+
If you choose a bucket which doesn't yet exist, Octopress Deploy will offer to create it for you, and offer to configure it as a static website.
|
70
|
+
|
71
|
+
##### ENV config
|
72
|
+
|
73
|
+
For the following configurations you can set environment vars instead of adding items to your config file.
|
74
|
+
|
75
|
+
| Config | ENV var |
|
76
|
+
|:--------------------|:-------------------------------|
|
77
|
+
| `access_key_id` | AWS_ACCESS_KEY_ID |
|
78
|
+
| `secret_access_key` | AWS_SECRET_ACCESS_KEY |
|
79
|
+
| `region` | AWS_DEFAULT_REGIONS |
|
80
|
+
|
81
|
+
|
82
|
+
##### Deleting files from S3
|
83
|
+
|
84
|
+
If the `delete` option is true, files in the `remote_path` on the bucket will be removed if they do not match local site files.
|
85
|
+
If `remote_path` is a subdirectory, only files in that subdirectory will be evaluated for deletion.
|
86
|
+
|
87
|
+
#### Git
|
51
88
|
|
52
|
-
Only git_url is required. Other options will default as shown below.
|
89
|
+
Only `git_url` is required. Other options will default as shown below.
|
53
90
|
|
54
|
-
|
|
91
|
+
| Config | Description | Default
|
55
92
|
|:--------------|:-------------------------------------------------|:---------------|
|
56
93
|
| `git_url` | Url for remote git repository. | |
|
57
94
|
| `git_branch` | Deployment branch for git repository. | master |
|
58
95
|
| `deploy_dir` | Directory where deployment files are staged. | .deploy |
|
59
96
|
| `remote` | Name of git remote. | deploy |
|
60
97
|
|
61
|
-
|
98
|
+
#### Rsync
|
62
99
|
|
63
100
|
Only `remote_path` is required. If `user` is not present, Rsync will sync between two locally available directories. Do this if your site root is mounted locally.
|
64
101
|
|
65
|
-
|
|
102
|
+
| Config | Description | Default
|
66
103
|
|:---------------|:--------------------------------------------------|:---------------|
|
67
104
|
| `user` | ssh user, e.g user@host.com | |
|
68
105
|
| `port` | ssh port | 22 |
|
data/lib/octopress-deploy.rb
CHANGED
@@ -1,18 +1,26 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
1
|
+
$LOAD_PATH.unshift File.expand_path("../", __FILE__)
|
2
|
+
|
3
|
+
require 'octopress-deploy/version'
|
4
|
+
require 'octopress-deploy/core_ext'
|
4
5
|
require 'colorator'
|
5
|
-
require '
|
6
|
+
require 'yaml'
|
7
|
+
require 'pathname'
|
8
|
+
|
9
|
+
if defined? Octopress::Command
|
10
|
+
require 'octopress-deploy/commands'
|
11
|
+
end
|
6
12
|
|
7
13
|
|
8
14
|
module Octopress
|
9
15
|
module Deploy
|
10
16
|
autoload :Git, 'octopress-deploy/git'
|
11
17
|
autoload :Rsync, 'octopress-deploy/rsync'
|
18
|
+
autoload :S3, 'octopress-deploy/s3'
|
12
19
|
|
13
20
|
METHODS = {
|
14
21
|
'git'=> Git,
|
15
|
-
'rsync'=> Rsync
|
22
|
+
'rsync'=> Rsync,
|
23
|
+
's3'=> S3
|
16
24
|
}
|
17
25
|
|
18
26
|
def self.push(options={})
|
@@ -25,6 +33,19 @@ module Octopress
|
|
25
33
|
end
|
26
34
|
end
|
27
35
|
|
36
|
+
def self.pull(dir='site-pull', options={})
|
37
|
+
init_options(options)
|
38
|
+
if !File.exists? @options[:config_file]
|
39
|
+
init_config if ask_bool("Deployment config file not found. Create #{@options[:config_file]}?")
|
40
|
+
else
|
41
|
+
parse_options
|
42
|
+
if !File.exists? @options[:pull_dir] = dir
|
43
|
+
FileUtils.mkdir_p @options[:pull_dir]
|
44
|
+
end
|
45
|
+
deploy_method.new(@options).pull()
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
28
49
|
def self.parse_options
|
29
50
|
config = YAML.load(File.open(@options[:config_file])).to_symbol_keys
|
30
51
|
@options = @options.to_symbol_keys
|
@@ -32,7 +53,7 @@ module Octopress
|
|
32
53
|
end
|
33
54
|
|
34
55
|
def self.init_options(options={})
|
35
|
-
@options
|
56
|
+
@options = options.to_symbol_keys
|
36
57
|
@options[:config_file] ||= '_deploy.yml'
|
37
58
|
@options[:site_dir] ||= site_dir
|
38
59
|
end
|
@@ -51,53 +72,100 @@ module Octopress
|
|
51
72
|
|
52
73
|
# Create a config file
|
53
74
|
#
|
54
|
-
def self.init_config(method=nil, options=
|
55
|
-
|
56
|
-
|
57
|
-
|
75
|
+
def self.init_config(method=nil, options=nil)
|
76
|
+
if options
|
77
|
+
options[:method] = method
|
78
|
+
init_options(options)
|
79
|
+
end
|
80
|
+
|
58
81
|
unless @options[:method]
|
59
82
|
@options[:method] = ask("How would you like to deploy your site?", METHODS.keys)
|
60
83
|
end
|
61
|
-
config = <<-FILE
|
62
|
-
method: #{@options[:method]}
|
63
|
-
site_dir: #{@options[:site_dir]}
|
64
|
-
FILE
|
65
|
-
config += deploy_method.default_config(options)
|
66
84
|
|
67
|
-
|
68
|
-
|
69
|
-
|
85
|
+
write_config
|
86
|
+
check_gitignore
|
87
|
+
end
|
88
|
+
|
89
|
+
def self.write_config
|
90
|
+
if !@options[:force_write_config]
|
91
|
+
if File.exist?(@options[:config_file]) &&
|
92
|
+
!ask_bool("A config file already exists at #{@options[:config_file]}. Overwrite?")
|
93
|
+
return puts "No config file written."
|
70
94
|
end
|
71
95
|
end
|
96
|
+
|
97
|
+
config = get_config.strip
|
72
98
|
File.open(@options[:config_file], 'w') { |f| f.write(config) }
|
73
99
|
puts "File #{@options[:config_file]} created.".green
|
74
100
|
puts "------------------"
|
75
|
-
puts "#{config.yellow}
|
101
|
+
puts "#{config.yellow}"
|
102
|
+
puts "------------------"
|
76
103
|
puts "Please add your configurations to this file."
|
77
104
|
end
|
78
105
|
|
106
|
+
def self.get_config
|
107
|
+
<<-FILE
|
108
|
+
method: #{@options[:method]}
|
109
|
+
site_dir: #{@options[:site_dir]}
|
110
|
+
#{deploy_method.default_config(@options)}
|
111
|
+
FILE
|
112
|
+
end
|
113
|
+
|
114
|
+
def self.check_gitignore
|
115
|
+
gitignore = File.join(`git rev-parse --show-toplevel`.strip, ".gitignore")
|
116
|
+
if !File.exist?(gitignore) ||
|
117
|
+
Pathname.new(gitignore).read.match(/^#{@options[:config_file]}/i).nil?
|
118
|
+
if ask_bool("Do you want to add #{@options[:config_file]} to your .gitignore?")
|
119
|
+
git_ignore_config_file gitignore
|
120
|
+
return true
|
121
|
+
end
|
122
|
+
else
|
123
|
+
return true
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def self.git_ignore_config_file(gitignore)
|
128
|
+
File.open(gitignore, 'a') { |f| f.write(@options[:config_file]) }
|
129
|
+
end
|
130
|
+
|
79
131
|
def self.ask_bool(message)
|
80
|
-
|
132
|
+
ask_or_default(true, message) do
|
133
|
+
ask(message, ['y','n']) == 'y'
|
134
|
+
end
|
81
135
|
end
|
82
136
|
|
83
137
|
def self.ask(message, valid_options)
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
138
|
+
ask_or_default(false, message) do
|
139
|
+
if valid_options
|
140
|
+
options = valid_options.join '/'
|
141
|
+
answer = get_stdin("#{message} [#{options}]: ").downcase.strip
|
142
|
+
if valid_options.map{|o| o.downcase}.include?(answer)
|
143
|
+
return answer
|
144
|
+
else
|
145
|
+
return false
|
146
|
+
end
|
89
147
|
else
|
90
|
-
|
148
|
+
answer = get_stdin("#{message}: ")
|
91
149
|
end
|
92
|
-
|
93
|
-
answer = get_stdin("#{message}: ")
|
150
|
+
answer
|
94
151
|
end
|
95
|
-
answer
|
96
152
|
end
|
97
153
|
|
98
154
|
def self.get_stdin(message)
|
99
155
|
print message
|
100
156
|
STDIN.gets.chomp
|
101
157
|
end
|
158
|
+
|
159
|
+
def self.should_ask?
|
160
|
+
!ENV['NO_ASK']
|
161
|
+
end
|
162
|
+
|
163
|
+
def self.ask_or_default(default, message)
|
164
|
+
if should_ask?
|
165
|
+
yield
|
166
|
+
else
|
167
|
+
puts "Assuming '#{default}' for '#{message}'."
|
168
|
+
end
|
169
|
+
end
|
102
170
|
end
|
103
171
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Octopress
|
2
|
+
module Deploy
|
3
|
+
class Commands < Octopress::Command
|
4
|
+
def self.init_with_program(p)
|
5
|
+
p.command(:deploy) do |c|
|
6
|
+
c.syntax "octopress deploy [options]"
|
7
|
+
c.description "Deploy your Octopress site."
|
8
|
+
c.option "using", "--using METHOD", "Define the push method to use, overriding your configuration file's setting"
|
9
|
+
c.option "config_file", "--config FILE", "The path to your config file (default: _deploy.yml)"
|
10
|
+
c.option "init", "--init METHOD", "Initialize a config file with the options for the given method."
|
11
|
+
c.option "pull", "--pull DIRECTORY", "Pull down the published copy of your site into a directory (default: ./site-pull)"
|
12
|
+
|
13
|
+
c.action do |_, options|
|
14
|
+
if options["init"] and options["init"].is_a?(String)
|
15
|
+
Octopress::Deploy.init_config(options["init"], options)
|
16
|
+
elsif options["pull"] and options["pull"].is_a?(String)
|
17
|
+
Octopress::Deploy.pull(options["pull"], options)
|
18
|
+
else
|
19
|
+
Octopress::Deploy.push(options)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/octopress-deploy/git.rb
CHANGED
@@ -3,89 +3,106 @@ module Octopress
|
|
3
3
|
class Git
|
4
4
|
|
5
5
|
def initialize(options={})
|
6
|
-
@options
|
7
|
-
@repo
|
8
|
-
@branch
|
9
|
-
@
|
10
|
-
@
|
11
|
-
@
|
12
|
-
|
6
|
+
@options = options
|
7
|
+
@repo = @options[:git_url]
|
8
|
+
@branch = @options[:git_branch]
|
9
|
+
@remote = @options[:remote] || 'deploy'
|
10
|
+
@remote_path = @options[:remote_path] || ''
|
11
|
+
@remote_path = @remote_path.sub(/^\//,'') #remove leading slash
|
12
|
+
@site_dir = File.expand_path(@options[:site_dir])
|
13
|
+
@deploy_dir = File.expand_path(@options[:deploy_dir] || '.deploy')
|
14
|
+
@pull_dir = @options[:pull_dir]
|
15
|
+
abort "Deploy Failed: Configure a git_url in #{@options[:config_file]} before deploying.".red if @repo.nil?
|
13
16
|
end
|
14
17
|
|
15
18
|
# Initialize, pull, copy and deploy.
|
16
|
-
# This is the method you're looking for.
|
17
19
|
#
|
18
20
|
def push
|
19
|
-
init_repo
|
20
|
-
|
21
|
-
|
22
|
-
|
21
|
+
init_repo
|
22
|
+
puts "Syncing #{@site_dir.sub(`pwd`.strip+'/', '')} files to #{@repo}."
|
23
|
+
FileUtils.cd @deploy_dir do
|
24
|
+
git_pull
|
25
|
+
clean_deploy
|
26
|
+
copy_site
|
27
|
+
git_push
|
28
|
+
end
|
23
29
|
end
|
24
30
|
|
25
|
-
|
31
|
+
def pull
|
32
|
+
`git clone -b #{@branch} #{@repo} #{@pull_dir}`
|
33
|
+
end
|
34
|
+
|
35
|
+
# Check to see if local deployment dir is configured to deploy.
|
26
36
|
#
|
27
37
|
def check_repo
|
28
|
-
|
29
|
-
|
30
|
-
|
38
|
+
if Dir.exist? @deploy_dir
|
39
|
+
FileUtils.cd @deploy_dir do
|
40
|
+
return `git remote -v`.include? @repo
|
41
|
+
end
|
31
42
|
end
|
32
43
|
end
|
33
44
|
|
34
45
|
def self.default_config(options={})
|
35
|
-
|
46
|
+
<<-CONFIG
|
36
47
|
git_url: #{options[:git_url]}
|
37
48
|
git_branch: #{options[:git_branch] || 'master'}
|
38
49
|
CONFIG
|
39
50
|
end
|
40
51
|
|
41
|
-
# If necessary create deploy directory and initialize it with deployment remote
|
52
|
+
# If necessary create deploy directory and initialize it with deployment remote.
|
42
53
|
#
|
43
54
|
def init_repo
|
55
|
+
return if check_repo
|
44
56
|
FileUtils.mkdir_p @deploy_dir
|
45
57
|
FileUtils.cd @deploy_dir do
|
46
58
|
if Dir[@deploy_dir+'/*'].empty?
|
47
59
|
|
48
|
-
#
|
60
|
+
# initialize the repository and add the remote.
|
61
|
+
#
|
62
|
+
`git init; git remote add #{@remote} #{@repo}`
|
63
|
+
|
64
|
+
# Attempt to pull from the remote.
|
49
65
|
#
|
50
|
-
|
51
|
-
|
52
|
-
Open3.popen3(cmd) do |stdin, stdout, stderr, wait_thr|
|
53
|
-
exit_status = wait_thr.value
|
66
|
+
if git_pull
|
67
|
+
`git branch -m #{@branch}`
|
54
68
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
`git add .; git commit -m 'initial commit'`
|
62
|
-
`git branch -m #{@branch}`
|
63
|
-
`git rm _; git add -u; git commit -m 'cleanup'`
|
64
|
-
end
|
69
|
+
# If no branch exists on remote, create one locally.
|
70
|
+
else
|
71
|
+
`echo "initialize deploy repo" > _`
|
72
|
+
`git add .; git commit -m 'initial commit'`
|
73
|
+
`git branch -m #{@branch}`
|
74
|
+
`git rm _; git add -u; git commit -m 'cleanup'`
|
65
75
|
end
|
66
76
|
end
|
67
77
|
end
|
68
78
|
end
|
69
79
|
|
70
80
|
def git_push
|
71
|
-
|
72
|
-
`git push #{@remote} #{@branch}`
|
73
|
-
end
|
81
|
+
`git push #{@remote} #{@branch}`
|
74
82
|
end
|
75
83
|
|
84
|
+
# Attempt to pull from the remote branch
|
85
|
+
#
|
76
86
|
def git_pull
|
77
|
-
|
78
|
-
|
79
|
-
|
87
|
+
if `git branch -a` =~ /remotes\/#{@remote}\/#{@branch}/ ||
|
88
|
+
`git ls-remote #{@remote}` =~ /refs\/heads\/#{@branch}/
|
89
|
+
`git pull #{@remote} #{@branch}`
|
80
90
|
end
|
81
91
|
end
|
82
92
|
|
93
|
+
# Remove files in deploy dir, ensuring a 1:1 site files deployment.
|
94
|
+
#
|
95
|
+
def clean_deploy
|
96
|
+
FileUtils.rm_rf(Dir.glob('*'), secure: true)
|
97
|
+
end
|
98
|
+
|
99
|
+
# Copy site files into deploy dir.
|
100
|
+
#
|
83
101
|
def copy_site
|
84
|
-
|
85
|
-
FileUtils.
|
86
|
-
|
87
|
-
|
88
|
-
end
|
102
|
+
target_dir = File.join(@deploy_dir, @remote_path).sub(/\/$/,'')
|
103
|
+
FileUtils.cp_r @site_dir + '/.', target_dir
|
104
|
+
message = "Site updated at: #{Time.now.utc}"
|
105
|
+
`git add --all :/; git commit -m '#{message}'`
|
89
106
|
end
|
90
107
|
end
|
91
108
|
end
|
@@ -3,48 +3,57 @@ module Octopress
|
|
3
3
|
class Rsync
|
4
4
|
|
5
5
|
def initialize(options)
|
6
|
-
@
|
7
|
-
@
|
8
|
-
@
|
9
|
-
@
|
10
|
-
@
|
11
|
-
@
|
12
|
-
@
|
13
|
-
@
|
6
|
+
@options = options
|
7
|
+
@user = @options[:user]
|
8
|
+
@port = @options[:port]
|
9
|
+
@local = @options[:site_dir]
|
10
|
+
@remote_path = @options[:remote_path]
|
11
|
+
@exclude = @options[:exclude]
|
12
|
+
@exclude_file = @options[:exclude_file]
|
13
|
+
@exclude_file = File.expand_path(@exclude_file) if @exclude_file
|
14
|
+
@include = @options[:include]
|
15
|
+
@delete = @options[:delete]
|
16
|
+
@remote_path = @remote_path.sub(/^\//,'') #remove leading slash
|
17
|
+
@pull_dir = @options[:pull_dir]
|
14
18
|
end
|
15
19
|
|
16
20
|
def push
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
if @exclude_file
|
21
|
-
cmd << " --exclude-from #{@exclude_file}"
|
22
|
-
else
|
23
|
-
cmd << " --exclude #{@exclude}"
|
24
|
-
end
|
25
|
-
end
|
26
|
-
if @include
|
27
|
-
cmd << " --include #{@include}"
|
28
|
-
end
|
29
|
-
if @user
|
30
|
-
cmd << " ssh -p #{@port}"
|
31
|
-
end
|
32
|
-
if @delete
|
33
|
-
cmd << " --delete"
|
34
|
-
end
|
35
|
-
cmd += " #{File.join(@local, '')} "
|
36
|
-
if @user
|
37
|
-
cmd << "#{@user}:"
|
38
|
-
end
|
39
|
-
cmd << "#{@remote}"
|
21
|
+
puts "Syncing #{@local} files to #{@remote_path} with rsync."
|
22
|
+
system cmd
|
23
|
+
end
|
40
24
|
|
25
|
+
def pull
|
26
|
+
puts "Syncing #{@remote_path} files to #{@pull_dir} with rsync."
|
41
27
|
system cmd
|
42
28
|
end
|
43
29
|
|
30
|
+
def cmd
|
31
|
+
local = ''
|
32
|
+
remote = ''
|
33
|
+
|
34
|
+
cmd = "rsync -avz "
|
35
|
+
cmd << " -e " if @exclude_file || @exclude
|
36
|
+
cmd << " --exclude-from #{@exclude_file} " if @exclude_file
|
37
|
+
cmd << " --exclude #{@exclude} " if @exclude
|
38
|
+
cmd << " --include #{@include} " if @include
|
39
|
+
cmd << " --rsh='ssh -p#{@port}' " if @user && @port
|
40
|
+
cmd << " --delete " if @delete
|
41
|
+
|
42
|
+
local << " #{File.join(@local, '')} "
|
43
|
+
remote << " #{@user}:" if @user
|
44
|
+
remote << "#{@remote_path}"
|
45
|
+
|
46
|
+
if @pull_dir
|
47
|
+
cmd << remote+'/ ' << @pull_dir
|
48
|
+
else
|
49
|
+
cmd << local << remote
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
44
53
|
def self.default_config(options={})
|
45
|
-
|
54
|
+
<<-CONFIG
|
46
55
|
user: #{options[:user]}
|
47
|
-
port: #{options[:port]
|
56
|
+
port: #{options[:port]}
|
48
57
|
remote_path: #{options[:remote_path]}
|
49
58
|
delete: #{options[:delete]}
|
50
59
|
CONFIG
|
@@ -0,0 +1,177 @@
|
|
1
|
+
require 'find'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'aws-sdk'
|
4
|
+
|
5
|
+
module Octopress
|
6
|
+
module Deploy
|
7
|
+
class S3
|
8
|
+
|
9
|
+
def initialize(options)
|
10
|
+
@local = options[:site_dir]
|
11
|
+
@bucket_name = options[:bucket_name]
|
12
|
+
@access_key = options[:access_key_id] || ENV['AWS_ACCESS_KEY_ID']
|
13
|
+
@secret_key = options[:secret_access_key] || ENV['AWS_SECRET_ACCESS_KEY']
|
14
|
+
@region = options[:region] || ENV['AWS_DEFAULT_REGION'] || 'us-east-1'
|
15
|
+
@remote_path = (options[:remote_path] || '/').sub(/^\//,'')
|
16
|
+
@verbose = options[:verbose] || true
|
17
|
+
@delete = options[:delete]
|
18
|
+
@remote_path = @remote_path.sub(/^\//,'') # remove leading slash
|
19
|
+
@pull_dir = options[:pull_dir]
|
20
|
+
connect
|
21
|
+
end
|
22
|
+
|
23
|
+
def push
|
24
|
+
abort "Seriously, you should. Quitting..." unless Deploy.check_gitignore
|
25
|
+
puts "Syncing #{@local} files to #{@bucket_name} on S3."
|
26
|
+
write_files
|
27
|
+
delete_files if delete_files?
|
28
|
+
status_message
|
29
|
+
end
|
30
|
+
|
31
|
+
def pull
|
32
|
+
puts "Syncing #{@bucket_name} files to #{@pull_dir} on S3."
|
33
|
+
@bucket.objects.each do |object|
|
34
|
+
path = File.join(@pull_dir, object.key)
|
35
|
+
dir = File.dirname(path)
|
36
|
+
FileUtils.mkdir_p(dir) unless Dir.exist?(dir)
|
37
|
+
File.open(path, 'w') { |f| f.write(object.read) }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Connect to S3 using the AWS SDK
|
42
|
+
# Retuns an aws bucket
|
43
|
+
def connect
|
44
|
+
AWS.config(access_key_id: @access_key, secret_access_key: @secret_key, region: @region)
|
45
|
+
s3 = AWS.s3
|
46
|
+
@bucket = s3.buckets[@bucket_name]
|
47
|
+
unless @bucket.exists? || create_bucket(s3.buckets)
|
48
|
+
abort "No bucket created. Change your config to point to an existing bucket."
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Write site files to the selected bucket
|
53
|
+
def write_files
|
54
|
+
puts "Writing #{pluralize('file', site_files.size)}:" if @verbose
|
55
|
+
site_files.each do |file|
|
56
|
+
o = @bucket.objects[remote_path(file)]
|
57
|
+
o.write(file: file)
|
58
|
+
if @verbose
|
59
|
+
puts "+ #{remote_path(file)}"
|
60
|
+
else
|
61
|
+
progress('+')
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Delete files from the bucket, to ensure a 1:1 match with site files
|
67
|
+
def delete_files
|
68
|
+
if deletable_files.size > 0
|
69
|
+
puts "Deleting #{pluralize('file', deletable_files.size)}:" if @verbose
|
70
|
+
deletable_files.each do |file|
|
71
|
+
@bucket.objects.delete(file)
|
72
|
+
if @verbose
|
73
|
+
puts "- #{file}"
|
74
|
+
else
|
75
|
+
progress('-')
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def create_bucket(buckets)
|
82
|
+
|
83
|
+
if Deploy.ask_bool("S3 bucket '#{@bucket_name}' not found. Create one in region #{@region}?")
|
84
|
+
@bucket = buckets.create(@bucket_name)
|
85
|
+
puts "Created new bucket #{@bucket_name} in region #{@region}."
|
86
|
+
|
87
|
+
configure_bucket
|
88
|
+
true
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def configure_bucket
|
93
|
+
error_page = remote_path('404.html')
|
94
|
+
index_page = remote_path('index.html')
|
95
|
+
|
96
|
+
if Deploy.ask_bool("Bucket is not currently configured as a static websites. Configure it with index_page: #{index_page} and error_page: #{error_page}?")
|
97
|
+
config = @bucket.configure_website do |cfg|
|
98
|
+
cfg.index_document_suffix = index_page
|
99
|
+
cfg.error_document_key = error_page
|
100
|
+
end
|
101
|
+
puts "Bucket configured with index_document: #{index_page} and error_document: #{error_page}."
|
102
|
+
else
|
103
|
+
puts "You'll want to configure your new bucket using the AWS management console."
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def delete_files?
|
108
|
+
!!@delete
|
109
|
+
end
|
110
|
+
|
111
|
+
# local site files
|
112
|
+
def site_files
|
113
|
+
@site_files ||= Find.find(@local).to_a.reject do |f|
|
114
|
+
File.directory?(f)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# Destination paths for local site files.
|
119
|
+
def site_files_dest
|
120
|
+
@site_files_dest ||= site_files.map{|f| remote_path(f) }
|
121
|
+
end
|
122
|
+
|
123
|
+
# Replace local path with remote path
|
124
|
+
def remote_path(file)
|
125
|
+
File.join(@remote_path, file.sub(@local, '')).sub(/^\//, '')
|
126
|
+
end
|
127
|
+
|
128
|
+
# Files from the bucket which are deletable
|
129
|
+
# Only deletes files beneath the remote_path if specified
|
130
|
+
def deletable_files
|
131
|
+
return [] unless delete_files?
|
132
|
+
unless @deletable
|
133
|
+
@deletable = @bucket.objects.map(&:key) - site_files_dest
|
134
|
+
@deletable.reject!{|f| (f =~ /^#{@remote_path}/).nil? }
|
135
|
+
end
|
136
|
+
@deletable
|
137
|
+
end
|
138
|
+
|
139
|
+
# List written and deleted file counts
|
140
|
+
def status_message
|
141
|
+
uploaded = site_files.size
|
142
|
+
deleted = deletable_files.size
|
143
|
+
|
144
|
+
message = "\nSuccess:".green + " #{uploaded} #{pluralize('file', uploaded)} uploaded"
|
145
|
+
message << ", #{deleted} #{pluralize('file', deleted)} deleted."
|
146
|
+
puts message
|
147
|
+
configure_bucket unless @bucket.website?
|
148
|
+
end
|
149
|
+
|
150
|
+
# Print consecutive characters
|
151
|
+
def progress(str)
|
152
|
+
print str
|
153
|
+
$stdout.flush
|
154
|
+
end
|
155
|
+
|
156
|
+
def pluralize(str, num)
|
157
|
+
str << 's' if num != 1
|
158
|
+
str
|
159
|
+
end
|
160
|
+
|
161
|
+
# Return default configuration options for this deployment type
|
162
|
+
def self.default_config(options={})
|
163
|
+
<<-CONFIG
|
164
|
+
bucket_name: #{options[:bucket_name]}
|
165
|
+
access_key_id: #{options[:access_key_id]}
|
166
|
+
secret_access_key: #{options[:secret_access_key]}
|
167
|
+
region: #{options[:region] || 'us-east-1'}
|
168
|
+
remote_path: #{options[:remote_path] || '/'}
|
169
|
+
delete: #{options[:delete] || 'false'}
|
170
|
+
verbose: #{options[:verbose] || 'true'}
|
171
|
+
CONFIG
|
172
|
+
end
|
173
|
+
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
data/octopress-deploy.gemspec
CHANGED
data/test/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
_s3_deploy.yml
|
data/test/Gemfile
CHANGED
data/test/source/.hidden
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
shh.. I'm hiding. Also ignore that garbage.
|
@@ -0,0 +1 @@
|
|
1
|
+
Amazingness. And yes, the garbage.
|
@@ -0,0 +1 @@
|
|
1
|
+
Ignore the follwing garbage:
|
data/test/test.rb
CHANGED
@@ -1,22 +1,161 @@
|
|
1
|
-
require
|
1
|
+
require File.expand_path("../../lib/octopress-deploy.rb", __FILE__)
|
2
|
+
require 'fileutils'
|
3
|
+
require 'find'
|
2
4
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
5
|
+
@has_failed = false
|
6
|
+
@failures = {}
|
7
|
+
|
8
|
+
def setup_tests
|
9
|
+
`rm -rf _site` # clean up from previous tests
|
10
|
+
generate_site
|
11
|
+
end
|
12
|
+
|
13
|
+
def generate_site
|
14
|
+
system "cp -r source/ _site"
|
15
|
+
Find.find('_site').to_a.each do |f|
|
16
|
+
system("echo '#{garbage}' >> #{f}") unless File.directory?(f)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def pout(str)
|
21
|
+
print str
|
22
|
+
$stdout.flush
|
23
|
+
end
|
24
|
+
|
25
|
+
def garbage
|
26
|
+
o = [('a'..'z'), ('A'..'Z')].map { |i| i.to_a }.flatten
|
27
|
+
(0...50).map { o[rand(o.length)] }.join
|
28
|
+
end
|
29
|
+
|
30
|
+
def diff_dir(dir1, dir2)
|
31
|
+
dir_files(dir1).each do |f1|
|
32
|
+
if diff = diff_file(f1, f1.sub(dir1, dir2))
|
33
|
+
@failures["#{@testing}: #{f1}"] = diff
|
34
|
+
pout "F".red
|
35
|
+
@has_failed = true
|
36
|
+
else
|
37
|
+
pout ".".green
|
38
|
+
end
|
7
39
|
end
|
8
|
-
Octopress::Deploy.init_config('git', git_url: File.expand_path("deploy_target"))
|
9
|
-
Octopress::Deploy.push()
|
10
|
-
FileUtils.rm_r 'deploy_target'
|
11
40
|
end
|
12
41
|
|
13
|
-
def
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
42
|
+
def diff_file(file1, file2)
|
43
|
+
if File.exist?(file2)
|
44
|
+
diff = `diff #{file1} #{file2}`
|
45
|
+
if diff.size > 0
|
46
|
+
diff
|
47
|
+
else
|
48
|
+
false
|
49
|
+
end
|
50
|
+
else
|
51
|
+
"File: #{file2}: No such file or directory."
|
52
|
+
end
|
18
53
|
end
|
19
54
|
|
20
|
-
|
21
|
-
|
55
|
+
def dir_files(dir)
|
56
|
+
Find.find(dir).to_a.reject!{|f| File.directory?(f) }
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_remote_git
|
60
|
+
@testing = 'remote git'
|
61
|
+
repo = "git@github.com:octopress/deploy"
|
62
|
+
config = "_git_deploy.yml"
|
63
|
+
|
64
|
+
# Clean up from previous tests
|
65
|
+
#
|
66
|
+
`rm -rf .deploy pull-git`
|
67
|
+
|
68
|
+
# Test remote git deployment
|
69
|
+
#
|
70
|
+
Octopress::Deploy.init_config('git', config_file: config, force_write_config: true, git_branch: 'test_git_deploy', git_url: repo)
|
71
|
+
Octopress::Deploy.push(config_file: config)
|
72
|
+
Octopress::Deploy.pull('pull-git', config_file: config)
|
73
|
+
diff_dir('_site', 'pull-git')
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_local_git
|
77
|
+
@testing = 'local git'
|
78
|
+
config = "_git_deploy.yml"
|
79
|
+
|
80
|
+
# Clean up from previous tests
|
81
|
+
#
|
82
|
+
`rm -rf .deploy local-git pull-git`
|
83
|
+
`git init --bare local-git`
|
84
|
+
repo = "local-git"
|
85
|
+
|
86
|
+
# Test local git deployment
|
87
|
+
#
|
88
|
+
Octopress::Deploy.push(config_file: config, git_url: File.expand_path(repo), remote_path: 'site')
|
89
|
+
Octopress::Deploy.pull('pull-git', config_file: config, git_url: File.expand_path(repo), remote_path: 'site')
|
90
|
+
diff_dir('_site', 'pull-git/site')
|
91
|
+
end
|
92
|
+
|
93
|
+
def test_remote_rsync
|
94
|
+
@testing = 'remote rsync'
|
95
|
+
config = '_rsync_deploy.yml'
|
96
|
+
|
97
|
+
# Clean up from previous tests
|
98
|
+
`rm -rf local-rsync pull-rsync`
|
99
|
+
|
100
|
+
# Test remote git deployment
|
101
|
+
#
|
102
|
+
Octopress::Deploy.init_config('rsync', config_file: config, force_write_config: true, user: 'imathis@imathis.com', remote_path: '~/octopress-deploy/rsync/')
|
103
|
+
Octopress::Deploy.push(config_file: config)
|
104
|
+
Octopress::Deploy.pull('pull-rsync', config_file: config)
|
105
|
+
diff_dir('_site', 'pull-rsync')
|
106
|
+
|
107
|
+
end
|
108
|
+
|
109
|
+
def test_local_rsync
|
110
|
+
@testing = 'local rsync'
|
111
|
+
config = '_rsync_deploy.yml'
|
112
|
+
|
113
|
+
# Clean up from previous tests
|
114
|
+
`rm -rf local-rsync pull-rsync`
|
115
|
+
|
116
|
+
# Test local git deployment
|
117
|
+
#
|
118
|
+
Octopress::Deploy.init_config('rsync', config_file: config, force_write_config: true, remote_path: 'local-rsync')
|
119
|
+
Octopress::Deploy.push(config_file: config)
|
120
|
+
Octopress::Deploy.pull('pull-rsync', config_file: config, user: false, remote_path: 'local-rsync')
|
121
|
+
diff_dir('_site', 'pull-rsync')
|
122
|
+
end
|
123
|
+
|
124
|
+
def test_s3
|
125
|
+
@testing = 's3'
|
126
|
+
config = "_s3_deploy.yml"
|
127
|
+
`rm -rf pull-s3`
|
128
|
+
Octopress::Deploy.push(config_file: config)
|
129
|
+
Octopress::Deploy.pull('pull-s3', config_file: config)
|
130
|
+
diff_dir('_site', 'pull-s3')
|
131
|
+
end
|
132
|
+
|
133
|
+
def print_test_results
|
134
|
+
puts "\n"
|
135
|
+
if @has_failed
|
136
|
+
@failures.each do |name, diff|
|
137
|
+
puts "Failure in #{name}:".red
|
138
|
+
puts "---------"
|
139
|
+
puts diff
|
140
|
+
puts "---------"
|
141
|
+
end
|
142
|
+
abort
|
143
|
+
else
|
144
|
+
puts "All passed!".green
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
|
149
|
+
setup_tests
|
150
|
+
|
151
|
+
# local tests
|
152
|
+
test_local_rsync
|
153
|
+
test_local_git
|
154
|
+
|
155
|
+
# remote tests
|
156
|
+
test_remote_git
|
157
|
+
test_remote_rsync
|
158
|
+
test_s3
|
159
|
+
|
160
|
+
print_test_results
|
22
161
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: octopress-deploy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0.alpha.
|
4
|
+
version: 1.0.0.alpha.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brandon Mathis
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-02-
|
11
|
+
date: 2014-02-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: colorator
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - '>='
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: aws-sdk
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: bundler
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -61,19 +75,25 @@ extra_rdoc_files: []
|
|
61
75
|
files:
|
62
76
|
- .gitignore
|
63
77
|
- Gemfile
|
78
|
+
- History.markdown
|
64
79
|
- LICENSE.txt
|
65
80
|
- README.md
|
66
81
|
- Rakefile
|
67
82
|
- lib/octopress-deploy.rb
|
83
|
+
- lib/octopress-deploy/commands.rb
|
68
84
|
- lib/octopress-deploy/core_ext.rb
|
69
85
|
- lib/octopress-deploy/git.rb
|
70
86
|
- lib/octopress-deploy/rsync.rb
|
87
|
+
- lib/octopress-deploy/s3.rb
|
71
88
|
- lib/octopress-deploy/version.rb
|
72
89
|
- octopress-deploy.gemspec
|
90
|
+
- test/.gitignore
|
73
91
|
- test/Gemfile
|
74
|
-
- test/
|
75
|
-
- test/
|
76
|
-
- test/
|
92
|
+
- test/_deploy_rsync.yml
|
93
|
+
- test/_deploy_rsync_local.yml
|
94
|
+
- test/source/.hidden
|
95
|
+
- test/source/dir/file
|
96
|
+
- test/source/test_file
|
77
97
|
- test/test.rb
|
78
98
|
homepage: https://github.com/octopress/deploy
|
79
99
|
licenses:
|
@@ -100,8 +120,11 @@ signing_key:
|
|
100
120
|
specification_version: 4
|
101
121
|
summary: Deploy Octopress and Jekyll sites easily
|
102
122
|
test_files:
|
123
|
+
- test/.gitignore
|
103
124
|
- test/Gemfile
|
104
|
-
- test/
|
105
|
-
- test/
|
106
|
-
- test/
|
125
|
+
- test/_deploy_rsync.yml
|
126
|
+
- test/_deploy_rsync_local.yml
|
127
|
+
- test/source/.hidden
|
128
|
+
- test/source/dir/file
|
129
|
+
- test/source/test_file
|
107
130
|
- test/test.rb
|
data/test/_deploy.yml
DELETED
data/test/_site/foo
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
asdfasdf
|
data/test/_site/test
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
fioobr
|