diff --git a/.gitignore b/.gitignore index 46957c4..fed4dfe 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ Vagrantfile .vagrant +checkout/ +docs_generation.lock diff --git a/README.md b/README.md index 9dbf522..d73fb17 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,20 @@ LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8 ``` +## RVM + +Be sure to have rvm binaries in your PATH to make RVM scripting work correctly. + +- With fish shell, in `~/.config/fish/config.fish`: + +``` +set -gx PATH $HOME/.rvm/bin $PATH +``` + +## Nokogiri + +You might need to do `bundle config build.nokogiri --use-system-libraries` on macOS to have nokogiri compile. + ## Deployment Just push to `master`. The cron job in the docs server pulls before invoking @@ -48,4 +62,4 @@ In order to run the test suite you need a recent version of minitest: There are two tasks: The default task, `test`, tests everything except actual docs generation. The `test:all` task runs the entire suite including doc -generation for a few releases, this one takes about 20 minutes in my laptop. +generation for a few releases, this one takes about 20 minutes on my laptop. diff --git a/bin/generate_docs.rb b/bin/generate_docs.rb index a4795eb..f027be7 100755 --- a/bin/generate_docs.rb +++ b/bin/generate_docs.rb @@ -5,11 +5,52 @@ require 'lock_file' require 'docs_generator' require 'git_manager' +require 'fileutils' +require 'optparse' + +options = {} + +OptionParser.new do |opts| + opts.banner = "Usage: bin/generate_docs.rb [options]" + + opts.on("-v", "--verbose", "Run verbosely") do |v| + options[:verbose] = v + end + + opts.on( + "-tTARGET", + "--target=TARGET", + "Target directory where to checkout the code, defaults to HOME" + ) do |t| + options[:target] = t + end + + opts.on( + "--tags=TAGS", + "Comma-separated git tags (e.g. Rails versions) to checkout (format: vX.X.X, edge)" + ) do |t| + options[:tags] = t + end +end.parse! + +options[:verbose] = false if options[:verbose].nil? +options[:target] ||= Dir.home +options[:target].gsub!(/\/$/, '') + +unless Dir.exists?(options[:target]) + FileUtils.mkdir(options[:target]) +end LockFile.acquiring('docs_generation.lock') do - git_manager = GitManager.new(Dir.home) + git_manager = GitManager.new(options[:target], verbose: options[:verbose]) git_manager.update_master - generator = DocsGenerator.new(Dir.home, git_manager) + generator = DocsGenerator.new( + options[:target], + git_manager, + verbose: options[:verbose], + tags: options[:tags] + ) + generator.generate end diff --git a/lib/docs_compressor.rb b/lib/docs_compressor.rb index 0fad71f..38a398a 100644 --- a/lib/docs_compressor.rb +++ b/lib/docs_compressor.rb @@ -2,12 +2,14 @@ require 'fileutils' require 'shellwords' require 'logging' +require 'running' # Compresses HTML, JavaScript, and CSS under the given directory, recursively. # # We do this to leverage gzip_static in nginx. class DocsCompressor include Logging + include Running EXTENSIONS = %w(.js .html .css) @@ -40,7 +42,7 @@ def compress_file(file) orig = Shellwords.shellescape(file) dest = Shellwords.shellescape(gzname(file)) - system %(gzip -c -9 #{orig} > #{dest}) + log_and_system %(gzip -c -9 #{orig} > #{dest}) end def compress_file?(file) diff --git a/lib/docs_generator.rb b/lib/docs_generator.rb index 7ed5810..aa00250 100644 --- a/lib/docs_generator.rb +++ b/lib/docs_generator.rb @@ -66,9 +66,11 @@ class DocsGenerator # # @param basedir [String] # @param git_manager [GitManager] - def initialize(basedir, git_manager=GitManager.new(basedir)) + def initialize(basedir, git_manager = GitManager.new(basedir), verbose: false, tags: nil) @basedir = File.expand_path(basedir) @git_manager = git_manager + @verbose = verbose + @tags = tags.to_s.split(',') end def generate @@ -82,7 +84,14 @@ def generate_release_docs new_release_docs = false git_manager.release_tags.each do |tag| - if generate_docs_for_release?(tag) + targeted = @tags.any? && @tags.include?(tag) + + if !targeted && @tags.any? + log "Skipping #{tag}" + next + end + + if generate_docs_for_release?(tag) || targeted generate_docs_for_release(tag) new_release_docs = true end @@ -98,7 +107,7 @@ def generate_docs_for_release?(tag) def generate_docs_for_release(tag) git_manager.checkout(tag) - generator = Generators::Release.new(tag, tag) + generator = Generators::Release.new(tag, tag, verbose: @verbose) generator.generate DocsCompressor.new(generator.api_output).compress @@ -109,7 +118,17 @@ def generate_docs_for_release(tag) end def generate_edge_docs - generator = Generators::Master.new(git_manager.short_sha1, 'master') + if @tags.any? && !@tags.include?("edge") + log "Skipping edge" + return + end + + generator = Generators::Master.new( + git_manager.short_sha1, + 'master', + verbose: @verbose + ) + generator.generate DocsCompressor.new(generator.api_output).compress diff --git a/lib/generators/base.rb b/lib/generators/base.rb index c59da8c..c931418 100644 --- a/lib/generators/base.rb +++ b/lib/generators/base.rb @@ -13,9 +13,14 @@ class Base # @param target [String] A Git reference like 'v4.2.0' or a SHA1 # @param basedir [String] A directory in which target has been checked out - def initialize(target, basedir) + def initialize(target, basedir, verbose: false) @target = target @basedir = File.expand_path(basedir) + @verbose = verbose + end + + def dev_null_unless_verbose + @verbose ? "" : " > /dev/null" end # Generates the documentation for target within basedir. @@ -72,9 +77,15 @@ def env_as_assigns(env) # @param command [String] # @param env [Hash{String => String}] def run(command, env={}) - command = "rvm #{ruby_version} do #{command} >/dev/null" + command = "rvm #{ruby_version} do #{command}#{dev_null_unless_verbose}" log "#{env_as_assigns(env)} #{command}" - system(env, command) + + if system(env, command) + true + else + log "\"#{command}\" failed to execute" + abort + end end # Runs `bundle exec rake` with the appropriate Bundler and Ruby versions. diff --git a/lib/generators/release.rb b/lib/generators/release.rb index e6c642b..56ce2d2 100644 --- a/lib/generators/release.rb +++ b/lib/generators/release.rb @@ -7,8 +7,8 @@ module Generators class Release < Base include Config::Release - def initialize(tag, basedir) - super(tag, basedir) + def initialize(tag, basedir, verbose: false) + super(tag, basedir, verbose: verbose) @version_number = VersionNumber.new(tag) end @@ -35,6 +35,8 @@ def generate_api end def generate_guides + FileUtils.mkdir('guides') unless Dir.exists?('guides') + Dir.chdir('guides') do rake 'guides:generate:html', 'RAILS_VERSION' => target rake 'guides:generate:kindle', 'RAILS_VERSION' => target diff --git a/lib/git_manager.rb b/lib/git_manager.rb index 0184bc1..5457f90 100644 --- a/lib/git_manager.rb +++ b/lib/git_manager.rb @@ -1,13 +1,20 @@ require 'logging' +require 'running' # Lightweight wrapper over Git, shells out everything. class GitManager include Logging + include Running attr_reader :basedir - def initialize(basedir) + def initialize(basedir, verbose: false) @basedir = File.expand_path(basedir) + @verbose = verbose + end + + def q_unless_verbose + @verbose ? "" : "-q" end def remote_rails_url @@ -18,7 +25,7 @@ def update_master Dir.chdir(basedir) do unless Dir.exist?('master') log "cloning master into #{basedir}/master" - system "git clone -q #{remote_rails_url} master" + log_and_system "git clone #{q_unless_verbose} #{remote_rails_url} master" end Dir.chdir('master') do @@ -28,8 +35,8 @@ def update_master # git pull from succeeding. Starting with Bundler 1.10, if Gemfile.lock # does not change BUNDLED WITH is left as is, even if versions differ, # but since docs generation is automated better play safe. - system 'git checkout Gemfile.lock' - system 'git pull -q' + log_and_system 'git checkout Gemfile.lock' + log_and_system "git pull #{q_unless_verbose}" end end end @@ -37,10 +44,10 @@ def update_master def checkout(tag) Dir.chdir(basedir) do log "checking out tag #{tag}" - system "git clone -q #{remote_rails_url} #{tag}" + log_and_system "git clone #{q_unless_verbose} #{remote_rails_url} #{tag}" Dir.chdir(tag) do - system "git checkout -q #{tag}" + log_and_system "git checkout #{q_unless_verbose} #{tag}" end end end diff --git a/lib/running.rb b/lib/running.rb new file mode 100644 index 0000000..b8ddb3f --- /dev/null +++ b/lib/running.rb @@ -0,0 +1,8 @@ +module Running + include Logging + + def log_and_system(*args) + log(args.map(&:to_s).join(' ')) + system(*args).tap { log "Done" } + end +end