-
Notifications
You must be signed in to change notification settings - Fork 0
/
deploy.rb
executable file
·183 lines (152 loc) · 5.33 KB
/
deploy.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
#!/usr/bin/env ruby
# encoding: utf-8
require 'json'
require 'shellwords'
require 'optparse'
OPTIONS = {config: 'deployment.json', dry_run: false, verbose: false}
OptionParser.new do |opts|
opts.banner = "Usage: #{File.basename(__FILE__)} [options] [staging|production]"
opts.banner << "\n"
opts.banner << "\nOptimize assets and deploy your static website."
opts.banner << "\n"
opts.banner << "\nOptions:"
opts.on("-c", "--config", "Config file (default: deployment.json)") do |v|
OPTIONS[:dry_run] = v
end
opts.on("-n", "--[no-]dry-run", "Run without actions") do |v|
OPTIONS[:dry_run] = v
end
opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
OPTIONS[:verbose] = v
end
end.parse!
OPTIONS[:env] = ARGV[0] || 'staging'
CONFIG = JSON.parse(File.read(OPTIONS[:config]))
URL = CONFIG['urls'].fetch(OPTIONS[:env])
module Utils
def tool_present?(cmd_string)
components = Shellwords.split(cmd_string)
program = components[0]
`which #{program}`
$?.success?
end
def log(string)
if OPTIONS[:verbose]
puts string
end
end
def run(cmd_string, options = {})
unless tool_present?(cmd_string)
abort("ERROR: Please install #{program} to continue!")
end
# Print message
message = options[:message]
if message.nil?
log ">> Running `#{cmd_string}`..."
else
log(">> #{message}...") if message
end
# Run command
output = OPTIONS[:dry_run] || `#{cmd_string}`
abort("ERROR in execution of `#{cmd_string}`!") unless $?.success?
output
end
def ask_for_commit
dirty = `git status -s | grep -v '^??'`.split("\n").length > 0
if dirty
print "Do you want to commmit changes first? [yN] "
if STDIN.gets.chomp =~ /y/i
exit 1
end
end
end
end
class Deployment
include Utils
def initialize(source_dir, build_dir)
@source_dir = source_dir
@build_dir = build_dir
@git = tool_present?("git")
end
def deploy_to(url)
log "Deploying to #{url}..."
sleep 0.5
ask_for_commit if @git
clone
minify_asset(css_dir, CONFIG['css_files'], 'css')
run("rm -rf #{File.join(clone_dir, CONFIG['assets_dir'], 'sass')}", message: 'Remove SASS files')
minify_asset(js_dir, CONFIG['js_files'], 'js')
# minify_html
minify_images(File.join(clone_dir, CONFIG['images_dir']))
set_noindex if OPTIONS[:env] != 'production'
upload(url)
log "Successfully deployed."
end
def clone
run("rm -rf #{@build_dir}", message: 'Cleaning old build')
if @git
run("git clone . #{@build_dir} 2>/dev/null", message: 'Cloning commited files')
else
run("cp -r #{@source_dir} #{@build_dir}", message: 'Copying files')
end
end
private
def minify_html
pages.each do |page|
basename = File.basename(page)
run("minify --type=html -o #{clone_dir}/#{basename} #{clone_dir}/#{basename}", message: "Minify #{basename}")
end
end
def minify_asset(dir, basenames, ext)
files = basenames.map { |basename| File.join(dir, basename) }
minified = "#{dir}/min.#{ext}"
run("minify -o #{minified} #{files.join(' ')}", message: 'Minify CSS')
log ">> Use MD5 hash for minified #{ext.upcase}..."
if OPTIONS[:dry_run]
md5 = 'dry-run'
else
md5 = run("md5sum #{minified}", message: false).chomp.split(/\s+/)[0]
end
run("mv #{minified} #{File.join(clone_dir, CONFIG['assets_dir'], "#{md5}.min.#{ext}")}", message: false)
run("rm -rf #{dir}", message: false)
# replace every asset reference with minified version
files.each_with_index do |file, i|
file = File.join(ext, File.basename(file))
regex = Regexp.escape(file).gsub('/', '\/')
last = i == files.length - 1
if last
change_pages("s/#{regex}/#{md5}.min.#{ext}/g", message: false)
else
change_pages("/#{regex}/d", message: false)
end
end
end
def minify_images(dir)
run("jpegoptim --strip-all --all-progressive -m90 #{dir}/*.jpg", message: 'Optimize JPGs')
run("optipng #{dir}/*.png 2>/dev/null", message: 'Optimize PNGs')
# run("svgo -f #{dir}", message: 'Optimize SVGs')
end
def set_noindex
message = "Set noindex and nofollow for #{OPTIONS[:env]} environment"
change_pages("s/content=\".*index.*\"/content=\"noindex, nofollow\"/g", message: message)
end
def upload(url)
run("rsync -avz --delete #{clone_dir}/ #{File.join(CONFIG['remote'], url)}", message: 'Uploading')
end
def change_pages(sed_string, options)
run("sed -i -e '#{sed_string}' #{pages.join(' ')}", options)
end
def css_dir
File.join(clone_dir, CONFIG['css_dir'])
end
def js_dir
File.join(clone_dir, CONFIG['js_dir'])
end
def pages
CONFIG['pages'].map { |p| File.join(clone_dir, p) }
end
def clone_dir
@git ? File.join(@build_dir, @source_dir) : @build_dir
end
end
Deployment.new("src", "build").deploy_to(URL)