forked from prometheus/docs
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Automate repository docs inclusion (prometheus#1762)
So far every prometheus/alertmanater/... release branch had to be manually configured in the nanoc.yaml config file. With this change the most recent release branches will be checked out automatically if a corresponding semver tag exists. As the Prometheus git repository includes several hundreds of megabytes of vendored assets, the repository is cloned bare and all blobs are filtered by default. Each version is then checked out in an individual working tree and git's spare-chekcout feature is used to reduce the checkout to the `docs/` folder. The git data is cached in `tmp/repo_docs/` and will be recreated automatically if removed. Signed-off-by: Tobias Schmidt <[email protected]>
- Loading branch information
Showing
12 changed files
with
199 additions
and
377 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,8 +5,7 @@ output/ | |
|
||
# Temporary file directory | ||
tmp/ | ||
/downloads/ | ||
/repositories/ | ||
downloads/ | ||
|
||
# Crash Log | ||
crash.log | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,60 +1,158 @@ | ||
# TODO(ts): Rewrite data source and use one single instance to combine all | ||
# different versions for a given path. | ||
class RepoDocsDataSource < ::Nanoc::DataSources::Filesystem | ||
require 'uri' | ||
|
||
# The RepoDocs data source provides items sourced from other Git repositories. | ||
# For a given repository_url, all git version tags are fetched and for the most | ||
# recent (in order to save compilation time) tags the `docs/` folder in the | ||
# respective `release-<version>` is checked out and its content mounted under | ||
# the given `items_root`. | ||
# | ||
# As the Prometheus git repository includes several hundreds of megabytes of | ||
# vendored assets, the repository is cloned bare and all blobs are filtered by | ||
# default. Each version is then checked out in an individual working tree and | ||
# git's spare-checkout feature is used to reduce the checkout to the `docs/` | ||
# folder. The git data is cached in `tmp/repo_docs/`. | ||
class RepoDocsDataSource < ::Nanoc::DataSource | ||
identifier :repo_docs | ||
|
||
PATH = "repositories" | ||
DOCS_DIRECTORY = 'docs'.freeze | ||
BRANCH_PATTERN = 'release-*'.freeze | ||
VERSION_REGEXP = /\Av\d+\.\d+\.\d+(?:-[a-z0-9.]+)?\z/.freeze | ||
TMPDIR = 'tmp/repo_docs/'.freeze | ||
|
||
def up | ||
c = config[:config] | ||
|
||
%x( | ||
scripts/checkout.sh \ | ||
-d "#{docs_root}" \ | ||
-t "#{repo_path}" \ | ||
"#{c[:repository]}" "#{c[:refspec]}" | ||
) | ||
if $?.exitstatus != 0 | ||
raise "Couldn't checkout repository #{c.inspect}" | ||
end | ||
|
||
super | ||
validate | ||
sync_repository | ||
end | ||
|
||
def items | ||
c = config.fetch(:config) | ||
super.map do |item| | ||
attrs = item.attributes.dup | ||
attrs[:repo_docs] = c | ||
attrs[:repo_docs][:items_root] = config.fetch(:items_root) | ||
# TODO(ts): Remove assumptions about the path layout, rewrite datasource. | ||
attrs[:repo_docs][:version_root] = config.fetch(:items_root).sub(%r{(.+/)[^/]+/\Z}, '\\1') | ||
# TODO(ts): Document that repo doc index.md will be ignored. | ||
if item.identifier == '/' | ||
attrs[:nav] = { strip: true } | ||
items_root = config.fetch(:items_root, '/') | ||
latest = latest_version | ||
|
||
versions.inject([]) do |list, version| | ||
branch = "release-#{version}" | ||
dir = git_checkout(branch, DOCS_DIRECTORY) | ||
fs_config = { content_dir: dir, encoding: 'utf-8', identifier_type: 'legacy' } | ||
fs = ::Nanoc::DataSources::Filesystem.new(@site_config, '/', '/', fs_config) | ||
|
||
fs.items.each do |item| | ||
attrs = item.attributes.dup | ||
attrs[:nav] = { strip: true } if item.identifier == '/' | ||
attrs[:repo_docs] = { | ||
name: version, | ||
refspec: branch, | ||
version: version, | ||
latest: latest, | ||
items_root: items_root, | ||
version_root: File.join(items_root, version, '/'), | ||
canonical_root: File.join(items_root, 'latest', '/'), | ||
repository_url: git_remote, | ||
entrypoint: config[:config][:entrypoint], | ||
} | ||
|
||
if version == latest | ||
lattrs = attrs.dup | ||
lattrs[:repo_docs] = attrs[:repo_docs].dup | ||
lattrs[:repo_docs][:name] = "latest (#{version})" | ||
lattrs[:repo_docs][:version_root] = lattrs[:repo_docs][:canonical_root] | ||
list << new_item(item.content, lattrs, item.identifier.prefix('/latest')) | ||
end | ||
|
||
list << new_item(item.content, attrs, item.identifier.prefix('/' + version)) | ||
end | ||
new_item(item.content, attrs, item.identifier) | ||
|
||
list | ||
end | ||
end | ||
|
||
def content_dir_name | ||
File.join(repo_path, docs_root) | ||
private | ||
|
||
def validate | ||
if !config[:config].has_key?(:entrypoint) | ||
fail ArgumentError, 'entrypoint config option must be set' | ||
end | ||
if !config[:config].has_key?(:repository_url) | ||
fail ArgumentError, 'repository config option must be set' | ||
end | ||
URI(config[:config][:repository_url]) # raises an exception if invalid | ||
end | ||
|
||
def layouts_dir_name | ||
'unsupported' | ||
def git_remote | ||
config[:config][:repository_url] | ||
end | ||
|
||
private | ||
def git_dir | ||
basename = File.basename(git_remote) | ||
basename += '.git' unless basename.end_with?('.git') | ||
File.join(TMPDIR, basename) | ||
end | ||
|
||
def git_branches | ||
output = `cd #{git_dir} && git branch --format='%(refname:short)' --list '#{BRANCH_PATTERN}'` | ||
fail "Could not list git branches" if $?.exitstatus != 0 | ||
output.split("\n") | ||
end | ||
|
||
def git_tags | ||
output = `cd #{git_dir} && git tag` | ||
fail "Could not list git tags" if $?.exitstatus != 0 | ||
output.split("\n") | ||
end | ||
|
||
# git_checkout checks out the directory in the specified branch using git's | ||
# sparse checkout and returns the path to the location in the workingtree. | ||
def git_checkout(branch, directory) | ||
worktree = File.absolute_path(File.join(git_dir.delete_suffix('.git'), branch)) | ||
if !File.exist?(File.join(worktree, '.git')) | ||
run_command("cd #{git_dir} && git worktree add --no-checkout #{worktree} #{branch}") | ||
end | ||
|
||
worktree_info = File.join(git_dir, 'worktrees', branch, 'info') | ||
Dir.mkdir(worktree_info) if !Dir.exist?(worktree_info) | ||
File.write(File.join(worktree_info, 'sparse-checkout'), "/#{directory}\n") | ||
|
||
run_command("cd #{worktree} && git reset --hard --quiet && git clean --force") | ||
File.join(worktree, directory) | ||
end | ||
|
||
# sync_repository clones or updates a bare git repository and enables the | ||
# sparse checkout feature. | ||
def sync_repository | ||
if !Dir.exist?(git_dir) | ||
run_command("git clone --bare --filter=blob:none #{git_remote} #{git_dir}") | ||
run_command("cd #{git_dir} && git config core.sparseCheckout true") | ||
else | ||
run_command("cd #{git_dir} && git fetch --quiet") | ||
end | ||
end | ||
|
||
# versions returns an ordered list of major.minor version names for which | ||
# documentation should be published. Only the most recent versions for which a | ||
# corresponding release-* branch exists are returned. | ||
def versions | ||
branches = git_branches | ||
all = git_tags | ||
.select { |v| v.match(VERSION_REGEXP) } | ||
.map { |v| v.delete_prefix('v').split('.')[0, 2].join('.') } | ||
.uniq | ||
.select { |v| branches.include?('release-' + v) } | ||
.sort_by { |v| v.split('.').map(&:to_i) } | ||
.reverse | ||
|
||
# Number of versions is reduced to speed up site compilation time. | ||
grouped = all.group_by { |v| v.split('.').first } | ||
grouped.inject([]) do |list, (major, versions)| | ||
size = major == grouped.keys.first ? 10 : 1 | ||
list += versions[0, size] | ||
end | ||
end | ||
|
||
def docs_root | ||
c = config.fetch(:config) | ||
c.fetch(:root, 'docs/') | ||
# latest_version returns the latest released version. | ||
def latest_version | ||
tags = git_tags | ||
versions.find { |v| tags.any? { |t| t.start_with?('v' + v) && !t.include?('-') } } | ||
end | ||
|
||
def repo_path | ||
c = config.fetch(:config) | ||
base = c.fetch(:repo_base, 'repositories') | ||
File.join(base, File.basename(c[:repository]), c[:name]) | ||
def run_command(cmd) | ||
fail "Running command '#{cmd}' failed" if !system(cmd) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
# encoding: utf-8 | ||
|
||
require 'nokogiri' | ||
|
||
# VersionWarning adds a warning to the top of pre-release or outdated versioned | ||
# documentation pages. | ||
class VersionWarning < ::Nanoc::Filter | ||
identifier :version_warning | ||
|
||
def run(content, params = {}) | ||
case version_compare(params[:version], params[:latest]) | ||
when 1 | ||
type = 'a pre-release version' | ||
when 0 | ||
return content | ||
when -1 | ||
type = 'an old version' | ||
end | ||
|
||
href = File.join(params[:canonical_root], params[:entrypoint]) | ||
repo = File.basename(params[:repository_url], '.git').capitalize | ||
warning = %(<p>CAUTION: This page documents #{type} of #{repo}. | ||
Check out the <a href="#{href}">latest stable version</a>.</p>) | ||
|
||
prepend_warning(content, warning) | ||
end | ||
|
||
private | ||
|
||
def prepend_warning(content, warning) | ||
doc = Nokogiri::HTML(content) | ||
body = doc.css('body') | ||
if first = body.children.first | ||
first.add_previous_sibling(warning) | ||
else | ||
body << Nokogiri::HTML::DocumentFragment.parse(warning) | ||
end | ||
doc.to_s | ||
end | ||
|
||
def version_compare(a, b) | ||
a.split('.').map(&:to_i) <=> b.split('.').map(&:to_i) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.