Rakefile 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. require "rubygems"
  2. require 'rake'
  3. require 'yaml'
  4. require 'time'
  5. SOURCE = "."
  6. CONFIG = {
  7. 'version' => "0.3.0",
  8. 'themes' => File.join(SOURCE, "_includes", "themes"),
  9. 'layouts' => File.join(SOURCE, "_layouts"),
  10. 'posts' => File.join(SOURCE, "_posts"),
  11. 'post_ext' => "md",
  12. 'theme_package_version' => "0.1.0"
  13. }
  14. # Path configuration helper
  15. module JB
  16. class Path
  17. SOURCE = "."
  18. Paths = {
  19. :layouts => "_layouts",
  20. :themes => "_includes/themes",
  21. :theme_assets => "assets/themes",
  22. :theme_packages => "_theme_packages",
  23. :posts => "_posts"
  24. }
  25. def self.base
  26. SOURCE
  27. end
  28. # build a path relative to configured path settings.
  29. def self.build(path, opts = {})
  30. opts[:root] ||= SOURCE
  31. path = "#{opts[:root]}/#{Paths[path.to_sym]}/#{opts[:node]}".split("/")
  32. path.compact!
  33. File.__send__ :join, path
  34. end
  35. end #Path
  36. end #JB
  37. # Usage: rake post title="A Title" [date="2012-02-09"] [tags=[tag1,tag2]] [category="category"]
  38. desc "Begin a new post in #{CONFIG['posts']}"
  39. task :post do
  40. abort("rake aborted: '#{CONFIG['posts']}' directory not found.") unless FileTest.directory?(CONFIG['posts'])
  41. title = ENV["title"] || "new-post"
  42. tags = ENV["tags"] || "[]"
  43. category = ENV["category"] || ""
  44. category = "\"#{category.gsub(/-/,' ')}\"" if !category.empty?
  45. slug = title.downcase.strip.gsub(' ', '-').gsub(/[^\w-]/, '')
  46. begin
  47. date = (ENV['date'] ? Time.parse(ENV['date']) : Time.now).strftime('%Y-%m-%d')
  48. rescue => e
  49. puts "Error - date format must be YYYY-MM-DD, please check you typed it correctly!"
  50. exit -1
  51. end
  52. filename = File.join(CONFIG['posts'], "#{date}-#{slug}.#{CONFIG['post_ext']}")
  53. if File.exist?(filename)
  54. abort("rake aborted!") if ask("#{filename} already exists. Do you want to overwrite?", ['y', 'n']) == 'n'
  55. end
  56. puts "Creating new post: #{filename}"
  57. open(filename, 'w') do |post|
  58. post.puts "---"
  59. post.puts "layout: post"
  60. post.puts "title: \"#{title.gsub(/-/,' ')}\""
  61. post.puts 'description: ""'
  62. post.puts "category: #{category}"
  63. post.puts "tags: #{tags}"
  64. post.puts "---"
  65. post.puts "{% include JB/setup %}"
  66. end
  67. end # task :post
  68. # Usage: rake page name="about.html"
  69. # You can also specify a sub-directory path.
  70. # If you don't specify a file extention we create an index.html at the path specified
  71. desc "Create a new page."
  72. task :page do
  73. name = ENV["name"] || "new-page.md"
  74. filename = File.join(SOURCE, "#{name}")
  75. filename = File.join(filename, "index.html") if File.extname(filename) == ""
  76. title = File.basename(filename, File.extname(filename)).gsub(/[\W\_]/, " ").gsub(/\b\w/){$&.upcase}
  77. if File.exist?(filename)
  78. abort("rake aborted!") if ask("#{filename} already exists. Do you want to overwrite?", ['y', 'n']) == 'n'
  79. end
  80. mkdir_p File.dirname(filename)
  81. puts "Creating new page: #{filename}"
  82. open(filename, 'w') do |post|
  83. post.puts "---"
  84. post.puts "layout: page"
  85. post.puts "title: \"#{title}\""
  86. post.puts 'description: ""'
  87. post.puts "---"
  88. post.puts "{% include JB/setup %}"
  89. end
  90. end # task :page
  91. desc "Launch preview environment"
  92. task :preview do
  93. system "jekyll serve -w"
  94. end # task :preview
  95. # Public: Alias - Maintains backwards compatability for theme switching.
  96. task :switch_theme => "theme:switch"
  97. namespace :theme do
  98. # Public: Switch from one theme to another for your blog.
  99. #
  100. # name - String, Required. name of the theme you want to switch to.
  101. # The theme must be installed into your JB framework.
  102. #
  103. # Examples
  104. #
  105. # rake theme:switch name="the-program"
  106. #
  107. # Returns Success/failure messages.
  108. desc "Switch between Jekyll-bootstrap themes."
  109. task :switch do
  110. theme_name = ENV["name"].to_s
  111. theme_path = File.join(CONFIG['themes'], theme_name)
  112. settings_file = File.join(theme_path, "settings.yml")
  113. non_layout_files = ["settings.yml"]
  114. abort("rake aborted: name cannot be blank") if theme_name.empty?
  115. abort("rake aborted: '#{theme_path}' directory not found.") unless FileTest.directory?(theme_path)
  116. abort("rake aborted: '#{CONFIG['layouts']}' directory not found.") unless FileTest.directory?(CONFIG['layouts'])
  117. Dir.glob("#{theme_path}/*") do |filename|
  118. next if non_layout_files.include?(File.basename(filename).downcase)
  119. puts "Generating '#{theme_name}' layout: #{File.basename(filename)}"
  120. open(File.join(CONFIG['layouts'], File.basename(filename)), 'w') do |page|
  121. page.puts "---"
  122. page.puts File.read(settings_file) if File.exist?(settings_file)
  123. page.puts "layout: default" unless File.basename(filename, ".html").downcase == "default"
  124. page.puts "---"
  125. page.puts "{% include JB/setup %}"
  126. page.puts "{% include themes/#{theme_name}/#{File.basename(filename)} %}"
  127. end
  128. end
  129. puts "=> Theme successfully switched!"
  130. puts "=> Reload your web-page to check it out =)"
  131. end # task :switch
  132. # Public: Install a theme using the theme packager.
  133. # Version 0.1.0 simple 1:1 file matching.
  134. #
  135. # git - String, Optional path to the git repository of the theme to be installed.
  136. # name - String, Optional name of the theme you want to install.
  137. # Passing name requires that the theme package already exist.
  138. #
  139. # Examples
  140. #
  141. # rake theme:install git="https://github.com/jekyllbootstrap/theme-twitter.git"
  142. # rake theme:install name="cool-theme"
  143. #
  144. # Returns Success/failure messages.
  145. desc "Install theme"
  146. task :install do
  147. if ENV["git"]
  148. manifest = theme_from_git_url(ENV["git"])
  149. name = manifest["name"]
  150. else
  151. name = ENV["name"].to_s.downcase
  152. end
  153. packaged_theme_path = JB::Path.build(:theme_packages, :node => name)
  154. abort("rake aborted!
  155. => ERROR: 'name' cannot be blank") if name.empty?
  156. abort("rake aborted!
  157. => ERROR: '#{packaged_theme_path}' directory not found.
  158. => Installable themes can be added via git. You can find some here: http://github.com/jekyllbootstrap
  159. => To download+install run: `rake theme:install git='[PUBLIC-CLONE-URL]'`
  160. => example : rake theme:install git='git@github.com:jekyllbootstrap/theme-the-program.git'
  161. ") unless FileTest.directory?(packaged_theme_path)
  162. manifest = verify_manifest(packaged_theme_path)
  163. # Get relative paths to packaged theme files
  164. # Exclude directories as they'll be recursively created. Exclude meta-data files.
  165. packaged_theme_files = []
  166. FileUtils.cd(packaged_theme_path) {
  167. Dir.glob("**/*.*") { |f|
  168. next if ( FileTest.directory?(f) || f =~ /^(manifest|readme|packager)/i )
  169. packaged_theme_files << f
  170. }
  171. }
  172. # Mirror each file into the framework making sure to prompt if already exists.
  173. packaged_theme_files.each do |filename|
  174. file_install_path = File.join(JB::Path.base, filename)
  175. if File.exist? file_install_path and ask("#{file_install_path} already exists. Do you want to overwrite?", ['y', 'n']) == 'n'
  176. next
  177. else
  178. mkdir_p File.dirname(file_install_path)
  179. cp_r File.join(packaged_theme_path, filename), file_install_path
  180. end
  181. end
  182. puts "=> #{name} theme has been installed!"
  183. puts "=> ---"
  184. if ask("=> Want to switch themes now?", ['y', 'n']) == 'y'
  185. system("rake switch_theme name='#{name}'")
  186. end
  187. end
  188. # Public: Package a theme using the theme packager.
  189. # The theme must be structured using valid JB API.
  190. # In other words packaging is essentially the reverse of installing.
  191. #
  192. # name - String, Required name of the theme you want to package.
  193. #
  194. # Examples
  195. #
  196. # rake theme:package name="twitter"
  197. #
  198. # Returns Success/failure messages.
  199. desc "Package theme"
  200. task :package do
  201. name = ENV["name"].to_s.downcase
  202. theme_path = JB::Path.build(:themes, :node => name)
  203. asset_path = JB::Path.build(:theme_assets, :node => name)
  204. abort("rake aborted: name cannot be blank") if name.empty?
  205. abort("rake aborted: '#{theme_path}' directory not found.") unless FileTest.directory?(theme_path)
  206. abort("rake aborted: '#{asset_path}' directory not found.") unless FileTest.directory?(asset_path)
  207. ## Mirror theme's template directory (_includes)
  208. packaged_theme_path = JB::Path.build(:themes, :root => JB::Path.build(:theme_packages, :node => name))
  209. mkdir_p packaged_theme_path
  210. cp_r theme_path, packaged_theme_path
  211. ## Mirror theme's asset directory
  212. packaged_theme_assets_path = JB::Path.build(:theme_assets, :root => JB::Path.build(:theme_packages, :node => name))
  213. mkdir_p packaged_theme_assets_path
  214. cp_r asset_path, packaged_theme_assets_path
  215. ## Log packager version
  216. packager = {"packager" => {"version" => CONFIG["theme_package_version"].to_s } }
  217. open(JB::Path.build(:theme_packages, :node => "#{name}/packager.yml"), "w") do |page|
  218. page.puts packager.to_yaml
  219. end
  220. puts "=> '#{name}' theme is packaged and available at: #{JB::Path.build(:theme_packages, :node => name)}"
  221. end
  222. end # end namespace :theme
  223. # Internal: Download and process a theme from a git url.
  224. # Notice we don't know the name of the theme until we look it up in the manifest.
  225. # So we'll have to change the folder name once we get the name.
  226. #
  227. # url - String, Required url to git repository.
  228. #
  229. # Returns theme manifest hash
  230. def theme_from_git_url(url)
  231. tmp_path = JB::Path.build(:theme_packages, :node => "_tmp")
  232. abort("rake aborted: system call to git clone failed") if !system("git clone #{url} #{tmp_path}")
  233. manifest = verify_manifest(tmp_path)
  234. new_path = JB::Path.build(:theme_packages, :node => manifest["name"])
  235. if File.exist?(new_path) && ask("=> #{new_path} theme package already exists. Override?", ['y', 'n']) == 'n'
  236. remove_dir(tmp_path)
  237. abort("rake aborted: '#{manifest["name"]}' already exists as theme package.")
  238. end
  239. remove_dir(new_path) if File.exist?(new_path)
  240. mv(tmp_path, new_path)
  241. manifest
  242. end
  243. # Internal: Process theme package manifest file.
  244. #
  245. # theme_path - String, Required. File path to theme package.
  246. #
  247. # Returns theme manifest hash
  248. def verify_manifest(theme_path)
  249. manifest_path = File.join(theme_path, "manifest.yml")
  250. manifest_file = File.open( manifest_path )
  251. abort("rake aborted: repo must contain valid manifest.yml") unless File.exist? manifest_file
  252. manifest = YAML.load( manifest_file )
  253. manifest_file.close
  254. manifest
  255. end
  256. def ask(message, valid_options)
  257. if valid_options
  258. answer = get_stdin("#{message} #{valid_options.to_s.gsub(/"/, '').gsub(/, /,'/')} ") while !valid_options.include?(answer)
  259. else
  260. answer = get_stdin(message)
  261. end
  262. answer
  263. end
  264. def get_stdin(message)
  265. print message
  266. STDIN.gets.chomp
  267. end
  268. #Load custom rake scripts
  269. Dir['_rake/*.rake'].each { |r| load r }