Skip to content

Commit

Permalink
Add versions argument to deploydocs. The versions argument decide
Browse files Browse the repository at this point in the history
the content, and order, of the version selector. The argument is
a vector, defaulting to ["stable" => "v^", "v#.#", devurl => devurl].

Arguments to the selector can be:
 - "v^": expands to the latest release.
 - "v#": expands to latest doc per major release.
 - "v#.#": expands to latest doc per minor release.
 - "v#.#.#": expands to latest doc per patch release.
 - A pair p, which put p.first in the selector, and symlink it to p.second.
  • Loading branch information
fredrikekre committed Aug 23, 2018
1 parent 924d99b commit 9e6a85d
Show file tree
Hide file tree
Showing 3 changed files with 172 additions and 51 deletions.
62 changes: 41 additions & 21 deletions src/Documenter.jl
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,7 @@ hide(root::AbstractString, children) = (true, nothing, root, map(hide, children)
make = <Function>,
devbranch = "master",
devurl = "dev",
versions = ["stable" => "v^", "v#.#", devurl => devurl]
)
Converts markdown files generated by [`makedocs`](@ref) to HTML and pushes them to `repo`.
Expand Down Expand Up @@ -316,6 +317,20 @@ documentation. By default this value is set to `"master"`.
**`devurl`** the folder that in-development version of the docs will be deployed.
Defaults to `"dev"`.
**`versions`** determines content and order of the resulting version selector in
the generated html. The following entries are valied in the `versions` vector:
- `"v#"`: includes links to the latest documentation for each major release cycle
(i.e. `v2.0`, `v1.1`).
- `"v#.#"`: includes links to the latest documentation for each minor release cycle
(i.e. `v2.0`, `v1.1`, `v1.0`, `v0.1`).
- `"v#.#.#"`: includes links to all released versions.
- `"v^"`: includes a link to the docs for the maximum version
(i.e. a link `vX.Y` pointing to `vX.Y.Z` for highest `X`, `Y`, `Z`, respectively).
- A pair, e.g. `"first" => "second"`, which will put `"first"` in the selector,
and generate a url from which `"second"` can be accessed.
The second argument can be `"v^"`, to point to the maximum version docs
(as in e.g. `"stable" => "v^"`).
# See Also
The [Hosting Documentation](@ref) section of the manual provides a step-by-step guide to
Expand All @@ -339,6 +354,7 @@ function deploydocs(;

devbranch = "master",
devurl = "dev",
versions = ["stable" => "v^", "v#.#", devurl => devurl]
)
# deprecation of latest kwarg (renamed to devbranch)
if latest !== nothing
Expand Down Expand Up @@ -443,7 +459,7 @@ function deploydocs(;
root, temp, repo;
branch=branch, dirname=dirname, target=target,
tag=travis_tag, key=documenter_key, sha=sha,
devurl = devurl,
devurl = devurl, versions = versions,
)
end
end
Expand All @@ -462,19 +478,15 @@ end
Handles pushing changes to the remote documentation branch.
When `tag` is empty the docs are deployed to the `devurl` directory,
and when building docs for a tag they are deployed to a `vX.Y.Z` directory,
and also to the `stable` directory.
and when building docs for a tag they are deployed to a `vX.Y.Z` directory.
"""
function git_push(
root, temp, repo;
branch="gh-pages", dirname="", target="site", tag="", key="", sha="", devurl="dev"
branch="gh-pages", dirname="", target="site", tag="", key="", sha="", devurl="dev",
versions
)
dirname = isempty(dirname) ? temp : joinpath(temp, dirname)
isdir(dirname) || mkpath(dirname)
# Versioned docs directories.
devurl_dir = joinpath(dirname, devurl)
stable_dir = joinpath(dirname, "stable")
tagged_dir = joinpath(dirname, tag)

keyfile = abspath(joinpath(root, ".documenter"))
target_dir = abspath(target)
Expand Down Expand Up @@ -516,6 +528,7 @@ function git_push(

# Copy docs to `devurl`, or `stable`, `<release>`, and `<version>` directories.
if isempty(tag)
devurl_dir = joinpath(dirname, devurl)
gitrm_copy(target_dir, devurl_dir)
Writers.HTMLWriter.generate_siteinfo_file(devurl_dir, devurl)
# symlink "latest" to devurl to preserve links (remove in some future release)
Expand All @@ -524,25 +537,25 @@ function git_push(
@warn(string("creating symlink from `latest` to `$(devurl)` for backwards ",
"compatibility with old links. In future Documenter versions this symlink ",
"will not be created. Please update any links that point to `latest`."))
cd(dirname) do; symlink(devurl, "latest"); end
cd(dirname) do; rm_and_add_symlink(devurl, "latest"); end
end
else
@assert occursin(Base.VERSION_REGEX, tag) # checked in deploydocs
version = VersionNumber(tag)
# only push to stable if this is the latest stable release
versions = filter!(x -> occursin(Base.VERSION_REGEX, x), readdir(dirname))
maxver = mapreduce(x -> VersionNumber(x), max, versions; init=v"0.0.0")
if version >= maxver && version.prerelease == () # don't deploy to stable for prereleases
gitrm_copy(target_dir, stable_dir)
Writers.HTMLWriter.generate_siteinfo_file(stable_dir, "stable")
end
tagged_dir = joinpath(dirname, tag)
gitrm_copy(target_dir, tagged_dir)
Writers.HTMLWriter.generate_siteinfo_file(tagged_dir, tag)
end

# Create the versions.js file containing a list of all docs
# versions. This must always happen after the folder copying.
Writers.HTMLWriter.generate_version_file(dirname)
# Expand the users `versions` vector
entries, symlinks = Writers.HTMLWriter.expand_versions(dirname, versions)

# Create the versions.js file containing a list of `entries`.
# This must always happen after the folder copying.
Writers.HTMLWriter.generate_version_file(joinpath(dir, "versions.js"), entries)

# generate the symlinks
cd(dirname) do
foreach(kv -> rm_and_add_symlink(kv.second, kv.first), symlinks)
end

# Add, commit, and push the docs to the remote.
run(`git add -A .`)
Expand All @@ -560,6 +573,13 @@ function git_push(
end
end

function rm_and_add_symlink(target, link)
# If `link` is an old symlink, remove it,
# if `link` is something else we let symlink throw.
islink(link) && rm(link)
symlink(target, link)
end

"""
gitrm_copy(src, dst)
Expand Down
90 changes: 77 additions & 13 deletions src/Writers/HTMLWriter.jl
Original file line number Diff line number Diff line change
Expand Up @@ -477,23 +477,87 @@ function render_topbar(ctx, navnode)
return div["#topbar"](span(page_title), a[".fa .fa-bars", :href => "#"])
end

