Removing Rubypants (automatic smartquotes)
[octopress.git] / Rakefile
1 require "rubygems"
2 require "bundler/setup"
3 require "stringex"
4
5 ## -- Rsync Deploy config -- ##
6 # Be sure your public key is listed in your server's ~/.ssh/authorized_keys file
7 ssh_user       = "user@domain.com"
8 ssh_port       = "22"
9 document_root  = "~/website.com/"
10 rsync_delete   = false
11 rsync_args     = ""  # Any extra arguments to pass to rsync
12 deploy_default = "rsync"
13
14 # This will be configured for you when you run config_deploy
15 deploy_branch  = "gh-pages"
16
17 ## -- Misc Configs -- ##
18
19 public_dir      = "public"    # compiled site directory
20 source_dir      = "source"    # source file directory
21 blog_index_dir  = 'source'    # directory for your blog's index page (if you put your index in source/blog/index.html, set this to 'source/blog')
22 deploy_dir      = "_deploy"   # deploy directory (for Github pages deployment)
23 stash_dir       = "_stash"    # directory to stash posts for speedy generation
24 posts_dir       = "_posts"    # directory for blog files
25 themes_dir      = ".themes"   # directory for blog files
26 new_post_ext    = "markdown"  # default new post file extension when using the new_post task
27 new_page_ext    = "markdown"  # default new page file extension when using the new_page task
28 server_port     = "4000"      # port for preview server eg. localhost:4000
29
30 if (/cygwin|mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM) != nil
31   puts '## Set the codepage to 65001 for Windows machines'
32   `chcp 65001`
33 end
34
35 desc "Initial setup for Octopress: copies the default theme into the path of Jekyll's generator. Rake install defaults to rake install[classic] to install a different theme run rake install[some_theme_name]"
36 task :install, :theme do |t, args|
37   if File.directory?(source_dir) || File.directory?("sass")
38     abort("rake aborted!") if ask("A theme is already installed, proceeding will overwrite existing files. Are you sure?", ['y', 'n']) == 'n'
39   end
40   # copy theme into working Jekyll directories
41   theme = args.theme || 'classic'
42   puts "## Copying "+theme+" theme into ./#{source_dir} and ./sass"
43   mkdir_p source_dir
44   cp_r "#{themes_dir}/#{theme}/source/.", source_dir
45   mkdir_p "sass"
46   cp_r "#{themes_dir}/#{theme}/sass/.", "sass"
47   mkdir_p "#{source_dir}/#{posts_dir}"
48   mkdir_p public_dir
49 end
50
51 #######################
52 # Working with Jekyll #
53 #######################
54
55 desc "Generate jekyll site"
56 task :generate do
57   raise "### You haven't set anything up yet. First run `rake install` to set up an Octopress theme." unless File.directory?(source_dir)
58   puts "## Generating Site with Jekyll"
59   system "compass compile --css-dir #{source_dir}/stylesheets"
60   system "jekyll build"
61 end
62
63 desc "Watch the site and regenerate when it changes"
64 task :watch do
65   raise "### You haven't set anything up yet. First run `rake install` to set up an Octopress theme." unless File.directory?(source_dir)
66   puts "Starting to watch source with Jekyll and Compass."
67   system "compass compile --css-dir #{source_dir}/stylesheets" unless File.exist?("#{source_dir}/stylesheets/screen.css")
68   jekyllPid = Process.spawn({"OCTOPRESS_ENV"=>"preview"}, "jekyll build --watch")
69   compassPid = Process.spawn("compass watch")
70
71   trap("INT") {
72     [jekyllPid, compassPid].each { |pid| Process.kill(9, pid) rescue Errno::ESRCH }
73     exit 0
74   }
75
76   [jekyllPid, compassPid].each { |pid| Process.wait(pid) }
77 end
78
79 desc "preview the site in a web browser"
80 task :preview do
81   raise "### You haven't set anything up yet. First run `rake install` to set up an Octopress theme." unless File.directory?(source_dir)
82   puts "Starting to watch source with Jekyll and Compass. Starting Rack on port #{server_port}"
83   system "compass compile --css-dir #{source_dir}/stylesheets" unless File.exist?("#{source_dir}/stylesheets/screen.css")
84   jekyllPid = Process.spawn({"OCTOPRESS_ENV"=>"preview"}, "jekyll build --watch")
85   compassPid = Process.spawn("compass watch")
86   rackupPid = Process.spawn("rackup --port #{server_port}")
87
88   trap("INT") {
89     [jekyllPid, compassPid, rackupPid].each { |pid| Process.kill(9, pid) rescue Errno::ESRCH }
90     exit 0
91   }
92
93   [jekyllPid, compassPid, rackupPid].each { |pid| Process.wait(pid) }
94 end
95
96 # usage rake new_post[my-new-post] or rake new_post['my new post'] or rake new_post (defaults to "new-post")
97 desc "Begin a new post in #{source_dir}/#{posts_dir}"
98 task :new_post, :title do |t, args|
99   if args.title
100     title = args.title
101   else
102     title = get_stdin("Enter a title for your post: ")
103   end
104   raise "### You haven't set anything up yet. First run `rake install` to set up an Octopress theme." unless File.directory?(source_dir)
105   mkdir_p "#{source_dir}/#{posts_dir}"
106   filename = "#{source_dir}/#{posts_dir}/#{Time.now.strftime('%Y-%m-%d')}-#{title.to_url}.#{new_post_ext}"
107   if File.exist?(filename)
108     abort("rake aborted!") if ask("#{filename} already exists. Do you want to overwrite?", ['y', 'n']) == 'n'
109   end
110   puts "Creating new post: #{filename}"
111   open(filename, 'w') do |post|
112     post.puts "---"
113     post.puts "layout: post"
114     post.puts "title: \"#{title.gsub(/&/,'&')}\""
115     post.puts "date: #{Time.now.strftime('%Y-%m-%d %H:%M:%S %z')}"
116     post.puts "comments: true"
117     post.puts "categories: "
118     post.puts "---"
119   end
120 end
121
122 # usage rake new_page[my-new-page] or rake new_page[my-new-page.html] or rake new_page (defaults to "new-page.markdown")
123 desc "Create a new page in #{source_dir}/(filename)/index.#{new_page_ext}"
124 task :new_page, :filename do |t, args|
125   raise "### You haven't set anything up yet. First run `rake install` to set up an Octopress theme." unless File.directory?(source_dir)
126   args.with_defaults(:filename => 'new-page')
127   page_dir = [source_dir]
128   if args.filename.downcase =~ /(^.+\/)?(.+)/
129     filename, dot, extension = $2.rpartition('.').reject(&:empty?)         # Get filename and extension
130     title = filename
131     page_dir.concat($1.downcase.sub(/^\//, '').split('/')) unless $1.nil?  # Add path to page_dir Array
132     if extension.nil?
133       page_dir << filename
134       filename = "index"
135     end
136     extension ||= new_page_ext
137     page_dir = page_dir.map! { |d| d = d.to_url }.join('/')                # Sanitize path
138     filename = filename.downcase.to_url
139
140     mkdir_p page_dir
141     file = "#{page_dir}/#{filename}.#{extension}"
142     if File.exist?(file)
143       abort("rake aborted!") if ask("#{file} already exists. Do you want to overwrite?", ['y', 'n']) == 'n'
144     end
145     puts "Creating new page: #{file}"
146     open(file, 'w') do |page|
147       page.puts "---"
148       page.puts "layout: page"
149       page.puts "title: \"#{title}\""
150       page.puts "date: #{Time.now.strftime('%Y-%m-%d %H:%M')}"
151       page.puts "comments: true"
152       page.puts "sharing: true"
153       page.puts "footer: true"
154       page.puts "---"
155     end
156   else
157     puts "Syntax error: #{args.filename} contains unsupported characters"
158   end
159 end
160
161 # usage rake isolate[my-post]
162 desc "Move all other posts than the one currently being worked on to a temporary stash location (stash) so regenerating the site happens much more quickly."
163 task :isolate, :filename do |t, args|
164   stash_dir = "#{source_dir}/#{stash_dir}"
165   FileUtils.mkdir(stash_dir) unless File.exist?(stash_dir)
166   Dir.glob("#{source_dir}/#{posts_dir}/*.*") do |post|
167     FileUtils.mv post, stash_dir unless post.include?(args.filename)
168   end
169 end
170
171 desc "Move all stashed posts back into the posts directory, ready for site generation."
172 task :integrate do
173   FileUtils.mv Dir.glob("#{source_dir}/#{stash_dir}/*.*"), "#{source_dir}/#{posts_dir}/"
174 end
175
176 desc "Clean out caches: .pygments-cache, .gist-cache, .sass-cache"
177 task :clean do
178   rm_rf [Dir.glob(".pygments-cache/**"), Dir.glob(".gist-cache/**"), Dir.glob(".sass-cache/**"), "source/stylesheets/screen.css"]
179 end
180
181 desc "Move sass to sass.old, install sass theme updates, replace sass/custom with sass.old/custom"
182 task :update_style, :theme do |t, args|
183   theme = args.theme || 'classic'
184   if File.directory?("sass.old")
185     puts "removed existing sass.old directory"
186     rm_r "sass.old", :secure=>true
187   end
188   mv "sass", "sass.old"
189   puts "## Moved styles into sass.old/"
190   cp_r "#{themes_dir}/"+theme+"/sass/", "sass", :remove_destination=>true
191   cp_r "sass.old/custom/.", "sass/custom/", :remove_destination=>true
192   puts "## Updated Sass ##"
193 end
194
195 desc "Move source to source.old, install source theme updates, replace source/_includes/navigation.html with source.old's navigation"
196 task :update_source, :theme do |t, args|
197   theme = args.theme || 'classic'
198   if File.directory?("#{source_dir}.old")
199     puts "## Removed existing #{source_dir}.old directory"
200     rm_r "#{source_dir}.old", :secure=>true
201   end
202   mkdir "#{source_dir}.old"
203   cp_r "#{source_dir}/.", "#{source_dir}.old"
204   puts "## Copied #{source_dir} into #{source_dir}.old/"
205   cp_r "#{themes_dir}/"+theme+"/source/.", source_dir, :remove_destination=>true
206   cp_r "#{source_dir}.old/_includes/custom/.", "#{source_dir}/_includes/custom/", :remove_destination=>true
207   cp "#{source_dir}.old/favicon.png", source_dir
208   mv "#{source_dir}/index.html", "#{blog_index_dir}", :force=>true if blog_index_dir != source_dir
209   cp "#{source_dir}.old/index.html", source_dir if blog_index_dir != source_dir && File.exists?("#{source_dir}.old/index.html")
210   puts "## Updated #{source_dir} ##"
211 end
212
213 ##############
214 # Deploying  #
215 ##############
216
217 desc "Default deploy task"
218 task :deploy do
219   # Check if preview posts exist, which should not be published
220   if File.exists?(".preview-mode")
221     puts "## Found posts in preview mode, regenerating files ..."
222     File.delete(".preview-mode")
223     Rake::Task[:generate].execute
224   end
225
226   Rake::Task[:copydot].invoke(source_dir, public_dir)
227   Rake::Task["#{deploy_default}"].execute
228 end
229
230 desc "Generate website and deploy"
231 task :gen_deploy => [:integrate, :generate, :deploy] do
232 end
233
234 desc "copy dot files for deployment"
235 task :copydot, :source, :dest do |t, args|
236   FileList["#{args.source}/**/.*"].exclude("**/.", "**/..", "**/.DS_Store", "**/._*").each do |file|
237     cp_r file, file.gsub(/#{args.source}/, "#{args.dest}") unless File.directory?(file)
238   end
239 end
240
241 desc "Deploy website via rsync"
242 task :rsync do
243   exclude = ""
244   if File.exists?('./rsync-exclude')
245     exclude = "--exclude-from '#{File.expand_path('./rsync-exclude')}'"
246   end
247   puts "## Deploying website via Rsync"
248   ok_failed system("rsync -avze 'ssh -p #{ssh_port}' #{exclude} #{rsync_args} #{"--delete" unless rsync_delete == false} #{public_dir}/ #{ssh_user}:#{document_root}")
249 end
250
251 desc "deploy public directory to github pages"
252 multitask :push do
253   puts "## Deploying branch to Github Pages "
254   puts "## Pulling any updates from Github Pages "
255   cd "#{deploy_dir}" do 
256     Bundler.with_clean_env { system "git pull" }
257   end
258   (Dir["#{deploy_dir}/*"]).each { |f| rm_rf(f) }
259   Rake::Task[:copydot].invoke(public_dir, deploy_dir)
260   puts "\n## Copying #{public_dir} to #{deploy_dir}"
261   cp_r "#{public_dir}/.", deploy_dir
262   cd "#{deploy_dir}" do
263     system "git add -A"
264     message = "Site updated at #{Time.now.utc}"
265     puts "\n## Committing: #{message}"
266     system "git commit -m \"#{message}\""
267     puts "\n## Pushing generated #{deploy_dir} website"
268     Bundler.with_clean_env { system "git push origin #{deploy_branch}" }
269     puts "\n## Github Pages deploy complete"
270   end
271 end
272
273 desc "Update configurations to support publishing to root or sub directory"
274 task :set_root_dir, :dir do |t, args|
275   puts ">>> !! Please provide a directory, eg. rake config_dir[publishing/subdirectory]" unless args.dir
276   if args.dir
277     if args.dir == "/"
278       dir = ""
279     else
280       dir = "/" + args.dir.sub(/(\/*)(.+)/, "\\2").sub(/\/$/, '');
281     end
282     rakefile = IO.read(__FILE__)
283     rakefile.sub!(/public_dir(\s*)=(\s*)(["'])[\w\-\/]*["']/, "public_dir\\1=\\2\\3public#{dir}\\3")
284     File.open(__FILE__, 'w') do |f|
285       f.write rakefile
286     end
287     compass_config = IO.read('config.rb')
288     compass_config.sub!(/http_path(\s*)=(\s*)(["'])[\w\-\/]*["']/, "http_path\\1=\\2\\3#{dir}/\\3")
289     compass_config.sub!(/http_images_path(\s*)=(\s*)(["'])[\w\-\/]*["']/, "http_images_path\\1=\\2\\3#{dir}/images\\3")
290     compass_config.sub!(/http_fonts_path(\s*)=(\s*)(["'])[\w\-\/]*["']/, "http_fonts_path\\1=\\2\\3#{dir}/fonts\\3")
291     compass_config.sub!(/css_dir(\s*)=(\s*)(["'])[\w\-\/]*["']/, "css_dir\\1=\\2\\3public#{dir}/stylesheets\\3")
292     File.open('config.rb', 'w') do |f|
293       f.write compass_config
294     end
295     jekyll_config = IO.read('_config.yml')
296     jekyll_config.sub!(/^destination:.+$/, "destination: public#{dir}")
297     jekyll_config.sub!(/^subscribe_rss:\s*\/.+$/, "subscribe_rss: #{dir}/atom.xml")
298     jekyll_config.sub!(/^root:.*$/, "root: /#{dir.sub(/^\//, '')}")
299     File.open('_config.yml', 'w') do |f|
300       f.write jekyll_config
301     end
302     rm_rf public_dir
303     mkdir_p "#{public_dir}#{dir}"
304     puts "## Site's root directory is now '/#{dir.sub(/^\//, '')}' ##"
305   end
306 end
307
308 desc "Set up _deploy folder and deploy branch for Github Pages deployment"
309 task :setup_github_pages, :repo do |t, args|
310   if args.repo
311     repo_url = args.repo
312   else
313     puts "Enter the read/write url for your repository"
314     puts "(For example, 'git@github.com:your_username/your_username.github.io.git)"
315     puts "           or 'https://github.com/your_username/your_username.github.io')"
316     repo_url = get_stdin("Repository url: ")
317   end
318   protocol = (repo_url.match(/(^git)@/).nil?) ? 'https' : 'git'
319   if protocol == 'git'
320     user = repo_url.match(/:([^\/]+)/)[1]
321   else
322     user = repo_url.match(/github\.com\/([^\/]+)/)[1]
323   end
324   branch = (repo_url.match(/\/[\w-]+\.github\.(?:io|com)/).nil?) ? 'gh-pages' : 'master'
325   project = (branch == 'gh-pages') ? repo_url.match(/([^\/]+?)(\.git|$)/i)[1] : ''
326   unless (`git remote -v` =~ /origin.+?octopress(?:\.git)?/).nil?
327     # If octopress is still the origin remote (from cloning) rename it to octopress
328     system "git remote rename origin octopress"
329     if branch == 'master'
330       # If this is a user/organization pages repository, add the correct origin remote
331       # and checkout the source branch for committing changes to the blog source.
332       system "git remote add origin #{repo_url}"
333       puts "Added remote #{repo_url} as origin"
334       system "git config branch.master.remote origin"
335       puts "Set origin as default remote"
336       system "git branch -m master source"
337       puts "Master branch renamed to 'source' for committing your blog source files"
338     else
339       unless !public_dir.match("#{project}").nil?
340         system "rake set_root_dir[#{project}]"
341       end
342     end
343   end
344   url = blog_url(user, project, source_dir)
345   jekyll_config = IO.read('_config.yml')
346   jekyll_config.sub!(/^url:.*$/, "url: #{url}")
347   File.open('_config.yml', 'w') do |f|
348     f.write jekyll_config
349   end
350   rm_rf deploy_dir
351   mkdir deploy_dir
352   cd "#{deploy_dir}" do
353     system "git init"
354     system 'echo "My Octopress Page is coming soon &hellip;" > index.html'
355     system "git add ."
356     system "git commit -m \"Octopress init\""
357     system "git branch -m gh-pages" unless branch == 'master'
358     system "git remote add origin #{repo_url}"
359     rakefile = IO.read(__FILE__)
360     rakefile.sub!(/deploy_branch(\s*)=(\s*)(["'])[\w-]*["']/, "deploy_branch\\1=\\2\\3#{branch}\\3")
361     rakefile.sub!(/deploy_default(\s*)=(\s*)(["'])[\w-]*["']/, "deploy_default\\1=\\2\\3push\\3")
362     File.open(__FILE__, 'w') do |f|
363       f.write rakefile
364     end
365   end
366   puts "\n---\n## Now you can deploy to #{repo_url} with `rake deploy` ##"
367 end
368
369 def ok_failed(condition)
370   if (condition)
371     puts "OK"
372   else
373     puts "FAILED"
374   end
375 end
376
377 def get_stdin(message)
378   print message
379   STDIN.gets.chomp
380 end
381
382 def ask(message, valid_options)
383   if valid_options
384     answer = get_stdin("#{message} #{valid_options.to_s.gsub(/"/, '').gsub(/, /,'/')} ") while !valid_options.include?(answer)
385   else
386     answer = get_stdin(message)
387   end
388   answer
389 end
390
391 def blog_url(user, project, source_dir)
392   cname = "#{source_dir}/CNAME"
393   url = if File.exists?(cname)
394     "http://#{IO.read(cname).strip}"
395   else
396     "http://#{user.downcase}.github.io"
397   end
398   url += "/#{project}" unless project == ''
399   url
400 end
401
402 desc "list tasks"
403 task :list do
404   puts "Tasks: #{(Rake::Task.tasks - [Rake::Task[:list]]).join(', ')}"
405   puts "(type rake -T for more detail)\n\n"
406 end