#!/usr/bin/env ruby require 'optparse' require 'fileutils' require 'yaml' require 'date' options = {} OptionParser.new { |opts| opts.banner = "Usage: #{File.basename($0)} -c config.yml -t mkdocs.template.yml}" options[:config] = 'config.yml' opts.on('-f', '--config FILENAME', 'Configuration file with project definition. Defaults to "config.yml"') do |config| options[:config] = config end options[:template] = 'mkdocs.template.yml' opts.on('-t', '--template FILENAME', 'This file is used as template for the generated mkdocs.yaml. Defaults to "mkdocs.template.yml"') do |template| options[:template] = template end options[:skip_clone] = false opts.on('-s', '--skip-clone', 'Do not update the repository. This option only works if the repo was already cloned once.') do |skip| options[:skip_clone] = true end opts.on_tail('-h', '--help', 'Show this message') do puts opts exit end }.parse! def cleanup_and_clone(clone_target, git, ref, inject_central_docs = false) @git_options = "-b #{ref}" if ref =~ /tags/ if !File.directory?(clone_target) puts "Cloning #{git} to #{clone_target} ..." FileUtils.mkdir_p(clone_target) %x(git clone #{git} #{clone_target}) puts "Checking out #{ref}" %x(git --git-dir=#{clone_target}/.git --work-tree=#{clone_target} checkout #{ref} #{@git_options}) if inject_central_docs puts "Inject Central Docs" %x(git config --global user.email "info@icinga.com") %x(git config --global user.name "Icinga Docs Tool") %x(git --git-dir=#{clone_target}/.git --work-tree=#{clone_target} remote add -f package-installation-docs https://github.com/Icinga/package-installation-docs.git) %x(git --git-dir=#{clone_target}/.git --work-tree=#{clone_target} merge --squash --allow-unrelated-histories -s subtree -Xsubtree="doc/02-Installation.md.d" package-installation-docs/main) %x(git --git-dir=#{clone_target}/.git --work-tree=#{clone_target} commit -m "Merge package installation docs") end else puts "Cleaning up #{clone_target}" FileUtils::rm_rf(clone_target) cleanup_and_clone(clone_target, git, ref, inject_central_docs) end end def titleize(string) # Remove .md title = string.gsub('.md', '') # Remove numbering on the front title = title.gsub(/^\d+-/, '') # Remove dashes and underscores title = title.gsub(/(-|_)/, ' ') # Uppercase only first letter of each word, ignore stopwords stopwords = ['and', 'or', 'to', 'by', 'on', 'with', 'is', 'at', 'of', 'from', 'for'] title = title.split.map { |word| if stopwords.include?(word) word else word[0].upcase + word[1..-1] end }.join(" ") return title end def build_page_index(full_docs_dir, project_docs_dir, package = "", product = "") pages = [] puts "Building page index from #{full_docs_dir}" Dir.glob("#{full_docs_dir}/**", File::FNM_CASEFOLD).sort.each do |file| next if !file.match(/.*(\d+)-(.*)$/) filepath = file.gsub(full_docs_dir + '/', project_docs_dir + '/') filename = filepath.match(/.*?(\d+)-(.*?)(\.d)?$/) if(File.directory?("#{file}")) subdirectory = [] nav_item = titleize(filename[2]) unless File.symlink?(filepath) is_template_dir = !filename[3].nil? Dir.glob("#{file}/*.md", File::FNM_CASEFOLD).sort.each do |subfile| subfile_path = subfile.gsub(full_docs_dir + '/', project_docs_dir + '/') # Get everything after last slash subfile_name = subfile.match(/([^\/]+$)/) if(is_template_dir) %x(./parse_template.py -D product "#{product}" -D package "#{package}" -D icingaDocs true "#{full_docs_dir}" "#{subfile_path.gsub(/^doc\//, '')}") content = File.read(subfile) # Adjust self references content = content.gsub(/\[(.*)\]\(#{filename[1]}-#{Regexp.quote(filename[2])}(.*)\)/, '[\1](\2)') # Adjust external references content = content.gsub(/\[(.*)\]\((?!http|#)(.*)\)/, '[\1](../\2)') File.write(subfile, content) # Adjust path, the directory will be renamed soon subdir_name = File.basename(file) subfile_path = subfile_path.gsub(subdir_name, subdir_name.gsub(/\.md.d$/, '')) end header = titleize(subfile_name[1]) unless File.symlink?(subfile) subdirectory.push(header => subfile_path) if header end # Sort the "From Source" installation guide explicitly to the end subdirectory.each_with_index do |subdirectory_element, index| if subdirectory_element.key?("From Source") subdirectory.append(subdirectory_element) subdirectory.delete_at(index) break end end if(is_template_dir) template_path = filepath.gsub(/\.d$/, '') if(pages.include?(nav_item => template_path)) pages.delete_at(pages.find_index(nav_item => template_path)) end # Rename template directory to mimic template references newTemplateDirPath = file.gsub(/\.md.d$/, '') File.rename(file, newTemplateDirPath) # Attempt to create a index file index_content = %x(./parse_template.py -o - -D index true #{full_docs_dir} #{template_path.gsub(/^doc\//, '')}) if(!index_content.empty?) # Adjust self references index_content = index_content.gsub(/\[(.*)\]\(#{filename[1]}-#{Regexp.quote(filename[2])}(.*)\)/, '[\1](\2)') # Adjust external references index_content = index_content.gsub(/\[(.*)\]\((?!http|#)(.*)\)/, '[\1](../\2)') File.write(newTemplateDirPath + '/index.md', index_content) subdirectory.unshift(filepath.gsub(/.md.d$/, '') + '/index.md') end end pages.push(nav_item => subdirectory) if nav_item else header = titleize(filename[2]) unless File.symlink?(filepath) pages.push(header => filepath) if header end end return pages end def get_events(git, source_dir, categories) events = [] event_categories = categories clone_target = source_dir + '/events' #cleanup_and_clone('events', clone_target, git, 'master') event_categories.each do |category| category_events = YAML::load_file(clone_target + '/' + category + '.yml') if category_events category_events_sorted = category_events.sort_by { |k| k['date'] } category_events_sorted.each do |event| if event['date'] > Date.today events << event break else next end end end end events_sorted = events.sort_by { |k| k['date']} events_sorted.each do |k| k['date'] = Date::ABBR_MONTHNAMES[k['date'].month] end return events_sorted end config = YAML::load_file('config.yml') project_config = YAML::load_file(options[:config]) mkdocs = YAML::load_file(options[:template]) mkdocs['nav'] = [] puts "== #{project_config['site_name']}" version = if project_config['project']['latest'] 'latest' elsif project_config['project']['ref'] == 'master' 'snapshot' else project_config['project']['ref'].gsub('tags/', '') project_config['project']['ref'].gsub('support/', '') end source_dir = project_config['source_dir'] + '/' + project_config['project']['target'] clone_target = source_dir + '/' + version full_docs_dir = clone_target + '/' + project_config['project']['docs_dir'] inject_central_docs = project_config['inject_central_docs'] if !options[:skip_clone] cleanup_and_clone(clone_target, project_config['project']['git'], project_config['project']['ref'], inject_central_docs) end main_pages = build_page_index(full_docs_dir, project_config['project']['docs_dir'], project_config['project']['package'], project_config['project']['product']) # MKdocs allows only 'index.md' as homepage. This is a dirty workaround to use the first markdown file instead #FileUtils.ln_s("#{clone_target}/#{main_pages[0].values[0]}", "#{clone_target}/index.md", :force => true) index_file = "#{clone_target}/index.md" FileUtils.cp("#{clone_target}/#{main_pages[0].values[0]}", index_file) index_content = File.read(index_file) index_new_content = index_content.gsub(/\(((?!http)\S+(\.md|\.png)(\w)?)/, "(#{project_config['project']['docs_dir']}/\\1") File.open(index_file, "w") {|file| file.puts index_new_content } mkdocs['nav'].push('' => "index.md") if project_config['project']['subprojects'] subproject_navigation = [] project_config['project']['subprojects'].each do |subproject| if subproject[1]['git'] subproject_clone_target = clone_target + '/' + subproject[1]['target'] cleanup_and_clone(subproject_clone_target, subproject[1]['git'], subproject[1]['ref']) end subproject_navigation.push(subproject[0] => build_page_index(clone_target + '/' + subproject[1]['docs_dir'], subproject[1]['docs_dir'])) end end mkdocs['site_name'] = project_config['site_name'] mkdocs['docs_dir'] = clone_target mkdocs['site_dir'] = project_config['site_dir'] + '/' + project_config['project']['target'] + '/' + version mkdocs['repo_url'] = project_config['project']['git'].gsub('.git', '').downcase mkdocs['repo_url'] = '' if project_config['repo_url'] == 'hide' mkdocs['nav'].push(*main_pages) mkdocs['nav'].push(*subproject_navigation) if subproject_navigation #mkdocs['extra']['events'] = get_events(config['events']['git'], # config['events']['source_dir'], # config['events']['categories']) File.write('mkdocs.yml', mkdocs.to_yaml) %x(mkdocs build) Dir.chdir(mkdocs['site_dir']) Dir.glob('**/*.png').each do|f| system("optipng #{f}") or exit! end