function generate_version_file(dir::AbstractString)
all_folders = readdir(dir)
folders = []

for each in all_folders
occursin(Base.VERSION_REGEX, each) && push!(folders, each)
# expand the versions argument from the user
# and return entries and needed symlinks
function expand_versions(dir, versions)
# output: entries and symlinks
entries = String[]
symlinks = Pair{String,String}[]

# read folders and filter out symlinks
available_folders = readdir(dir)
cd(() -> filter!(!islink, available_folders), dir)

# filter and sort release folders
vnum(x) = VersionNumber(x)
version_folders = [x for x in available_folders if occursin(Base.VERSION_REGEX, x)]
sort!(version_folders, lt = (x, y) -> vnum(x) < vnum(y), rev = true)
release_folders = filter(x -> (v = vnum(x); v.prerelease == () && v.build == ()), version_folders)
# pre_release_folders = filter(x -> (v = vnum(x); v.prerelease != () || v.build != ()), version_folders)
major_folders = filter!(x -> (v = vnum(x); v.major != 0),
unique(x -> (v = vnum(x); v.major), release_folders))
minor_folders = filter!(x -> (v = vnum(x); !(v.major == 0 && v.minor == 0)),
unique(x -> (v = vnum(x); (v.major, v.minor)), release_folders))
patch_folders = unique(x -> (v = vnum(x); (v.major, v.minor, v.patch)), release_folders)

filter!(x -> vnum(x) !== 0, major_folders)

# populate output
for entry in versions
if entry == "v#" # one doc per major release
for x in major_folders
vstr = "v$(vnum(x).major).$(vnum(x).minor)"
push!(entries, vstr)
push!(symlinks, vstr => x)
end
elseif entry == "v#.#" # one doc per minor release
for x in minor_folders
vstr = "v$(vnum(x).major).$(vnum(x).minor)"
push!(entries, vstr)
push!(symlinks, vstr => x)
end
elseif entry == "v#.#.#" # one doc per patch release
for x in patch_folders
vstr = "v$(vnum(x).major).$(vnum(x).minor).$(vnum(x).patch)"
push!(entries, vstr)
push!(symlinks, vstr => x)
end
elseif entry == "v^" || (entry isa Pair && entry.second == "v^")
if !isempty(release_folders)
x = first(release_folders)
vstr = isa(entry, Pair) ? entry.first : "v$(vnum(x).major).$(vnum(x).minor)"
push!(entries, vstr)
push!(symlinks, vstr => x)
end
elseif entry isa Pair
k, v = entry
i = findfirst(==(v), available_folders)
if i === nothing
@info("no match for `versions` entry `$(repr(entry))`")
else
push!(entries, k)
push!(symlinks, k => v)
end
else
@info("no match for `versions` entry `$(repr(entry))`")
end
end
# sort tags by version number
sort!(folders, lt = (x, y) -> VersionNumber(x) < VersionNumber(y), rev = true)
unique!(entries) # remove any duplicates

# include stable first, then dev
"dev" in all_folders && pushfirst!(folders, "dev")
"stable" in all_folders && pushfirst!(folders, "stable")
# generate remaining symlinks
foreach(x -> push!(symlinks, "v$(vnum(x).major)" => x), major_folders)
foreach(x -> push!(symlinks, "v$(vnum(x).major).$(vnum(x).minor)" => x), minor_folders)
foreach(x -> push!(symlinks, "v$(vnum(x).major).$(vnum(x).minor).$(vnum(x).patch)" => x), patch_folders)
filter!(x -> x.first != x.second, unique!(symlinks))

return entries, symlinks
end

open(joinpath(dir, "versions.js"), "w") do buf
# write version file
function generate_version_file(versionfile::AbstractString, entries)
open(versionfile, "w") do buf
println(buf, "var DOC_VERSIONS = [")
for folder in folders
for folder in entries
println(buf, " \"", folder, "\",")
end
println(buf, "];")
Expand Down
71 changes: 54 additions & 17 deletions test/htmlwriter.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,18 @@ module HTMLWriterTests

using Test

import Documenter.Writers.HTMLWriter: jsescape, generate_version_file
import Documenter.Writers.HTMLWriter: jsescape, generate_version_file, expand_versions

function verify_version_file(versionfile, entries)
@test isfile(versionfile)
content = read(versionfile, String)
idx = 1
for entry in entries
i = findnext(entry, content, idx)
@test i !== nothing
idx = last(i)
end
end

@testset "HTMLWriter" begin
@test jsescape("abc123") == "abc123"
Expand All @@ -21,28 +32,54 @@ import Documenter.Writers.HTMLWriter: jsescape, generate_version_file
@test jsescape("policy to
 delete.") == "policy to\\u2028 delete."

mktempdir() do tmpdir
versions = ["stable", "dev", "v0.2.6", "v0.1.1", "v0.1.0"]
versionfile = joinpath(tmpdir, "versions.js")
versions = ["stable", "dev",
"2.1.1", "v2.1.0", "v2.0.1", "v2.0.0",
"1.1.1", "v1.1.0", "v1.0.1", "v1.0.0",
"0.1.1", "v0.1.0"] # note no `v` on first ones
cd(tmpdir) do
mkdir("foobar")
for version in versions
mkdir(version)
end
end

generate_version_file(tmpdir)

versions_file = joinpath(tmpdir, "versions.js")
@test isfile(versions_file)
contents = String(read(versions_file))
@test !occursin("foobar", contents) # only specific directories end up in the versions file
# let's make sure they're in the right order -- they should be sorted in the output file
last = 0:0
for version in versions
this = findfirst(version, contents)
@test this !== nothing
@test first(last) < first(this)
last = this
end
# expanding versions
versions = ["stable" => "v^", "v#.#", "dev" => "dev"] # default to makedocs
entries, symlinks = expand_versions(tmpdir, versions)
@test entries == ["stable", "v2.1", "v2.0", "v1.1", "v1.0", "v0.1", "dev"]
@test symlinks == ["stable"=>"2.1.1", "v2.1"=>"2.1.1", "v2.0"=>"v2.0.1",
"v1.1"=>"1.1.1", "v1.0"=>"v1.0.1", "v0.1"=>"0.1.1",
"v2"=>"2.1.1", "v1"=>"1.1.1", "v2.1.1"=>"2.1.1",
"v1.1.1"=>"1.1.1", "v0.1.1"=>"0.1.1"]
generate_version_file(versionfile, entries)
verify_version_file(versionfile, entries)

versions = ["v#"]
entries, symlinks = expand_versions(tmpdir, versions)
@test entries == ["v2.1", "v1.1"]
@test symlinks == ["v2.1"=>"2.1.1", "v1.1"=>"1.1.1", "v2"=>"2.1.1", "v1"=>"1.1.1",
"v2.0"=>"v2.0.1", "v1.0"=>"v1.0.1", "v0.1"=>"0.1.1",
"v2.1.1"=>"2.1.1", "v1.1.1"=>"1.1.1", "v0.1.1"=>"0.1.1"]
generate_version_file(versionfile, entries)
verify_version_file(versionfile, entries)

versions = ["v#.#.#"]
entries, symlinks = expand_versions(tmpdir, versions)
@test entries == ["v2.1.1", "v2.1.0", "v2.0.1", "v2.0.0", "v1.1.1", "v1.1.0",
"v1.0.1", "v1.0.0", "v0.1.1", "v0.1.0"]
@test symlinks == ["v2.1.1"=>"2.1.1", "v1.1.1"=>"1.1.1", "v0.1.1"=>"0.1.1",
"v2"=>"2.1.1", "v1"=>"1.1.1", "v2.1"=>"2.1.1",
"v2.0"=>"v2.0.1", "v1.1"=>"1.1.1", "v1.0"=>"v1.0.1", "v0.1"=>"0.1.1"]
generate_version_file(versionfile, entries)
verify_version_file(versionfile, entries)

versions = ["v^", "devel" => "dev", "foobar", "foo" => "bar"]
entries, symlinks = expand_versions(tmpdir, versions)
@test entries == ["v2.1", "devel"]
@test ("v2.1" => "2.1.1") in symlinks
@test ("devel" => "dev") in symlinks
generate_version_file(versionfile, entries)
verify_version_file(versionfile, entries)
end
end

Expand Down

0 comments on commit 9e6a85d

Please sign in to comment.